Skip to main content

tui_lipan/widgets/tree/
mod.rs

1//! Tree widget.
2
3mod component;
4mod types;
5
6pub(crate) use component::*;
7pub use types::*;
8
9use crate::callback::Callback;
10use crate::core::element::Element;
11use crate::style::{Length, ScrollbarConfig, Style, StyleSlot};
12use crate::utils::gradient::ColorGradient;
13use crate::widgets::FocusAccordion;
14use crate::widgets::ScrollKeymap;
15use std::sync::Arc;
16
17/// A hierarchical tree view.
18#[derive(Clone)]
19pub struct Tree {
20    props: TreeProps,
21}
22
23impl Tree {
24    /// Create a new tree with the given root node.
25    pub fn new(root: TreeNode) -> Self {
26        Self {
27            props: TreeProps {
28                root,
29                selected: None,
30                clear_selection: false,
31                force_scroll_to_selected: false,
32                gap: 0,
33                icon_gap: 1,
34                show_icons: true,
35                expanded_icon: "▼".into(),
36                collapsed_icon: "▶".into(),
37                leaf_icon: None,
38                icon_style: Style::default(),
39                width: Length::Flex(1),
40                height: Length::Flex(1),
41                style: Style::default(),
42                hover_style: StyleSlot::Inherit,
43                item_hover_style: StyleSlot::Inherit,
44                selection_style: StyleSlot::Inherit,
45                unfocused_selection_style: StyleSlot::Inherit,
46                selection_symbol: None,
47                selection_symbol_style: None,
48                unfocused_selection_symbol_style: None,
49                scrollbar: false,
50                scrollbar_config: ScrollbarConfig::default(),
51                scroll_keys: ScrollKeymap::default(),
52                scroll_wheel: true,
53                show_scroll_indicators: false,
54                scroll_indicator_style: Style::default(),
55                empty_text: None,
56                empty_text_style: Style::default(),
57                activate_on_click: true,
58                focusable: true,
59                tab_stop: true,
60                on_focus: None,
61                on_blur: None,
62                on_select: None,
63                on_activate: None,
64                on_toggle: None,
65                keymap: TreeKeymap::default(),
66                focus_policy: None,
67                focus_key: None,
68                indent_style: IndentStyle::None,
69                indent_guide_style: Style::default(),
70                indent_gradient: None,
71                solid_indent_connector_gap: false,
72                selection_full_width: false,
73                unselected_symbol: None,
74                key_interceptor: None,
75                indent_guide_start_depth: 1,
76            },
77        }
78    }
79
80    /// Set style of indentation guides.
81    pub fn indent_style(mut self, style: IndentStyle) -> Self {
82        self.props.indent_style = style;
83        self
84    }
85
86    /// Set style for indent guides.
87    pub fn indent_guide_style(mut self, style: Style) -> Self {
88        self.props.indent_guide_style = style;
89        self
90    }
91
92    /// Set the first non-root depth that renders indentation guides (default 1).
93    pub fn indent_guide_start_depth(mut self, depth: usize) -> Self {
94        self.props.indent_guide_start_depth = depth.max(1);
95        self
96    }
97
98    /// Set a depth-based gradient for indentation guides.
99    pub fn indent_gradient(mut self, gradient: ColorGradient) -> Self {
100        self.props.indent_gradient = Some(gradient);
101        self
102    }
103
104    pub(crate) fn solid_indent_connector_gap(mut self, solid: bool) -> Self {
105        self.props.solid_indent_connector_gap = solid;
106        self
107    }
108
109    /// Set whether the highlight should span the full width of the tree.
110    pub fn selection_full_width(mut self, full_width: bool) -> Self {
111        self.props.selection_full_width = full_width;
112        self
113    }
114
115    /// Set symbol for unselected items.
116    pub fn unselected_symbol(mut self, symbol: Option<impl Into<Arc<str>>>) -> Self {
117        self.props.unselected_symbol = symbol.map(Into::into);
118        self
119    }
120
121    /// Set vertical gap between rows.
122    pub fn gap(mut self, gap: u16) -> Self {
123        self.props.gap = gap;
124        self
125    }
126
127    /// Set the selected visible row index.
128    pub fn selected(mut self, selected: usize) -> Self {
129        self.props.selected = Some(selected);
130        self
131    }
132
133    /// Clear the selection highlight (no current row).
134    ///
135    /// When `true`, this is authoritative over both the controlled `selected`
136    /// prop and internal selection state: nothing is highlighted. Keyboard
137    /// navigation on the inner list may still emit `on_select` (adopting a
138    /// row), and internal state may record it, but `clear_selection` keeps
139    /// the highlight suppressed until cleared.
140    pub fn clear_selection(mut self, clear: bool) -> Self {
141        self.props.clear_selection = clear;
142        self
143    }
144
145    /// Force scroll to make the selected item visible on next render.
146    pub fn force_scroll_to_selected(mut self, force: bool) -> Self {
147        self.props.force_scroll_to_selected = force;
148        self
149    }
150
151    /// Set horizontal gap between icon and content.
152    pub fn icon_gap(mut self, gap: u16) -> Self {
153        self.props.icon_gap = gap;
154        self
155    }
156
157    /// Toggle showing expand/collapse icons.
158    pub fn show_icons(mut self, show: bool) -> Self {
159        self.props.show_icons = show;
160        self
161    }
162
163    /// Set expanded icon.
164    pub fn expanded_icon(mut self, icon: impl Into<Arc<str>>) -> Self {
165        self.props.expanded_icon = icon.into();
166        self
167    }
168
169    /// Set collapsed icon.
170    pub fn collapsed_icon(mut self, icon: impl Into<Arc<str>>) -> Self {
171        self.props.collapsed_icon = icon.into();
172        self
173    }
174
175    /// Set leaf icon.
176    pub fn leaf_icon(mut self, icon: Option<impl Into<Arc<str>>>) -> Self {
177        self.props.leaf_icon = icon.map(Into::into);
178        self
179    }
180
181    /// Set icon style.
182    pub fn icon_style(mut self, style: Style) -> Self {
183        self.props.icon_style = style;
184        self
185    }
186
187    /// Set base style for the list.
188    pub fn style(mut self, style: Style) -> Self {
189        self.props.style = style;
190        self
191    }
192
193    /// Set hover style for the list.
194    pub fn hover_style(mut self, style: Style) -> Self {
195        self.props.hover_style = StyleSlot::Replace(style);
196        self
197    }
198
199    /// Extend the themed hover style for the list.
200    pub fn extend_hover_style(mut self, style: Style) -> Self {
201        self.props.hover_style = StyleSlot::Extend(style);
202        self
203    }
204
205    /// Inherit the themed hover style for the list.
206    pub fn inherit_hover_style(mut self) -> Self {
207        self.props.hover_style = StyleSlot::Inherit;
208        self
209    }
210
211    /// Set hover style slot directly for composite forwarding.
212    pub fn hover_style_slot(mut self, slot: StyleSlot) -> Self {
213        self.props.hover_style = slot;
214        self
215    }
216
217    /// Set item hover style.
218    pub fn item_hover_style(mut self, style: Style) -> Self {
219        self.props.item_hover_style = StyleSlot::Replace(style);
220        self
221    }
222
223    /// Extend the themed item-hover style.
224    pub fn extend_item_hover_style(mut self, style: Style) -> Self {
225        self.props.item_hover_style = StyleSlot::Extend(style);
226        self
227    }
228
229    /// Inherit the themed item-hover style.
230    pub fn inherit_item_hover_style(mut self) -> Self {
231        self.props.item_hover_style = StyleSlot::Inherit;
232        self
233    }
234
235    /// Set item-hover style slot directly for composite forwarding.
236    pub fn item_hover_style_slot(mut self, slot: StyleSlot) -> Self {
237        self.props.item_hover_style = slot;
238        self
239    }
240
241    /// Set highlight style for the selected row.
242    pub fn selection_style(mut self, style: Style) -> Self {
243        self.props.selection_style = StyleSlot::Replace(style);
244        self
245    }
246
247    /// Extend the themed highlight style for the selected row.
248    pub fn extend_selection_style(mut self, style: Style) -> Self {
249        self.props.selection_style = StyleSlot::Extend(style);
250        self
251    }
252
253    /// Inherit the themed highlight style for the selected row.
254    pub fn inherit_selection_style(mut self) -> Self {
255        self.props.selection_style = StyleSlot::Inherit;
256        self
257    }
258
259    /// Set selection style slot directly for composite forwarding.
260    pub fn selection_style_slot(mut self, slot: StyleSlot) -> Self {
261        self.props.selection_style = slot;
262        self
263    }
264
265    /// Set highlight style while the tree is not focused.
266    pub fn unfocused_selection_style(mut self, style: Style) -> Self {
267        self.props.unfocused_selection_style = StyleSlot::Replace(style);
268        self
269    }
270
271    /// Extend the themed highlight style while the tree is not focused.
272    pub fn extend_unfocused_selection_style(mut self, style: Style) -> Self {
273        self.props.unfocused_selection_style = StyleSlot::Extend(style);
274        self
275    }
276
277    /// Inherit the themed highlight style while the tree is not focused.
278    pub fn inherit_unfocused_selection_style(mut self) -> Self {
279        self.props.unfocused_selection_style = StyleSlot::Inherit;
280        self
281    }
282
283    /// Set unfocused-selection style slot directly for composite forwarding.
284    pub fn unfocused_selection_style_slot(mut self, slot: StyleSlot) -> Self {
285        self.props.unfocused_selection_style = slot;
286        self
287    }
288
289    /// Set highlight symbol.
290    pub fn selection_symbol(mut self, symbol: Option<impl Into<Arc<str>>>) -> Self {
291        self.props.selection_symbol = symbol.map(Into::into);
292        self
293    }
294
295    /// Set highlight symbol style.
296    pub fn selection_symbol_style(mut self, style: Option<Style>) -> Self {
297        self.props.selection_symbol_style = style;
298        self
299    }
300
301    /// Set highlight symbol style while the tree is not focused.
302    pub fn unfocused_selection_symbol_style(mut self, style: Option<Style>) -> Self {
303        self.props.unfocused_selection_symbol_style = style;
304        self
305    }
306
307    /// Set requested width.
308    pub fn width(mut self, width: Length) -> Self {
309        self.props.width = width;
310        self
311    }
312
313    /// Set requested height.
314    pub fn height(mut self, height: Length) -> Self {
315        self.props.height = height;
316        self
317    }
318
319    /// Draw a scrollbar.
320    pub fn scrollbar(mut self, scrollbar: bool) -> Self {
321        self.props.scrollbar = scrollbar;
322        self
323    }
324
325    /// Set scrollbar configuration.
326    pub fn scrollbar_config(mut self, config: ScrollbarConfig) -> Self {
327        self.props.scrollbar_config = config;
328        self
329    }
330
331    /// Configure which keys move selection.
332    pub fn scroll_keys(mut self, keys: ScrollKeymap) -> Self {
333        self.props.scroll_keys = keys;
334        self
335    }
336
337    /// Enable mouse wheel scrolling.
338    pub fn scroll_wheel(mut self, enabled: bool) -> Self {
339        self.props.scroll_wheel = enabled;
340        self
341    }
342
343    /// Enable "N more" scroll indicators when items are hidden.
344    pub fn show_scroll_indicators(mut self, show: bool) -> Self {
345        self.props.show_scroll_indicators = show;
346        self
347    }
348
349    /// Set style for scroll indicators.
350    pub fn scroll_indicator_style(mut self, style: Style) -> Self {
351        self.props.scroll_indicator_style = style;
352        self
353    }
354
355    /// Set text to display when the tree is empty.
356    pub fn empty_text(mut self, text: impl Into<Arc<str>>) -> Self {
357        self.props.empty_text = Some(text.into());
358        self
359    }
360
361    /// Set style for empty text.
362    pub fn empty_text_style(mut self, style: Style) -> Self {
363        self.props.empty_text_style = style;
364        self
365    }
366
367    /// Control activation on mouse click.
368    pub fn activate_on_click(mut self, activate: bool) -> Self {
369        self.props.activate_on_click = activate;
370        self
371    }
372
373    /// Control whether the tree is focusable.
374    pub fn focusable(mut self, focusable: bool) -> Self {
375        self.props.focusable = focusable;
376        self
377    }
378
379    /// Control whether the tree participates in tab focus traversal.
380    pub fn tab_stop(mut self, tab_stop: bool) -> Self {
381        self.props.tab_stop = tab_stop;
382        self
383    }
384
385    /// Set the callback fired when the tree gains focus.
386    pub fn on_focus(mut self, cb: Callback<()>) -> Self {
387        self.props.on_focus = Some(cb);
388        self
389    }
390
391    /// Set the callback fired when the tree loses focus.
392    pub fn on_blur(mut self, cb: Callback<()>) -> Self {
393        self.props.on_blur = Some(cb);
394        self
395    }
396
397    /// Set selection callback.
398    pub fn on_select(mut self, cb: Callback<TreeEvent>) -> Self {
399        self.props.on_select = Some(cb);
400        self
401    }
402
403    /// Set activation callback (Enter or click when `activate_on_click` is true).
404    pub fn on_activate(mut self, cb: Callback<TreeEvent>) -> Self {
405        self.props.on_activate = Some(cb);
406        self
407    }
408
409    /// Set toggle callback.
410    pub fn on_toggle(mut self, cb: Callback<TreeToggleEvent>) -> Self {
411        self.props.on_toggle = Some(cb);
412        self
413    }
414
415    /// Configure keymap for expand/collapse.
416    pub fn keymap(mut self, keymap: TreeKeymap) -> Self {
417        self.props.keymap = keymap;
418        self
419    }
420
421    /// Configure focus-aware collapsing for large trees.
422    pub fn focus_policy(mut self, policy: FocusAccordion) -> Self {
423        self.props.focus_policy = Some(policy);
424        self
425    }
426
427    /// Assign a stable key to the tree's focusable list node.
428    pub fn focus_key(mut self, key: impl Into<Arc<str>>) -> Self {
429        self.props.focus_key = Some(key.into());
430        self
431    }
432
433    /// Set a key interceptor that runs before internal tree key handling.
434    ///
435    /// If the interceptor returns `true`, the key is consumed and the tree's
436    /// built-in expand/collapse handling is skipped.
437    pub fn key_interceptor(mut self, handler: crate::callback::KeyHandler) -> Self {
438        self.props.key_interceptor = Some(handler);
439        self
440    }
441}
442
443impl From<Tree> for Element {
444    fn from(tree: Tree) -> Self {
445        crate::child(TreeComponent::new, tree.props)
446    }
447}