1use std::hash::Hash;
2
3#[cfg(feature = "keymap")]
4use crossterm::event::KeyEvent;
5
6use crate::action::{
7 TreeAction, TreeEditAction, TreeEditRequest, TreeEvent, TreeIntent, TreeViewAction,
8};
9use crate::columns::TreeColumns;
10use crate::context::TreeExpansionState;
11use crate::edit::{TreeChangeSet, TreeEditCommand, TreeEditor, TreeSelectionUpdate};
12use crate::model::{TreeFilter, TreeModel, TreeQuery, TreeSort};
13
14use super::TreeListViewState;
15
16#[derive(Clone, Copy)]
17enum ExpansionAction {
18 Expand,
19 Toggle,
20}
21
22impl<Id: Copy + Eq + Hash> TreeListViewState<Id> {
23 pub fn handle_action<T, F, S, C, Custom>(
25 &mut self,
26 model: &T,
27 query: &TreeQuery<F, S>,
28 columns: &C,
29 action: TreeAction<Custom>,
30 ) -> TreeEvent<Id, Custom>
31 where
32 T: TreeModel<Id = Id>,
33 F: TreeFilter<T>,
34 S: TreeSort<T>,
35 C: TreeColumns<T>,
36 {
37 self.ensure_projection(model, query);
38 let event = match action {
39 TreeAction::View(action) => {
40 self.handle_view_action(model, columns.column_count(), action)
41 }
42 TreeAction::Edit(action) => self.handle_edit_intent(action),
43 TreeAction::Custom(custom) => TreeEvent::Intent(TreeIntent::Custom(custom)),
44 };
45 if matches!(event, TreeEvent::Changed) {
46 self.ensure_projection(model, query);
47 }
48 event
49 }
50
51 pub fn apply_edit<T, F, S>(
57 &mut self,
58 model: &mut T,
59 query: &TreeQuery<F, S>,
60 command: TreeEditCommand<Id>,
61 ) -> Result<TreeChangeSet<Id>, T::Error>
62 where
63 T: TreeEditor<Id = Id>,
64 F: TreeFilter<T>,
65 S: TreeSort<T>,
66 {
67 let changes = model.apply(command)?;
68 self.reconcile_changes(&changes);
69 if let TreeSelectionUpdate::Select(id) = changes.selection {
70 self.expand_to(model, id);
71 }
72 self.ensure_projection(model, query);
73 Ok(changes)
74 }
75
76 pub fn reconcile_changes(&mut self, changes: &TreeChangeSet<Id>) {
78 self.expanded.retain(|path| {
79 !changes.removed.contains(&path.id)
80 && !path
81 .parent
82 .is_some_and(|parent| changes.removed.contains(&parent))
83 && !changes.moved.contains(&path.id)
84 });
85
86 self.manual_marked
87 .retain(|id| !changes.removed.contains(id));
88
89 match changes.selection {
90 TreeSelectionUpdate::Keep => {}
91 TreeSelectionUpdate::Select(id) => {
92 self.selected = Some(id);
93 self.selected_row = None;
94 self.selection_needs_visibility = true;
95 }
96 TreeSelectionUpdate::Clear => {
97 self.selected = None;
98 self.selected_row = None;
99 self.selection_needs_visibility = false;
100 }
101 }
102 }
103
104 fn handle_view_action<T, C>(
105 &mut self,
106 model: &T,
107 column_count: usize,
108 action: TreeViewAction,
109 ) -> TreeEvent<Id, C>
110 where
111 T: TreeModel<Id = Id>,
112 {
113 let changed = match action {
114 TreeViewAction::SelectPrev => self.select_prev(),
115 TreeViewAction::SelectNext => self.select_next(),
116 TreeViewAction::SelectParent => self.select_parent(),
117 TreeViewAction::SelectFirstChild => self.select_first_child(),
118 TreeViewAction::Expand => {
119 return self.change_selected_expansion(ExpansionAction::Expand);
120 }
121 TreeViewAction::Collapse => self.collapse_selected(),
122 TreeViewAction::ExpandOrSelectFirstChild => {
123 return self.expand_or_select_first_child();
124 }
125 TreeViewAction::CollapseOrSelectParent => {
126 if self.collapse_selected() {
127 true
128 } else {
129 self.select_parent()
130 }
131 }
132 TreeViewAction::ToggleNode => {
133 return self.change_selected_expansion(ExpansionAction::Toggle);
134 }
135 TreeViewAction::ToggleRecursive => return self.toggle_selected_recursive(model),
136 TreeViewAction::ExpandAll => self.expand_all(model),
137 TreeViewAction::CollapseAll => self.collapse_all(),
138 TreeViewAction::ToggleGuides => {
139 self.draw_lines = !self.draw_lines;
140 true
141 }
142 TreeViewAction::ToggleMark => self
143 .selected
144 .is_some_and(|selected| self.toggle_marked(selected)),
145 TreeViewAction::SelectFirst => self.select_first(),
146 TreeViewAction::SelectLast => self.select_last(),
147 TreeViewAction::SelectColumnLeft => self.select_column_left(column_count),
148 TreeViewAction::SelectColumnRight => self.select_column_right(column_count),
149 TreeViewAction::SelectFirstColumn => {
150 self.select_column((column_count > 0).then_some(0), column_count)
151 }
152 TreeViewAction::SelectLastColumn => self.select_column(
153 (column_count > 0).then_some(column_count.saturating_sub(1)),
154 column_count,
155 ),
156 TreeViewAction::ScrollViewUp => self.scroll_view_by(-1),
157 TreeViewAction::ScrollViewDown => self.scroll_view_by(1),
158 TreeViewAction::ScrollLeft => self.scroll_horizontal_by(-1),
159 TreeViewAction::ScrollRight => self.scroll_horizontal_by(1),
160 };
161 changed_event(changed)
162 }
163
164 fn change_selected_expansion<C>(&mut self, action: ExpansionAction) -> TreeEvent<Id, C> {
165 let Some(node) = self.selected_node() else {
166 return TreeEvent::Unchanged;
167 };
168 match node.expansion() {
169 TreeExpansionState::Collapsed => {
170 self.set_expanded(node.id(), node.parent(), true);
171 TreeEvent::Changed
172 }
173 TreeExpansionState::Expanded if matches!(action, ExpansionAction::Toggle) => {
174 self.set_expanded(node.id(), node.parent(), false);
175 TreeEvent::Changed
176 }
177 TreeExpansionState::Unloaded => TreeEvent::Intent(TreeIntent::LoadChildren(node.id())),
178 TreeExpansionState::Leaf
179 | TreeExpansionState::Expanded
180 | TreeExpansionState::ForcedByFilter
181 | TreeExpansionState::Loading => TreeEvent::Unchanged,
182 }
183 }
184
185 fn collapse_selected(&mut self) -> bool {
186 let Some(node) = self.selected_node() else {
187 return false;
188 };
189 matches!(node.expansion(), TreeExpansionState::Expanded)
190 && self.set_expanded(node.id(), node.parent(), false)
191 }
192
193 fn expand_or_select_first_child<C>(&mut self) -> TreeEvent<Id, C> {
194 let event = self.change_selected_expansion(ExpansionAction::Expand);
195 match event {
196 TreeEvent::Unchanged => changed_event(self.select_first_child()),
197 TreeEvent::Changed | TreeEvent::Intent(_) => event,
198 }
199 }
200
201 fn toggle_selected_recursive<T, C>(&mut self, model: &T) -> TreeEvent<Id, C>
202 where
203 T: TreeModel<Id = Id>,
204 {
205 let Some(node) = self.selected_node() else {
206 return TreeEvent::Unchanged;
207 };
208 match node.expansion() {
209 TreeExpansionState::Collapsed | TreeExpansionState::Expanded => {
210 let expand = matches!(node.expansion(), TreeExpansionState::Collapsed);
211 changed_event(self.set_expanded_recursive(model, node.id(), node.parent(), expand))
212 }
213 TreeExpansionState::Unloaded => TreeEvent::Intent(TreeIntent::LoadChildren(node.id())),
214 TreeExpansionState::Leaf
215 | TreeExpansionState::ForcedByFilter
216 | TreeExpansionState::Loading => TreeEvent::Unchanged,
217 }
218 }
219
220 fn handle_edit_intent<C>(&self, action: TreeEditAction) -> TreeEvent<Id, C> {
221 let Some(node) = self.selected_node() else {
222 return TreeEvent::Unchanged;
223 };
224 let request = match action {
225 TreeEditAction::ReorderUp => {
226 let Some(parent) = node.parent() else {
227 return TreeEvent::Unchanged;
228 };
229 TreeEditRequest::ReorderUp {
230 node: node.id(),
231 parent,
232 }
233 }
234 TreeEditAction::ReorderDown => {
235 let Some(parent) = node.parent() else {
236 return TreeEvent::Unchanged;
237 };
238 TreeEditRequest::ReorderDown {
239 node: node.id(),
240 parent,
241 }
242 }
243 TreeEditAction::AddChild => TreeEditRequest::AddChild { parent: node.id() },
244 TreeEditAction::Rename => TreeEditRequest::Rename { node: node.id() },
245 TreeEditAction::Detach => {
246 let Some(parent) = node.parent() else {
247 return TreeEvent::Unchanged;
248 };
249 TreeEditRequest::Detach {
250 node: node.id(),
251 parent,
252 }
253 }
254 TreeEditAction::Delete => TreeEditRequest::Delete { node: node.id() },
255 TreeEditAction::Yank => TreeEditRequest::Yank { node: node.id() },
256 TreeEditAction::Paste => TreeEditRequest::Paste { parent: node.id() },
257 };
258 TreeEvent::Intent(TreeIntent::Edit(request))
259 }
260
261 #[cfg(feature = "keymap")]
262 pub fn handle_key<T, F, S, C>(
264 &mut self,
265 model: &T,
266 query: &TreeQuery<F, S>,
267 columns: &C,
268 key: KeyEvent,
269 ) -> TreeEvent<Id>
270 where
271 T: TreeModel<Id = Id>,
272 F: TreeFilter<T>,
273 S: TreeSort<T>,
274 C: TreeColumns<T>,
275 {
276 self.handle_key_with(model, query, columns, key, |_| None::<()>)
277 }
278
279 #[cfg(feature = "keymap")]
280 pub fn handle_key_with<T, F, S, C, Custom, R>(
282 &mut self,
283 model: &T,
284 query: &TreeQuery<F, S>,
285 columns: &C,
286 key: KeyEvent,
287 custom: R,
288 ) -> TreeEvent<Id, Custom>
289 where
290 T: TreeModel<Id = Id>,
291 F: TreeFilter<T>,
292 S: TreeSort<T>,
293 C: TreeColumns<T>,
294 R: Fn(KeyEvent) -> Option<Custom>,
295 {
296 let Some(action) = self.keymap.resolve_with(key, custom) else {
297 return TreeEvent::Unchanged;
298 };
299 self.handle_action(model, query, columns, action)
300 }
301}
302const fn changed_event<Id, Custom>(changed: bool) -> TreeEvent<Id, Custom> {
303 if changed {
304 TreeEvent::Changed
305 } else {
306 TreeEvent::Unchanged
307 }
308}