Skip to main content

fret_ui_kit/
tree.rs

1use std::collections::HashSet;
2use std::sync::Arc;
3
4use fret_ui::element::Elements;
5use fret_ui::{ElementContext, UiHost};
6
7pub type TreeItemId = u64;
8
9#[derive(Debug, Clone, Copy)]
10pub struct TreeRowState {
11    pub selected: bool,
12    pub expanded: bool,
13    pub disabled: bool,
14    pub depth: usize,
15    pub has_children: bool,
16}
17
18pub trait TreeRowRenderer<H: UiHost> {
19    fn render_row(
20        &mut self,
21        cx: &mut ElementContext<'_, H>,
22        entry: &TreeEntry,
23        state: TreeRowState,
24    ) -> Elements;
25
26    fn render_trailing(
27        &mut self,
28        _cx: &mut ElementContext<'_, H>,
29        _entry: &TreeEntry,
30        _state: TreeRowState,
31    ) -> Elements {
32        Elements::default()
33    }
34}
35
36impl<H: UiHost, F, R> TreeRowRenderer<H> for F
37where
38    F: FnMut(&mut ElementContext<'_, H>, &TreeEntry, TreeRowState) -> R,
39    R: Into<Elements>,
40{
41    fn render_row(
42        &mut self,
43        cx: &mut ElementContext<'_, H>,
44        entry: &TreeEntry,
45        state: TreeRowState,
46    ) -> Elements {
47        (self)(cx, entry, state).into()
48    }
49}
50
51#[derive(Debug, Clone)]
52pub struct TreeItem {
53    pub id: TreeItemId,
54    pub label: Arc<str>,
55    pub children: Vec<TreeItem>,
56    pub disabled: bool,
57}
58
59impl TreeItem {
60    pub fn new(id: TreeItemId, label: impl Into<Arc<str>>) -> Self {
61        Self {
62            id,
63            label: label.into(),
64            children: Vec::new(),
65            disabled: false,
66        }
67    }
68
69    pub fn disabled(mut self, disabled: bool) -> Self {
70        self.disabled = disabled;
71        self
72    }
73
74    pub fn child(mut self, child: TreeItem) -> Self {
75        self.children.push(child);
76        self
77    }
78
79    pub fn children(mut self, children: impl IntoIterator<Item = TreeItem>) -> Self {
80        self.children = children.into_iter().collect();
81        self
82    }
83
84    pub fn is_folder(&self) -> bool {
85        !self.children.is_empty()
86    }
87}
88
89#[derive(Debug, Default, Clone)]
90pub struct TreeState {
91    pub selected: Option<TreeItemId>,
92    pub expanded: HashSet<TreeItemId>,
93}
94
95#[derive(Debug, Clone)]
96pub struct TreeEntry {
97    pub id: TreeItemId,
98    pub label: Arc<str>,
99    pub depth: usize,
100    pub parent: Option<TreeItemId>,
101    pub has_children: bool,
102    pub disabled: bool,
103}
104
105pub fn flatten_tree(items: &[TreeItem], expanded: &HashSet<TreeItemId>) -> Vec<TreeEntry> {
106    fn walk(
107        out: &mut Vec<TreeEntry>,
108        items: &[TreeItem],
109        expanded: &HashSet<TreeItemId>,
110        depth: usize,
111        parent: Option<TreeItemId>,
112    ) {
113        for item in items {
114            let has_children = item.is_folder();
115            out.push(TreeEntry {
116                id: item.id,
117                label: Arc::clone(&item.label),
118                depth,
119                parent,
120                has_children,
121                disabled: item.disabled,
122            });
123
124            if has_children && expanded.contains(&item.id) {
125                walk(out, &item.children, expanded, depth + 1, Some(item.id));
126            }
127        }
128    }
129
130    let mut out = Vec::new();
131    walk(&mut out, items, expanded, 0, None);
132    out
133}