Skip to content

Commit f3fb7ac

Browse files
committed
chore: Row based implementations for tree-widgets
1 parent 5b64cad commit f3fb7ac

8 files changed

Lines changed: 492 additions & 502 deletions

File tree

examples/tree/src/tree.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::path::Path;
22

3-
use promkit::{preset::tree::Tree, widgets::tree::node::Node, Prompt};
3+
use promkit::{preset::tree::Tree, widgets::tree::Document, Prompt};
44

55
#[tokio::main]
66
async fn main() -> anyhow::Result<()> {
77
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../promkit/src");
8-
let ret = Tree::new(Node::try_from(&root)?)
8+
let document = Document::from_path(&root)?;
9+
let ret = Tree::new(document)
910
.title("Select a directory or file")
1011
.tree_lines(10)
1112
.run()

promkit-widgets/src/tree.rs

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use promkit_core::{Widget, grapheme::StyledGraphemes};
22

3-
pub mod node;
4-
use node::Kind;
5-
#[path = "tree/tree.rs"]
6-
mod inner;
7-
pub use inner::Tree;
3+
mod document;
4+
pub use document::Document;
85
pub mod config;
96
pub use config::Config;
7+
pub mod treez;
8+
pub use treez::Row;
109

1110
/// Represents the state of a tree structure within the application.
1211
///
@@ -17,31 +16,18 @@ pub use config::Config;
1716
/// for child items in the tree.
1817
#[derive(Clone)]
1918
pub struct State {
20-
pub tree: Tree,
19+
pub document: Document,
2120
/// Configuration for rendering and behavior.
2221
pub config: Config,
2322
}
2423

2524
impl Widget for State {
2625
fn create_graphemes(&self, _width: u16, height: u16) -> StyledGraphemes {
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(),
26+
let symbol = |row: &Row| -> &str {
27+
if row.has_children && !row.collapsed {
28+
&self.config.unfolded_symbol
29+
} else {
30+
&self.config.folded_symbol
4531
}
4632
};
4733

@@ -50,29 +36,30 @@ impl Widget for State {
5036
None => height as usize,
5137
};
5238

53-
let kinds = self.tree.kinds();
54-
let lines = kinds
55-
.iter()
56-
.enumerate()
57-
.filter(|(i, _)| *i >= self.tree.position() && *i < self.tree.position() + height)
58-
.map(|(i, kind)| {
59-
if i == self.tree.position() {
60-
StyledGraphemes::from_str(
61-
format!("{}{}{}", symbol(kind), " ".repeat(indent(kind)), id(kind),),
62-
self.config.active_item_style,
63-
)
64-
} else {
65-
StyledGraphemes::from_str(
66-
format!(
67-
"{}{}{}",
68-
" ".repeat(StyledGraphemes::from(symbol(kind)).widths()),
69-
" ".repeat(indent(kind)),
70-
id(kind),
71-
),
72-
self.config.inactive_item_style,
73-
)
74-
}
75-
});
39+
let rows = self.document.extract_rows_from_current(height);
40+
let lines = rows.iter().enumerate().map(|(offset, row)| {
41+
if offset == 0 {
42+
StyledGraphemes::from_str(
43+
format!(
44+
"{}{}{}",
45+
symbol(row),
46+
" ".repeat(row.depth * self.config.indent),
47+
row.id,
48+
),
49+
self.config.active_item_style,
50+
)
51+
} else {
52+
StyledGraphemes::from_str(
53+
format!(
54+
"{}{}{}",
55+
" ".repeat(StyledGraphemes::from(symbol(row)).widths()),
56+
" ".repeat(row.depth * self.config.indent),
57+
row.id,
58+
),
59+
self.config.inactive_item_style,
60+
)
61+
}
62+
});
7663

7764
StyledGraphemes::from_lines(lines)
7865
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use super::{Row, treez::RowOperation};
2+
3+
/// Represents a navigable tree document, allowing for efficient row navigation and folding.
4+
#[derive(Clone)]
5+
pub struct Document {
6+
rows: Vec<Row>,
7+
position: usize,
8+
}
9+
10+
impl Document {
11+
pub fn new(rows: Vec<Row>) -> Self {
12+
Self { rows, position: 0 }
13+
}
14+
15+
pub fn from_path(path: &std::path::Path) -> anyhow::Result<Self> {
16+
Ok(Self::new(super::treez::create_rows_from_path(path)?))
17+
}
18+
}
19+
20+
impl Document {
21+
/// Returns a reference to the underlying vector of rows.
22+
pub fn rows(&self) -> &[Row] {
23+
&self.rows
24+
}
25+
26+
/// Returns the selected tree path represented by visible row labels.
27+
pub fn get(&self) -> Vec<String> {
28+
self.rows
29+
.get(self.position)
30+
.map(|row| row.path.clone())
31+
.unwrap_or_default()
32+
}
33+
34+
/// Extract rows from the current cursor position.
35+
pub fn extract_rows_from_current(&self, n: usize) -> Vec<Row> {
36+
self.rows.extract(self.position, n)
37+
}
38+
39+
/// Toggles the visibility of a node at the cursor's current position.
40+
pub fn toggle(&mut self) {
41+
let index = self.rows.toggle(self.position);
42+
self.position = index;
43+
}
44+
45+
/// Sets the visibility of all rows.
46+
pub fn set_nodes_visibility(&mut self, collapsed: bool) {
47+
self.rows.set_rows_visibility(collapsed);
48+
self.position = self.rows.head();
49+
}
50+
51+
/// Moves the cursor backward through rows.
52+
pub fn up(&mut self) -> bool {
53+
let index = self.rows.up(self.position);
54+
let ret = index != self.position;
55+
self.position = index;
56+
ret
57+
}
58+
59+
/// Moves the cursor to the head position.
60+
pub fn head(&mut self) -> bool {
61+
self.position = self.rows.head();
62+
true
63+
}
64+
65+
/// Moves the cursor forward through rows.
66+
pub fn down(&mut self) -> bool {
67+
let index = self.rows.down(self.position);
68+
let ret = index != self.position;
69+
self.position = index;
70+
ret
71+
}
72+
73+
/// Moves the cursor to the last position.
74+
pub fn tail(&mut self) -> bool {
75+
self.position = self.rows.tail();
76+
true
77+
}
78+
}

0 commit comments

Comments
 (0)