tui_realm_treeview/
tree_state.rs

1//! # Tree State
2//!
3//! This module implements the tree state.
4
5use super::Node;
6
7/// Tree state tracks the current state for the component tree.
8#[derive(Default, Clone)]
9pub struct TreeState {
10    /// Tracks open nodes
11    open: Vec<String>,
12    /// Current selected item
13    selected: Option<String>,
14}
15
16impl TreeState {
17    /// Returns whether `node` is open
18    pub fn is_open<V>(&self, node: &Node<V>) -> bool {
19        self.open.contains(node.id())
20    }
21
22    /// Returns whether `node` is closed
23    pub fn is_closed<V>(&self, node: &Node<V>) -> bool {
24        !self.is_open(node)
25    }
26
27    /// Get current selected item
28    pub fn selected(&self) -> Option<&str> {
29        self.selected.as_deref()
30    }
31
32    /// Returns whether provided node is currently selected
33    pub fn is_selected<V>(&self, node: &Node<V>) -> bool {
34        self.selected
35            .as_ref()
36            .map(|x| x == node.id())
37            .unwrap_or(false)
38    }
39
40    /// Get first sibling in children of current selected node's parent
41    pub fn first_sibling<'a, V>(&self, tree: &'a Node<V>) -> Option<&'a Node<V>> {
42        let selected = self.selected.as_ref()?;
43        let parent = tree.parent(selected)?;
44        parent.iter().next()
45    }
46
47    /// Get last sibling in children of current selected node's parent
48    pub fn last_sibling<'a, V>(&self, tree: &'a Node<V>) -> Option<&'a Node<V>> {
49        let selected = self.selected.as_ref()?;
50        let parent = tree.parent(selected)?;
51        parent.iter().last()
52    }
53
54    /// The tree has changed, so this method must check whether to keep states or not
55    pub fn tree_changed<V>(&mut self, root: &Node<V>, preserve: bool) {
56        if preserve {
57            // Check whether selected is still valid; if doesn't exist, use root
58            self.selected = self
59                .selected
60                .take()
61                .map(|selected| root.query(&selected).unwrap_or(root).id().to_string());
62            // Check whether open nodes still exist
63            self.open.retain(|x| root.query(x).is_some());
64        } else {
65            // Reset state
66            self.open = Vec::new();
67            self.selected = Some(root.id().to_string());
68        }
69    }
70
71    /// Open currently selected `node`. Node can be open only if it is closed and it is NOT a leaf
72    pub fn open<V>(&mut self, root: &Node<V>) {
73        if let Some(selected) = self.selected.as_ref() {
74            if let Some(node) = root.query(selected) {
75                self.open_node(root, node);
76            }
77        }
78    }
79
80    /// Close currently selected `node`.
81    /// If node has children, then all children are closed recursively
82    pub fn close<V>(&mut self, root: &Node<V>) {
83        if let Some(selected) = self.selected.as_ref() {
84            if let Some(node) = root.query(selected) {
85                if self.is_open(node) {
86                    self.close_node(node);
87                }
88            }
89        }
90    }
91
92    /// Move cursor down in current tree from current position. Rewind if required
93    pub fn move_down<V>(&mut self, root: &Node<V>) {
94        if let Some(selected) = self.selected.take() {
95            // Get current node
96            if let Some(node) = root.query(&selected) {
97                // If node is open, then move to its first child
98                if !node.is_leaf() && self.is_open(node) {
99                    // NOTE: unwrap is safe; checked by `is_leaf()`
100                    self.selected = Some(node.iter().next().unwrap().id().to_string());
101                } else {
102                    // If has a "next sibling", let's get it
103                    if let Some(sibling) = self.next_sibling(root, node) {
104                        self.selected = Some(sibling.id().to_string());
105                    } else {
106                        // Then the next element becomes the next sibling of the parent
107                        // this thing has to be performed recursively for all parents, until one is found (or root is reached)
108                        let mut current = &selected;
109                        loop {
110                            if let Some(parent) = root.parent(current) {
111                                current = parent.id();
112                                if let Some(sibling) = self.next_sibling(root, parent) {
113                                    self.selected = Some(sibling.id().to_string());
114                                    break;
115                                }
116                            } else {
117                                // has no parent, keep selectd
118                                self.selected = Some(selected);
119                                break;
120                            }
121                        }
122                    }
123                }
124            }
125        }
126    }
127
128    /// Move cursor up in current tree from current position. Rewind if required
129    pub fn move_up<V>(&mut self, root: &Node<V>) {
130        if let Some(selected) = self.selected.take() {
131            // Get parent
132            if let Some(parent) = root.parent(&selected) {
133                // Selected becomes previous sibling's last child; or if None, the parent
134                self.selected = Some(
135                    self.previous_sibling(root, root.query(&selected).unwrap())
136                        .map(|x| self.get_last_open_heir(x))
137                        .unwrap_or(parent)
138                        .id()
139                        .to_string(),
140                );
141            } else {
142                // Is root; then keep selected
143                self.selected = Some(selected);
144            }
145        }
146    }
147
148    /// Set current selected node.
149    /// When selecting a node, all its ancestors will be opened
150    pub fn select<V>(&mut self, root: &Node<V>, node: &Node<V>) {
151        self.open_ancestors(root, node);
152        self.selected = Some(node.id().to_string());
153    }
154
155    /// Close `node`
156    fn close_node<V>(&mut self, node: &Node<V>) {
157        // Remove from open nodes
158        self.open.retain(|x| x != node.id());
159        // Close children for node
160        self.close_children(node);
161    }
162
163    /// Open provided node
164    /// Node is opened only if is NOT a leaf and it is closed.
165    /// It will also open all the ancestors for `node`
166    fn open_node<V>(&mut self, root: &Node<V>, node: &Node<V>) {
167        if !node.is_leaf() && self.is_closed(node) {
168            self.open.push(node.id().to_string());
169        }
170        self.open_ancestors(root, node);
171    }
172
173    /// Close all node children recursively
174    fn close_children<V>(&mut self, node: &Node<V>) {
175        node.iter().for_each(|x| self.close_node(x));
176    }
177
178    /// Open all ancestors for `node` in the current `tree`
179    fn open_ancestors<V>(&mut self, root: &Node<V>, node: &Node<V>) {
180        if let Some(parent) = root.parent(node.id()) {
181            self.open_node(root, parent);
182        }
183    }
184
185    /// Returns the previous sibling of `node` in `root`
186    fn previous_sibling<'a, V>(
187        &mut self,
188        root: &'a Node<V>,
189        node: &'a Node<V>,
190    ) -> Option<&'a Node<V>> {
191        let parent = root.parent(node.id())?;
192        let mut prev_node = None;
193        for child in parent.iter() {
194            if child.id() == node.id() {
195                break;
196            }
197            prev_node = Some(child);
198        }
199        prev_node
200    }
201
202    /// Returs next sibling of `node` in `tree`
203    fn next_sibling<'a, V>(&mut self, root: &'a Node<V>, node: &'a Node<V>) -> Option<&'a Node<V>> {
204        let parent = root.parent(node.id())?;
205        let mut keep_next = false;
206        for child in parent.iter() {
207            if keep_next {
208                // Return child
209                return Some(child);
210            } else if child.id() == node.id() {
211                // keep next element
212                keep_next = true;
213            }
214        }
215        // No next sibling
216        None
217    }
218
219    /// Get last open heir for node
220    fn get_last_open_heir<'a, V>(&self, node: &'a Node<V>) -> &'a Node<V> {
221        if self.is_open(node) {
222            // If node is open, get its last child and call this function recursively
223            self.get_last_open_heir(node.iter().last().unwrap())
224        } else {
225            // Else return `node`
226            node
227        }
228    }
229
230    #[cfg(test)]
231    /// Force open nodes
232    pub fn force_open(&mut self, open: &[&str]) {
233        self.open = open.iter().map(|x| x.to_string()).collect();
234    }
235}
236
237#[cfg(test)]
238mod test {
239
240    use pretty_assertions::assert_eq;
241
242    use super::*;
243    use crate::mock::mock_tree;
244
245    #[test]
246    fn should_initialize_tree_state() {
247        let state = TreeState::default();
248        assert!(state.open.is_empty());
249        assert!(state.selected().is_none());
250    }
251
252    #[test]
253    fn should_select_nodes() {
254        let mut state = TreeState::default();
255        let tree = mock_tree();
256        // select 'bA'
257        state.select(tree.root(), tree.root().query(&String::from("bA")).unwrap());
258        assert_eq!(state.selected().unwrap(), "bA");
259        // All ancestors should be opened
260        assert_eq!(state.open.len(), 2);
261        assert!(state.is_open(tree.root().query(&String::from("b")).unwrap()));
262        assert!(state.is_open(tree.root()));
263    }
264
265    #[test]
266    fn should_open_and_close_nodes() {
267        let mut state = TreeState::default();
268        let tree = mock_tree();
269        // Select node 'bA'
270        let ba = tree.root().query(&String::from("bA")).unwrap();
271        state.select(tree.root(), ba);
272        // Open
273        state.open(tree.root());
274        assert!(state.is_open(ba));
275        // At this point also its ancestors should be open
276        assert!(state.is_open(tree.root().query(&String::from("b")).unwrap()));
277        assert!(state.is_open(tree.root()));
278        assert_eq!(state.open.len(), 3);
279        // let's open its child 'bA0'
280        let ba0 = tree.root().query(&String::from("bA0")).unwrap();
281        state.select(tree.root(), ba0);
282        state.open(tree.root());
283        assert!(state.is_open(ba0));
284        assert_eq!(state.open.len(), 4);
285        // Now let's close 'b'
286        let b = tree.root().query(&String::from("b")).unwrap();
287        state.select(tree.root(), b);
288        state.close(tree.root());
289        assert!(state.is_closed(b));
290        // All its children should be close
291        assert!(state.is_closed(ba));
292        assert!(state.is_closed(ba0));
293        // But root should still be open
294        assert!(state.is_open(tree.root()));
295    }
296
297    #[test]
298    fn should_not_open_twice() {
299        let mut state = TreeState::default();
300        let tree = mock_tree();
301        // Open root
302        state.select(tree.root(), tree.root());
303        state.open(tree.root());
304        assert_eq!(state.open.len(), 1);
305        assert!(state.is_open(tree.root()));
306        // Open twice
307        state.open(tree.root());
308        assert_eq!(state.open.len(), 1);
309        assert!(state.is_open(tree.root()));
310    }
311
312    #[test]
313    fn should_find_previous_sibling() {
314        let mut state = TreeState::default();
315        let tree = mock_tree();
316        let bb4 = tree.root().query(&String::from("bB4")).unwrap();
317        // Prev siblign should be bb3
318        let bb3 = tree.root().query(&String::from("bB3")).unwrap();
319        assert_eq!(state.previous_sibling(tree.root(), bb4).unwrap(), bb3);
320        // bb0 shouldn't have a previous sibling
321        let bb0 = tree.root().query(&String::from("bB0")).unwrap();
322        assert!(state.previous_sibling(tree.root(), bb0).is_none());
323    }
324
325    #[test]
326    fn should_find_next_sibling() {
327        let mut state = TreeState::default();
328        let tree = mock_tree();
329        let bb4 = tree.root().query(&String::from("bB4")).unwrap();
330        // Next siblign should be bb3
331        let bb5 = tree.root().query(&String::from("bB5")).unwrap();
332        assert_eq!(state.next_sibling(tree.root(), bb4).unwrap(), bb5);
333        // bb5 shouldn't have a previous sibling
334        assert!(state.next_sibling(tree.root(), bb5).is_none());
335    }
336
337    #[test]
338    fn should_find_first_sibling() {
339        let mut state = TreeState::default();
340        let tree = mock_tree();
341        let bb4 = tree.root().query(&String::from("bB4")).unwrap();
342        state.select(tree.root(), bb4);
343        // First siblign should be bb1
344        let bb0 = tree.root().query(&String::from("bB0")).unwrap();
345        assert_eq!(state.first_sibling(tree.root()).unwrap(), bb0);
346        // / shouldn't have a first sibling
347        state.select(tree.root(), tree.root());
348        assert!(state.first_sibling(tree.root()).is_none());
349    }
350
351    #[test]
352    fn should_find_last_sibling() {
353        let mut state = TreeState::default();
354        let tree = mock_tree();
355        let bb2 = tree.root().query(&String::from("bB2")).unwrap();
356        state.select(tree.root(), bb2);
357        // First siblign should be bb1
358        let bb5 = tree.root().query(&String::from("bB5")).unwrap();
359        assert_eq!(state.last_sibling(tree.root()).unwrap(), bb5);
360        // / shouldn't have a last sibling
361        state.select(tree.root(), tree.root());
362        assert!(state.last_sibling(tree.root()).is_none());
363    }
364
365    #[test]
366    fn should_preserve_tree_state() {
367        let mut state = TreeState::default();
368        let tree = mock_tree();
369        // Open cA branch
370        let ca = tree.root().query(&String::from("cA")).unwrap();
371        state.select(tree.root(), ca);
372        state.open(tree.root());
373        let bb5 = tree.root().query(&String::from("bB5")).unwrap();
374        state.select(tree.root(), bb5);
375        // Reinitialize tree
376        state.tree_changed(tree.root(), true);
377        // States should have been preserved
378        assert_eq!(state.open.len(), 5);
379        assert!(state.is_open(ca));
380        assert_eq!(state.selected().unwrap(), "bB5");
381    }
382
383    #[test]
384    fn should_not_preserve_tree_state() {
385        let mut state = TreeState::default();
386        let mut tree = mock_tree();
387        // Open cA branch
388        let ca = tree.root().query(&String::from("cA")).unwrap();
389        state.select(tree.root(), ca);
390        state.open(tree.root());
391        let bb5 = tree.root().query(&String::from("bB5")).unwrap();
392        state.select(tree.root(), bb5);
393        // New tree without 'c' and without 'bB'
394        tree.root_mut()
395            .query_mut(&String::from("b"))
396            .unwrap()
397            .remove_child(&String::from("bB"));
398        tree.root_mut().remove_child(&String::from("c"));
399        // Re initialize tree
400        state.tree_changed(tree.root(), true);
401        // Select should be root
402        assert_eq!(state.selected().unwrap(), "/");
403        // No node should be open, except for root and 'b'
404        assert_eq!(state.open.len(), 2);
405        assert_eq!(state.open, vec![String::from("/"), String::from("b")]);
406        assert!(state.is_open(tree.root()));
407    }
408
409    #[test]
410    fn should_reinitialize_tree_state() {
411        let mut state = TreeState::default();
412        let tree = mock_tree();
413        // Open branches
414        let ca = tree.root().query(&String::from("cA")).unwrap();
415        state.select(tree.root(), ca);
416        state.open(tree.root());
417        let bb5 = tree.root().query(&String::from("bB5")).unwrap();
418        state.select(tree.root(), bb5);
419        // Re-initialize tree
420        state.tree_changed(tree.root(), false);
421        // No node should be open; selected should be root
422        assert!(state.open.is_empty());
423        assert_eq!(state.selected().unwrap(), "/");
424    }
425
426    #[test]
427    fn should_move_cursor_down_on_sibling() {
428        let mut state = TreeState::default();
429        let tree = mock_tree();
430        // Let's start from bB5
431        let bb3 = tree.root().query(&String::from("bB3")).unwrap();
432        state.select(tree.root(), bb3);
433        // Move down (should become bb4)
434        state.move_down(tree.root());
435        assert_eq!(state.selected().unwrap(), "bB4");
436        // Move down (should become bb5)
437        state.move_down(tree.root());
438        assert_eq!(state.selected().unwrap(), "bB5");
439    }
440
441    #[test]
442    fn should_move_cursor_down_to_next_lower_node_if_last_child() {
443        let mut state = TreeState::default();
444        let tree = mock_tree();
445        // Let's start from bB5
446        let bb5 = tree.root().query(&String::from("bB5")).unwrap();
447        state.select(tree.root(), bb5);
448        // Move down (should become 'c')
449        state.move_down(tree.root());
450        assert_eq!(state.selected().unwrap(), "c");
451    }
452
453    #[test]
454    fn should_move_cursor_down_to_child_if_parent_is_open() {
455        let mut state = TreeState::default();
456        let tree = mock_tree();
457        // Let's start from b
458        let b = tree.root().query(&String::from("b")).unwrap();
459        state.select(tree.root(), b);
460        // Open b
461        state.open(tree.root());
462        // Move down (should become 'bA')
463        state.move_down(tree.root());
464        assert_eq!(state.selected().unwrap(), "bA");
465        // Open bA
466        state.open(tree.root());
467        // Move down (should become 'bA0')
468        state.move_down(tree.root());
469        assert_eq!(state.selected().unwrap(), "bA0");
470    }
471
472    #[test]
473    fn should_move_cursor_down_to_next_sibling_if_node_is_closed() {
474        let mut state = TreeState::default();
475        let tree = mock_tree();
476        // Let's start from b
477        let aa = tree.root().query(&String::from("aA")).unwrap();
478        state.select(tree.root(), aa);
479        // Move down (should become 'aB')
480        state.move_down(tree.root());
481        assert_eq!(state.selected().unwrap(), "aB");
482        // Move down (should become 'aC')
483        state.move_down(tree.root());
484        assert_eq!(state.selected().unwrap(), "aC");
485    }
486
487    #[test]
488    fn should_not_move_cursor_down_if_root_is_closed() {
489        let mut state = TreeState::default();
490        let tree = mock_tree();
491        state.select(tree.root(), tree.root());
492        // Move cursor down
493        state.move_down(tree.root());
494        assert_eq!(state.selected().unwrap(), "/");
495    }
496
497    #[test]
498    fn should_not_move_cursor_down_if_last_element_is_selected() {
499        let mut state = TreeState::default();
500        let tree = mock_tree();
501        let ca2 = tree.root().query(&String::from("cA2")).unwrap();
502        state.select(tree.root(), ca2);
503        // Move cursor down (keep last element)
504        state.move_down(tree.root());
505        assert_eq!(state.selected().unwrap(), "cA2");
506    }
507
508    #[test]
509    fn should_move_cursor_up_on_sibling() {
510        let mut state = TreeState::default();
511        let tree = mock_tree();
512        // Let's start from bB5
513        let bb4 = tree.root().query(&String::from("bB4")).unwrap();
514        state.select(tree.root(), bb4);
515        // Move up (should become 'bB3')
516        state.move_up(tree.root());
517        assert_eq!(state.selected().unwrap(), "bB3");
518        // Move up (should become 'bB2')
519        state.move_up(tree.root());
520        assert_eq!(state.selected().unwrap(), "bB2");
521    }
522
523    #[test]
524    fn should_move_cursor_up_on_deepest_child_of_previous_sibling() {
525        let mut state = TreeState::default();
526        let tree = mock_tree();
527        // Open 'bB'
528        state.select(tree.root(), tree.root().query(&String::from("bB")).unwrap());
529        state.open(tree.root());
530        // Select 'c'
531        state.select(tree.root(), tree.root().query(&String::from("c")).unwrap());
532        // Move up (should become 'bB5')
533        state.move_up(tree.root());
534        assert_eq!(state.selected().unwrap(), "bB5");
535    }
536
537    #[test]
538    fn should_move_cursor_up_to_parent_if_first_child() {
539        let mut state = TreeState::default();
540        let tree = mock_tree();
541        // Open 'bB'
542        state.select(
543            tree.root(),
544            tree.root().query(&String::from("bB0")).unwrap(),
545        );
546        // Move up (should become 'bB')
547        state.move_up(tree.root());
548        assert_eq!(state.selected().unwrap(), "bB");
549        // Move up (should become 'bA')
550        state.move_up(tree.root());
551        assert_eq!(state.selected().unwrap(), "bA");
552        // Move up (should become 'b')
553        state.move_up(tree.root());
554        assert_eq!(state.selected().unwrap(), "b");
555    }
556
557    #[test]
558    fn should_not_move_cursor_up_if_root_is_selected() {
559        let mut state = TreeState::default();
560        let tree = mock_tree();
561        state.select(tree.root(), tree.root());
562        // Move cursor down
563        state.move_up(tree.root());
564        assert_eq!(state.selected().unwrap(), "/");
565    }
566
567    #[test]
568    fn should_get_last_open_heir() {
569        let mut state = TreeState::default();
570        let tree = mock_tree();
571        // Open aA, aB, aC
572        state.select(tree.root(), tree.root().query(&String::from("aA")).unwrap());
573        state.open(tree.root());
574        state.select(tree.root(), tree.root().query(&String::from("aB")).unwrap());
575        state.open(tree.root());
576        state.select(tree.root(), tree.root().query(&String::from("aC")).unwrap());
577        state.open(tree.root());
578        // Open bB
579        state.select(tree.root(), tree.root().query(&String::from("bB")).unwrap());
580        state.open(tree.root());
581        // Get last open heir from root
582        assert_eq!(
583            state
584                .get_last_open_heir(tree.root().query(&String::from("bB")).unwrap())
585                .id()
586                .as_str(),
587            "bB5"
588        );
589        // Get last open heir from a
590        assert_eq!(
591            state
592                .get_last_open_heir(tree.root().query(&String::from("a")).unwrap())
593                .id()
594                .as_str(),
595            "aC0"
596        );
597    }
598}