Skip to main content

promkit_widgets/
tree.rs

1use promkit_core::{Pane, PaneFactory, grapheme::StyledGraphemes};
2
3pub mod node;
4use node::Kind;
5#[path = "tree/tree.rs"]
6mod inner;
7pub use inner::Tree;
8pub mod config;
9pub use config::Config;
10
11/// Represents the state of a tree structure within the application.
12///
13/// This state includes not only the tree itself but also various properties
14/// that affect how the tree is displayed and interacted with. These properties
15/// include symbols for folded and unfolded items, styles for active and inactive
16/// items, the number of lines available for rendering, and the indentation level
17/// for child items in the tree.
18#[derive(Clone)]
19pub struct State {
20    pub tree: Tree,
21    /// Configuration for rendering and behavior.
22    pub config: Config,
23}
24
25impl PaneFactory for State {
26    fn create_pane(&self, width: u16, height: u16) -> Pane {
27        let symbol = |kind: &Kind| -> &str {
28            match kind {
29                Kind::Folded { .. } => &self.config.folded_symbol,
30                Kind::Unfolded { .. } => &self.config.unfolded_symbol,
31            }
32        };
33
34        let indent = |kind: &Kind| -> usize {
35            match kind {
36                Kind::Folded { path, .. } | Kind::Unfolded { path, .. } => {
37                    path.len() * self.config.indent
38                }
39            }
40        };
41
42        let id = |kind: &Kind| -> String {
43            match kind {
44                Kind::Folded { id, .. } | Kind::Unfolded { id, .. } => id.clone(),
45            }
46        };
47
48        let height = match self.config.lines {
49            Some(lines) => lines.min(height as usize),
50            None => height as usize,
51        };
52
53        let matrix = self
54            .tree
55            .kinds()
56            .iter()
57            .enumerate()
58            .filter(|(i, _)| *i >= self.tree.position() && *i < self.tree.position() + height)
59            .map(|(i, kind)| {
60                if i == self.tree.position() {
61                    StyledGraphemes::from_str(
62                        format!("{}{}{}", symbol(kind), " ".repeat(indent(kind)), id(kind),),
63                        self.config.active_item_style,
64                    )
65                } else {
66                    StyledGraphemes::from_str(
67                        format!(
68                            "{}{}{}",
69                            " ".repeat(StyledGraphemes::from(symbol(kind)).widths()),
70                            " ".repeat(indent(kind)),
71                            id(kind),
72                        ),
73                        self.config.inactive_item_style,
74                    )
75                }
76            })
77            .fold((vec![], 0), |(mut acc, pos), item| {
78                let rows = item.matrixify(width as usize, height, 0).0;
79                if pos < self.tree.position() + height {
80                    acc.extend(rows);
81                }
82                (acc, pos + 1)
83            });
84
85        Pane::new(matrix.0, 0)
86    }
87}