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}