Skip to main content

oino_ui/extras/
tree.rs

1//! `Tree` node (§4.5.2).
2
3use serde::{Deserialize, Serialize};
4
5use crate::extras::icon::Icon;
6use crate::node::{ActionId, Common, IntoNode, Key, Node};
7use crate::text::Line;
8
9/// One tree row.
10#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct TreeNode {
13    /// Node identity.
14    pub key: Key,
15    /// Row label.
16    pub label: Line,
17    /// Leading icon.
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub icon: Option<Icon>,
20    /// Right-aligned badge.
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub badge: Option<Line>,
23    /// Child rows.
24    #[serde(default, skip_serializing_if = "Vec::is_empty")]
25    pub children: Vec<TreeNode>,
26    /// Start expanded.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub expanded: Option<bool>,
29    /// GUI extra: children load on first expand (extension replies with a patch).
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub lazy: Option<bool>,
32}
33
34impl TreeNode {
35    /// A leaf row.
36    #[must_use]
37    pub fn new(key: impl Into<Key>, label: impl Into<Line>) -> Self {
38        Self {
39            key: key.into(),
40            label: label.into(),
41            icon: None,
42            badge: None,
43            children: Vec::new(),
44            expanded: None,
45            lazy: None,
46        }
47    }
48
49    /// Sets the leading icon.
50    #[must_use]
51    pub fn icon(mut self, icon: Icon) -> Self {
52        self.icon = Some(icon);
53        self
54    }
55
56    /// Sets the right-aligned badge.
57    #[must_use]
58    pub fn badge(mut self, badge: impl Into<Line>) -> Self {
59        self.badge = Some(badge.into());
60        self
61    }
62
63    /// Attaches child rows.
64    #[must_use]
65    pub fn children(mut self, children: impl IntoIterator<Item = TreeNode>) -> Self {
66        self.children = children.into_iter().collect();
67        self
68    }
69
70    /// Starts expanded.
71    #[must_use]
72    pub fn expanded(mut self, expanded: bool) -> Self {
73        self.expanded = Some(expanded);
74        self
75    }
76
77    /// Loads children lazily on first expand.
78    #[must_use]
79    pub fn lazy(mut self, lazy: bool) -> Self {
80        self.lazy = Some(lazy);
81        self
82    }
83}
84
85/// Tree selection state.
86#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88pub struct TreeState {
89    /// Selected node key.
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub selected: Option<Key>,
92}
93
94impl TreeState {
95    /// State with one selected key.
96    #[must_use]
97    pub fn with_selected(key: impl Into<Key>) -> Self {
98        Self {
99            selected: Some(key.into()),
100        }
101    }
102}
103
104/// A tree with expand / collapse and optional lazy children (§4.5.2).
105#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct Tree {
108    /// Common node props (`key`, `style`, `block`, `fallback`).
109    #[serde(flatten)]
110    pub common: Common,
111    /// Root rows (≤ 5 000 counting descendants, §4.8).
112    pub nodes: Vec<TreeNode>,
113    /// Selection state (wins when it changes, §4.6).
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub state: Option<TreeState>,
116    /// Action on selection change (node key).
117    #[serde(default, skip_serializing_if = "Option::is_none")]
118    pub on_select: Option<ActionId>,
119    /// Action on Enter (node key).
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub on_activate: Option<ActionId>,
122    /// Action on expand / collapse (`{key, expanded}`).
123    #[serde(default, skip_serializing_if = "Option::is_none")]
124    pub on_toggle: Option<ActionId>,
125}
126
127impl Tree {
128    /// A tree from root rows.
129    #[must_use]
130    pub fn new(nodes: impl IntoIterator<Item = TreeNode>) -> Self {
131        Self {
132            nodes: nodes.into_iter().collect(),
133            ..Self::default()
134        }
135    }
136
137    /// Sets the selection state.
138    #[must_use]
139    pub fn state(mut self, state: TreeState) -> Self {
140        self.state = Some(state);
141        self
142    }
143
144    /// Sets the selection action.
145    #[must_use]
146    pub fn on_select(mut self, action: impl Into<ActionId>) -> Self {
147        self.on_select = Some(action.into());
148        self
149    }
150
151    /// Sets the activate action.
152    #[must_use]
153    pub fn on_activate(mut self, action: impl Into<ActionId>) -> Self {
154        self.on_activate = Some(action.into());
155        self
156    }
157
158    /// Sets the toggle action.
159    #[must_use]
160    pub fn on_toggle(mut self, action: impl Into<ActionId>) -> Self {
161        self.on_toggle = Some(action.into());
162        self
163    }
164}
165
166impl IntoNode for Tree {}
167impl From<Tree> for Node {
168    fn from(t: Tree) -> Self {
169        Node::Tree(t)
170    }
171}