managarr_tree_widget/
tree_state.rs

1use ratatui::layout::{Position, Rect};
2use ratatui::text::ToText;
3use std::collections::HashSet;
4use std::fmt::Display;
5use std::hash::Hash;
6
7use crate::flatten::{flatten, Flattened};
8use crate::tree_item::TreeItem;
9
10/// Keeps the state of what is currently selected and what was opened in a [`Tree`](crate::Tree).
11///
12/// # Example
13///
14/// ```
15/// # use managarr_tree_widget::TreeState;
16///
17/// let mut state = TreeState::default();
18/// ```
19#[must_use]
20#[derive(Debug, Default)]
21pub struct TreeState {
22    pub(super) offset: usize,
23    pub(super) opened: HashSet<Vec<u64>>,
24    pub(super) selected: Vec<u64>,
25    pub(super) ensure_selected_in_view_on_next_render: bool,
26
27    pub(super) last_area: Rect,
28    pub(super) last_biggest_index: usize,
29    /// All identifiers open on last render
30    pub(super) last_identifiers: Vec<Vec<u64>>,
31    /// Identifier rendered at `y` on last render
32    pub(super) last_rendered_identifiers: Vec<(u16, Vec<u64>)>,
33}
34
35impl TreeState {
36    #[must_use]
37    pub const fn get_offset(&self) -> usize {
38        self.offset
39    }
40
41    #[must_use]
42    #[deprecated = "Use self.opened()"]
43    pub fn get_all_opened(&self) -> Vec<Vec<u64>> {
44        self.opened.iter().cloned().collect()
45    }
46
47    #[must_use]
48    pub const fn opened(&self) -> &HashSet<Vec<u64>> {
49        &self.opened
50    }
51
52    #[must_use]
53    pub fn selected(&self) -> &[u64] {
54        &self.selected
55    }
56
57    /// Get a flat list of all currently viewable (including by scrolling) [`TreeItem`]s with this `TreeState`.
58    #[must_use]
59    pub fn flatten<'a, T>(&self, items: &'a [TreeItem<T>]) -> Vec<Flattened<'a, T>>
60    where
61        T: ToText + Clone + Default + Display + Hash + PartialEq + Eq,
62    {
63        flatten(&self.opened, items, &[])
64    }
65
66    /// Selects the given identifier.
67    ///
68    /// Returns `true` when the selection changed.
69    ///
70    /// Clear the selection by passing an empty identifier vector:
71    ///
72    /// ```rust
73    /// # use managarr_tree_widget::TreeState;
74    /// # let mut state = TreeState::default();
75    /// state.select(Vec::new());
76    /// ```
77    pub fn select(&mut self, identifier: Vec<u64>) -> bool {
78        self.ensure_selected_in_view_on_next_render = true;
79        let changed = self.selected != identifier;
80        self.selected = identifier;
81        changed
82    }
83
84    /// Open a tree node.
85    /// Returns `true` when it was closed and has been opened.
86    /// Returns `false` when it was already open.
87    pub fn open(&mut self, identifier: Vec<u64>) -> bool {
88        if identifier.is_empty() {
89            false
90        } else {
91            self.opened.insert(identifier)
92        }
93    }
94
95    /// Close a tree node.
96    /// Returns `true` when it was open and has been closed.
97    /// Returns `false` when it was already closed.
98    pub fn close(&mut self, identifier: &[u64]) -> bool {
99        self.opened.remove(identifier)
100    }
101
102    /// Toggles a tree node open/close state.
103    /// When it is currently open, then [`close`](Self::close) is called. Otherwise [`open`](Self::open).
104    ///
105    /// Returns `true` when a node is opened / closed.
106    /// As toggle always changes something, this only returns `false` when an empty identifier is given.
107    pub fn toggle(&mut self, identifier: Vec<u64>) -> bool {
108        if identifier.is_empty() {
109            false
110        } else if self.opened.contains(&identifier) {
111            self.close(&identifier)
112        } else {
113            self.open(identifier)
114        }
115    }
116
117    /// Toggles the currently selected tree node open/close state.
118    /// See also [`toggle`](Self::toggle)
119    ///
120    /// Returns `true` when a node is opened / closed.
121    /// As toggle always changes something, this only returns `false` when nothing is selected.
122    pub fn toggle_selected(&mut self) -> bool {
123        if self.selected.is_empty() {
124            return false;
125        }
126
127        self.ensure_selected_in_view_on_next_render = true;
128
129        // Reimplement self.close because of multiple different borrows
130        let was_open = self.opened.remove(&self.selected);
131        if was_open {
132            return true;
133        }
134
135        self.open(self.selected.clone())
136    }
137
138    /// Closes all open nodes.
139    ///
140    /// Returns `true` when any node was closed.
141    pub fn close_all(&mut self) -> bool {
142        if self.opened.is_empty() {
143            false
144        } else {
145            self.opened.clear();
146            true
147        }
148    }
149
150    /// Select the first node.
151    ///
152    /// Returns `true` when the selection changed.
153    pub fn select_first(&mut self) -> bool {
154        let identifier = self.last_identifiers.first().cloned().unwrap_or_default();
155        self.select(identifier)
156    }
157
158    /// Select the last node.
159    ///
160    /// Returns `true` when the selection changed.
161    pub fn select_last(&mut self) -> bool {
162        let new_identifier = self.last_identifiers.last().cloned().unwrap_or_default();
163        self.select(new_identifier)
164    }
165
166    /// Select the node on the given index.
167    ///
168    /// Returns `true` when the selection changed.
169    ///
170    /// This can be useful for mouse clicks.
171    #[deprecated = "Prefer self.click_at or self.rendered_at as visible index is hard to predict with height != 1"]
172    pub fn select_visible_index(&mut self, new_index: usize) -> bool {
173        let new_index = new_index.min(self.last_biggest_index);
174        let new_identifier = self
175            .last_identifiers
176            .get(new_index)
177            .cloned()
178            .unwrap_or_default();
179        self.select(new_identifier)
180    }
181
182    /// Move the current selection with the direction/amount by the given function.
183    ///
184    /// Returns `true` when the selection changed.
185    ///
186    /// # Example
187    ///
188    /// ```
189    /// # use managarr_tree_widget::TreeState;
190    /// # let mut state = TreeState::default();
191    /// // Move the selection one down
192    /// state.select_relative(|current| {
193    ///     // When nothing is currently selected, select index 0
194    ///     // Otherwise select current + 1 (without panicking)
195    ///     current.map_or(0, |current| current.saturating_add(1))
196    /// });
197    /// ```
198    ///
199    /// For more examples take a look into the source code of [`key_up`](Self::key_up) or [`key_down`](Self::key_down).
200    /// They are implemented with this method.
201    #[deprecated = "renamed to select_relative"]
202    pub fn select_visible_relative<F>(&mut self, change_function: F) -> bool
203    where
204        F: FnOnce(Option<usize>) -> usize,
205    {
206        let identifiers = &self.last_identifiers;
207        let current_identifier = &self.selected;
208        let current_index = identifiers
209            .iter()
210            .position(|identifier| identifier == current_identifier);
211        let new_index = change_function(current_index).min(self.last_biggest_index);
212        let new_identifier = identifiers.get(new_index).cloned().unwrap_or_default();
213        self.select(new_identifier)
214    }
215
216    /// Move the current selection with the direction/amount by the given function.
217    ///
218    /// Returns `true` when the selection changed.
219    ///
220    /// # Example
221    ///
222    /// ```
223    /// # use managarr_tree_widget::TreeState;
224    /// # let mut state = TreeState::default();
225    /// // Move the selection one down
226    /// state.select_relative(|current| {
227    ///     // When nothing is currently selected, select index 0
228    ///     // Otherwise select current + 1 (without panicking)
229    ///     current.map_or(0, |current| current.saturating_add(1))
230    /// });
231    /// ```
232    ///
233    /// For more examples take a look into the source code of [`key_up`](Self::key_up) or [`key_down`](Self::key_down).
234    /// They are implemented with this method.
235    pub fn select_relative<F>(&mut self, change_function: F) -> bool
236    where
237        F: FnOnce(Option<usize>) -> usize,
238    {
239        let identifiers = &self.last_identifiers;
240        let current_identifier = &self.selected;
241        let current_index = identifiers
242            .iter()
243            .position(|identifier| identifier == current_identifier);
244        let new_index = change_function(current_index).min(self.last_biggest_index);
245        let new_identifier = identifiers.get(new_index).cloned().unwrap_or_default();
246        self.select(new_identifier)
247    }
248
249    /// Get the identifier that was rendered for the given position on last render.
250    #[must_use]
251    pub fn rendered_at(&self, position: Position) -> Option<&[u64]> {
252        if !self.last_area.contains(position) {
253            return None;
254        }
255
256        self.last_rendered_identifiers
257            .iter()
258            .rev()
259            .find(|(y, _)| position.y >= *y)
260            .map(|(_, identifier)| identifier.as_ref())
261    }
262
263    /// Select what was rendered at the given position on last render.
264    /// When it is already selected, toggle it.
265    ///
266    /// Returns `true` when the state changed.
267    /// Returns `false` when there was nothing at the given position.
268    pub fn click_at(&mut self, position: Position) -> bool {
269        if let Some(identifier) = self.rendered_at(position) {
270            if identifier == self.selected {
271                self.toggle_selected()
272            } else {
273                self.select(identifier.to_vec())
274            }
275        } else {
276            false
277        }
278    }
279
280    /// Ensure the selected [`TreeItem`] is in view on next render
281    pub fn scroll_selected_into_view(&mut self) {
282        self.ensure_selected_in_view_on_next_render = true;
283    }
284
285    /// Scroll the specified amount of lines up
286    ///
287    /// Returns `true` when the scroll position changed.
288    /// Returns `false` when the scrolling has reached the top.
289    pub fn scroll_up(&mut self, lines: usize) -> bool {
290        let before = self.offset;
291        self.offset = self.offset.saturating_sub(lines);
292        before != self.offset
293    }
294
295    /// Scroll the specified amount of lines down
296    ///
297    /// Returns `true` when the scroll position changed.
298    /// Returns `false` when the scrolling has reached the last [`TreeItem`].
299    pub fn scroll_down(&mut self, lines: usize) -> bool {
300        let before = self.offset;
301        self.offset = self
302            .offset
303            .saturating_add(lines)
304            .min(self.last_biggest_index);
305        before != self.offset
306    }
307
308    /// Handles the up arrow key.
309    /// Moves up in the current depth or to its parent.
310    ///
311    /// Returns `true` when the selection changed.
312    pub fn key_up(&mut self) -> bool {
313        self.select_relative(|current| {
314            // When nothing is selected, fall back to end
315            current.map_or(usize::MAX, |current| current.saturating_sub(1))
316        })
317    }
318
319    /// Handles the down arrow key.
320    /// Moves down in the current depth or into a child node.
321    ///
322    /// Returns `true` when the selection changed.
323    pub fn key_down(&mut self) -> bool {
324        self.select_relative(|current| {
325            // When nothing is selected, fall back to start
326            current.map_or(0, |current| current.saturating_add(1))
327        })
328    }
329
330    /// Handles the left arrow key.
331    /// Closes the currently selected or moves to its parent.
332    ///
333    /// Returns `true` when the selection or the open state changed.
334    pub fn key_left(&mut self) -> bool {
335        self.ensure_selected_in_view_on_next_render = true;
336        // Reimplement self.close because of multiple different borrows
337        let mut changed = self.opened.remove(&self.selected);
338        if !changed {
339            // Select the parent by removing the leaf from selection
340            let popped = self.selected.pop();
341            changed = popped.is_some();
342        }
343        changed
344    }
345
346    /// Handles the right arrow key.
347    /// Opens the currently selected.
348    ///
349    /// Returns `true` when it was closed and has been opened.
350    /// Returns `false` when it was already open or nothing being selected.
351    pub fn key_right(&mut self) -> bool {
352        if self.selected.is_empty() {
353            false
354        } else {
355            self.ensure_selected_in_view_on_next_render = true;
356            self.open(self.selected.clone())
357        }
358    }
359}