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}