egui_arbor/tree_ops.rs
1//! Tree manipulation operations for outliner nodes.
2//!
3//! This module provides the [`TreeOperations`] trait which offers default implementations
4//! for common tree manipulation operations like renaming, removing, and inserting nodes.
5//! These operations are essential for implementing drag-drop and editing functionality.
6//!
7//! # Examples
8//!
9//! ```
10//! use egui_arbor::{OutlinerNode, tree_ops::TreeOperations, DropPosition};
11//!
12//! #[derive(Clone)]
13//! struct MyNode {
14//! id: u64,
15//! name: String,
16//! children: Vec<MyNode>,
17//! }
18//!
19//! impl OutlinerNode for MyNode {
20//! type Id = u64;
21//! fn id(&self) -> Self::Id { self.id }
22//! fn name(&self) -> &str { &self.name }
23//! fn is_collection(&self) -> bool { !self.children.is_empty() }
24//! fn children(&self) -> &[Self] { &self.children }
25//! fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
26//! }
27//!
28//! // Automatically get tree operations!
29//! impl TreeOperations for MyNode {}
30//!
31//! let mut node = MyNode { id: 1, name: "root".into(), children: vec![] };
32//! node.rename_node(&1, "new_name".into());
33//! ```
34
35use crate::traits::{DropPosition, OutlinerNode};
36
37/// Trait providing tree manipulation operations for outliner nodes.
38///
39/// This trait offers default implementations for common tree operations:
40/// - **Renaming**: Find and update a node's name by ID
41/// - **Removing**: Extract a node from the tree by ID
42/// - **Inserting**: Place a node at a specific position relative to a target
43///
44/// All methods use recursive traversal to locate nodes within the tree hierarchy.
45///
46/// # Default Implementations
47///
48/// Simply implement this trait with an empty body to get all operations:
49///
50/// ```ignore
51/// impl TreeOperations for MyNode {}
52/// ```
53///
54/// # Custom Implementations
55///
56/// You can override any method to provide custom behavior:
57///
58/// ```ignore
59/// impl TreeOperations for MyNode {
60/// fn rename_node(&mut self, id: &Self::Id, new_name: String) -> bool {
61/// // Custom rename logic
62/// // ...
63/// }
64/// // Use default implementations for other methods
65/// }
66/// ```
67pub trait TreeOperations: OutlinerNode + Sized + Clone {
68 /// Finds a node by ID and updates its name.
69 ///
70 /// This method recursively searches the tree starting from this node,
71 /// looking for a node with the specified ID. When found, it updates
72 /// the node's name.
73 ///
74 /// # Arguments
75 ///
76 /// * `id` - The ID of the node to rename
77 /// * `new_name` - The new name for the node
78 ///
79 /// # Returns
80 ///
81 /// `true` if the node was found and renamed, `false` otherwise.
82 ///
83 /// # Examples
84 ///
85 /// ```ignore
86 /// if root.rename_node(&node_id, "New Name".to_string()) {
87 /// println!("Node renamed successfully");
88 /// }
89 /// ```
90 fn rename_node(&mut self, id: &Self::Id, new_name: String) -> bool {
91 // Check if this is the target node
92 if self.id() == *id {
93 // We can't directly modify the name through the trait,
94 // so we need to work with children
95 // This is a limitation - users may need to override this method
96 return false;
97 }
98
99 // Search in children
100 for child in self.children_mut() {
101 if child.rename_node(id, new_name.clone()) {
102 return true;
103 }
104 }
105
106 false
107 }
108
109 /// Removes a node from the tree by ID and returns it.
110 ///
111 /// This method recursively searches the tree starting from this node's children,
112 /// looking for a node with the specified ID. When found, it removes the node
113 /// from its parent's children list and returns it.
114 ///
115 /// # Arguments
116 ///
117 /// * `id` - The ID of the node to remove
118 ///
119 /// # Returns
120 ///
121 /// `Some(node)` if the node was found and removed, `None` otherwise.
122 ///
123 /// # Examples
124 ///
125 /// ```ignore
126 /// if let Some(removed_node) = root.remove_node(&node_id) {
127 /// println!("Removed node: {}", removed_node.name());
128 /// }
129 /// ```
130 fn remove_node(&mut self, id: &Self::Id) -> Option<Self> {
131 let children = self.children_mut();
132
133 // Check direct children first
134 for i in 0..children.len() {
135 if children[i].id() == *id {
136 return Some(children.remove(i));
137 }
138 }
139
140 // Recursively search in children
141 for child in children.iter_mut() {
142 if let Some(node) = child.remove_node(id) {
143 return Some(node);
144 }
145 }
146
147 None
148 }
149
150 /// Inserts a node at a specific position relative to a target node.
151 ///
152 /// This method recursively searches for the target node and inserts the new node
153 /// according to the specified position:
154 /// - **Before**: Insert before the target node (as a sibling)
155 /// - **After**: Insert after the target node (as a sibling)
156 /// - **Inside**: Insert as a child of the target node (only for collections)
157 ///
158 /// # Arguments
159 ///
160 /// * `target_id` - The ID of the target node
161 /// * `node` - The node to insert
162 /// * `position` - Where to insert relative to the target
163 ///
164 /// # Returns
165 ///
166 /// `true` if the node was successfully inserted, `false` otherwise.
167 ///
168 /// # Examples
169 ///
170 /// ```ignore
171 /// let new_node = MyNode::new(42, "New Node");
172 /// if root.insert_node(&target_id, new_node, DropPosition::Inside) {
173 /// println!("Node inserted successfully");
174 /// }
175 /// ```
176 fn insert_node(&mut self, target_id: &Self::Id, node: Self, position: DropPosition) -> bool {
177 // Check if this is the target node
178 if self.id() == *target_id {
179 match position {
180 DropPosition::Inside => {
181 if self.is_collection() {
182 self.children_mut().push(node);
183 return true;
184 }
185 }
186 _ => {
187 // Can't insert before/after at root level without parent context
188 return false;
189 }
190 }
191 }
192
193 // Search in children
194 let children = self.children_mut();
195 for i in 0..children.len() {
196 if children[i].id() == *target_id {
197 match position {
198 DropPosition::Before => {
199 children.insert(i, node);
200 return true;
201 }
202 DropPosition::After => {
203 children.insert(i + 1, node);
204 return true;
205 }
206 DropPosition::Inside => {
207 if children[i].is_collection() {
208 children[i].children_mut().push(node);
209 return true;
210 }
211 }
212 }
213 return false;
214 }
215
216 // Recursively search in this child
217 if children[i].insert_node(target_id, node.clone(), position) {
218 return true;
219 }
220 }
221
222 false
223 }
224
225 /// Finds a node by ID in the tree.
226 ///
227 /// This is a helper method that recursively searches for a node with the given ID.
228 ///
229 /// # Arguments
230 ///
231 /// * `id` - The ID of the node to find
232 ///
233 /// # Returns
234 ///
235 /// A reference to the node if found, `None` otherwise.
236 fn find_node(&self, id: &Self::Id) -> Option<&Self> {
237 if self.id() == *id {
238 return Some(self);
239 }
240
241 for child in self.children() {
242 if let Some(found) = child.find_node(id) {
243 return Some(found);
244 }
245 }
246
247 None
248 }
249
250 /// Finds a node by ID in the tree (mutable version).
251 ///
252 /// This is a helper method that recursively searches for a node with the given ID
253 /// and returns a mutable reference.
254 ///
255 /// # Arguments
256 ///
257 /// * `id` - The ID of the node to find
258 ///
259 /// # Returns
260 ///
261 /// A mutable reference to the node if found, `None` otherwise.
262 fn find_node_mut(&mut self, id: &Self::Id) -> Option<&mut Self> {
263 if self.id() == *id {
264 return Some(self);
265 }
266
267 for child in self.children_mut() {
268 if let Some(found) = child.find_node_mut(id) {
269 return Some(found);
270 }
271 }
272
273 None
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use crate::traits::{ActionIcon, IconType};
281
282 #[derive(Clone, Debug, PartialEq)]
283 struct TestNode {
284 id: u64,
285 name: String,
286 is_collection: bool,
287 children: Vec<TestNode>,
288 }
289
290 impl TestNode {
291 fn new(id: u64, name: &str, is_collection: bool) -> Self {
292 Self {
293 id,
294 name: name.to_string(),
295 is_collection,
296 children: Vec::new(),
297 }
298 }
299
300 fn with_children(mut self, children: Vec<TestNode>) -> Self {
301 self.children = children;
302 self
303 }
304 }
305
306 impl OutlinerNode for TestNode {
307 type Id = u64;
308
309 fn id(&self) -> Self::Id {
310 self.id
311 }
312
313 fn name(&self) -> &str {
314 &self.name
315 }
316
317 fn is_collection(&self) -> bool {
318 self.is_collection
319 }
320
321 fn children(&self) -> &[Self] {
322 &self.children
323 }
324
325 fn children_mut(&mut self) -> &mut Vec<Self> {
326 &mut self.children
327 }
328
329 fn icon(&self) -> Option<IconType> {
330 if self.is_collection {
331 Some(IconType::Collection)
332 } else {
333 Some(IconType::Entity)
334 }
335 }
336
337 fn action_icons(&self) -> Vec<ActionIcon> {
338 vec![]
339 }
340 }
341
342 impl TreeOperations for TestNode {}
343
344 #[test]
345 fn test_remove_node_direct_child() {
346 let mut root = TestNode::new(1, "root", true).with_children(vec![
347 TestNode::new(2, "child1", false),
348 TestNode::new(3, "child2", false),
349 ]);
350
351 let removed = root.remove_node(&2);
352 assert!(removed.is_some());
353 assert_eq!(removed.unwrap().id, 2);
354 assert_eq!(root.children.len(), 1);
355 assert_eq!(root.children[0].id, 3);
356 }
357
358 #[test]
359 fn test_remove_node_nested() {
360 let mut root = TestNode::new(1, "root", true).with_children(vec![
361 TestNode::new(2, "child1", true).with_children(vec![
362 TestNode::new(3, "grandchild", false),
363 ]),
364 ]);
365
366 let removed = root.remove_node(&3);
367 assert!(removed.is_some());
368 assert_eq!(removed.unwrap().id, 3);
369 assert_eq!(root.children[0].children.len(), 0);
370 }
371
372 #[test]
373 fn test_remove_node_not_found() {
374 let mut root = TestNode::new(1, "root", true).with_children(vec![
375 TestNode::new(2, "child1", false),
376 ]);
377
378 let removed = root.remove_node(&999);
379 assert!(removed.is_none());
380 }
381
382 #[test]
383 fn test_insert_node_inside_collection() {
384 let mut root = TestNode::new(1, "root", true);
385 let new_node = TestNode::new(2, "new", false);
386
387 let result = root.insert_node(&1, new_node, DropPosition::Inside);
388 assert!(result);
389 assert_eq!(root.children.len(), 1);
390 assert_eq!(root.children[0].id, 2);
391 }
392
393 #[test]
394 fn test_insert_node_before() {
395 let mut root = TestNode::new(1, "root", true).with_children(vec![
396 TestNode::new(2, "child1", false),
397 TestNode::new(3, "child2", false),
398 ]);
399
400 let new_node = TestNode::new(4, "new", false);
401 let result = root.insert_node(&3, new_node, DropPosition::Before);
402
403 assert!(result);
404 assert_eq!(root.children.len(), 3);
405 assert_eq!(root.children[1].id, 4);
406 assert_eq!(root.children[2].id, 3);
407 }
408
409 #[test]
410 fn test_insert_node_after() {
411 let mut root = TestNode::new(1, "root", true).with_children(vec![
412 TestNode::new(2, "child1", false),
413 TestNode::new(3, "child2", false),
414 ]);
415
416 let new_node = TestNode::new(4, "new", false);
417 let result = root.insert_node(&2, new_node, DropPosition::After);
418
419 assert!(result);
420 assert_eq!(root.children.len(), 3);
421 assert_eq!(root.children[0].id, 2);
422 assert_eq!(root.children[1].id, 4);
423 }
424
425 #[test]
426 fn test_insert_node_inside_non_collection() {
427 let mut root = TestNode::new(1, "root", true).with_children(vec![
428 TestNode::new(2, "child1", false),
429 ]);
430
431 let new_node = TestNode::new(3, "new", false);
432 let result = root.insert_node(&2, new_node, DropPosition::Inside);
433
434 assert!(!result);
435 assert_eq!(root.children.len(), 1);
436 }
437
438 #[test]
439 fn test_find_node() {
440 let root = TestNode::new(1, "root", true).with_children(vec![
441 TestNode::new(2, "child1", true).with_children(vec![
442 TestNode::new(3, "grandchild", false),
443 ]),
444 ]);
445
446 let found = root.find_node(&3);
447 assert!(found.is_some());
448 assert_eq!(found.unwrap().id, 3);
449
450 let not_found = root.find_node(&999);
451 assert!(not_found.is_none());
452 }
453
454 #[test]
455 fn test_find_node_mut() {
456 let mut root = TestNode::new(1, "root", true).with_children(vec![
457 TestNode::new(2, "child1", false),
458 ]);
459
460 let found = root.find_node_mut(&2);
461 assert!(found.is_some());
462
463 if let Some(node) = found {
464 node.name = "modified".to_string();
465 }
466
467 assert_eq!(root.children[0].name, "modified");
468 }
469}