use super::{TuiTreeItem, TuiTreeState};
use tui_tree_widget::{flatten, identifier};
#[derive(Debug)]
pub struct StatefulTree<'a> {
pub state: TuiTreeState,
pub items: Vec<TuiTreeItem<'a>>,
}
enum MoveDirection {
Up,
Down,
}
impl<'a> StatefulTree<'a> {
pub fn new() -> Self {
Self {
state: TuiTreeState::default(),
items: Vec::new(),
}
}
pub fn with_items(mut self, items: Vec<TuiTreeItem<'a>>) -> Self {
self.items = items;
self
}
pub fn next(&mut self) {
self.move_up_down(MoveDirection::Down);
}
pub fn previous(&mut self) {
self.move_up_down(MoveDirection::Up);
}
fn move_up_down(&mut self, direction: MoveDirection) {
let visible = flatten(&self.state.get_all_opened(), &self.items);
let current_identifier = self.state.selected();
let current_index = visible
.iter()
.position(|o| o.identifier == current_identifier);
let new_index = current_index.map_or(0, |current_index| {
match direction {
MoveDirection::Down => current_index.saturating_add(1),
MoveDirection::Up => current_index.saturating_sub(1),
}
.min(visible.len() - 1)
});
let new_identifier = visible.get(new_index).unwrap().identifier.clone();
self.state.select(new_identifier);
}
pub fn close(&mut self) {
let selected = self.selected();
if !self.state.close(&selected) {
let (head, _) = identifier::get_without_leaf(&selected);
self.state.select(head);
}
}
pub fn open(&mut self) {
self.state.open(self.selected());
}
pub fn selected(&self) -> Vec<usize> {
self.state.selected()
}
pub fn set_state(&mut self, route: &[usize]) {
fn set_state_m(ptr: &mut StatefulTree, route: &[usize]) {
if route.is_empty() {
return;
}
let next: usize = route[0] + 1;
for _ in 0..next {
ptr.next();
}
ptr.open();
set_state_m(ptr, &route[1..])
}
self.state = TuiTreeState::default();
set_state_m(self, route);
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{Node, Tree};
use pretty_assertions::assert_eq;
#[test]
fn test_stateful_tree() {
let tree: Tree = Tree::new(
Node::new("/", "/")
.with_child(
Node::new("/bin", "bin/")
.with_child(Node::new("/bin/ls", "ls"))
.with_child(Node::new("/bin/pwd", "pwd")),
)
.with_child(
Node::new("/home", "home/").with_child(
Node::new("/home/omar", "omar/")
.with_child(Node::new("/home/omar/readme.md", "readme.md"))
.with_child(Node::new("/home/omar/changelog.md", "changelog.md")),
),
),
);
let mut stateful_tree = StatefulTree::from(&tree);
stateful_tree.next();
assert_eq!(stateful_tree.selected(), vec![0]);
stateful_tree.open();
assert_eq!(stateful_tree.selected(), vec![0]);
stateful_tree.next();
assert_eq!(stateful_tree.selected(), vec![0, 0]);
stateful_tree.next();
assert_eq!(stateful_tree.selected(), vec![0, 1]);
stateful_tree.open();
assert_eq!(stateful_tree.selected(), vec![0, 1]);
stateful_tree.next();
stateful_tree.open();
assert_eq!(stateful_tree.selected(), vec![0, 1, 0]);
stateful_tree.next();
stateful_tree.next();
stateful_tree.next();
assert_eq!(stateful_tree.selected(), vec![0, 1, 0, 1]);
stateful_tree.previous();
assert_eq!(stateful_tree.selected(), vec![0, 1, 0, 0]);
stateful_tree.close();
assert_eq!(stateful_tree.selected(), vec![0, 1, 0]);
stateful_tree.set_state(&vec![0, 0, 1]);
assert_eq!(stateful_tree.selected(), vec![0, 0, 1]);
stateful_tree.set_state(&vec![0, 1, 0, 1]);
assert_eq!(stateful_tree.selected(), vec![0, 1, 0, 1]);
}
}