egui_arbor/
traits.rs

1//! Core trait system for egui-arbor outliner nodes and actions.
2//!
3//! This module defines the fundamental traits that users implement to integrate
4//! their data structures with the outliner widget.
5
6use std::hash::Hash;
7
8/// Represents a node in the outliner hierarchy.
9///
10/// Users implement this trait on their own data structures to integrate with
11/// the outliner widget. The trait provides methods for accessing node properties,
12/// hierarchy information, and visual customization.
13///
14/// # Type Parameters
15///
16/// * `Id` - A unique identifier type that must be hashable, comparable, and cloneable.
17///   This is used internally for state management and node tracking.
18///
19/// # Example
20///
21/// ```rust
22/// use egui_arbor::{OutlinerNode, IconType, ActionIcon};
23///
24/// struct SceneNode {
25///     id: u64,
26///     name: String,
27///     children: Vec<SceneNode>,
28/// }
29///
30/// impl OutlinerNode for SceneNode {
31///     type Id = u64;
32///
33///     fn id(&self) -> Self::Id {
34///         self.id
35///     }
36///
37///     fn name(&self) -> &str {
38///         &self.name
39///     }
40///
41///     fn is_collection(&self) -> bool {
42///         !self.children.is_empty()
43///     }
44///
45///     fn children(&self) -> &[Self] {
46///         &self.children
47///     }
48///
49///     fn children_mut(&mut self) -> &mut Vec<Self> {
50///         &mut self.children
51///     }
52/// }
53/// ```
54pub trait OutlinerNode: Sized {
55    /// The type used to uniquely identify nodes.
56    ///
57    /// Must implement [`Hash`], [`Eq`], [`Clone`], [`Send`], [`Sync`], and [`std::fmt::Debug`] for use in internal state management.
58    type Id: Hash + Eq + Clone + Send + Sync + std::fmt::Debug;
59
60    /// Returns the unique identifier for this node.
61    ///
62    /// This ID is used for state tracking, selection management, and drag-drop operations.
63    /// It must be stable across frames and unique within the hierarchy.
64    fn id(&self) -> Self::Id;
65
66    /// Returns the display name of the node.
67    ///
68    /// This is the text shown in the outliner next to the node's icon.
69    fn name(&self) -> &str;
70
71    /// Returns whether this node can contain children.
72    ///
73    /// Collections display an expand/collapse arrow and can have child nodes.
74    /// Non-collection nodes (entities) cannot have children.
75    fn is_collection(&self) -> bool;
76
77    /// Returns an immutable slice of this node's children.
78    ///
79    /// For non-collection nodes, this should return an empty slice.
80    /// For collection nodes, this returns all direct children.
81    fn children(&self) -> &[Self];
82
83    /// Returns a mutable reference to this node's children vector.
84    ///
85    /// This is used for drag-drop operations and hierarchy modifications.
86    /// For non-collection nodes, this should return an empty vector.
87    fn children_mut(&mut self) -> &mut Vec<Self>;
88
89    /// Returns the icon to display next to the node name.
90    ///
91    /// If `None`, no icon is displayed. The default implementation returns `None`.
92    ///
93    /// # Example
94    ///
95    /// ```rust
96    /// # use egui_arbor::{OutlinerNode, IconType};
97    /// # struct MyNode { children: Vec<MyNode> }
98    /// # impl OutlinerNode for MyNode {
99    /// #     type Id = u64;
100    /// #     fn id(&self) -> Self::Id { 0 }
101    /// #     fn name(&self) -> &str { "" }
102    /// #     fn is_collection(&self) -> bool { false }
103    /// #     fn children(&self) -> &[Self] { &self.children }
104    /// #     fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
105    /// fn icon(&self) -> Option<IconType> {
106    ///     if self.is_collection() {
107    ///         Some(IconType::Collection)
108    ///     } else {
109    ///         Some(IconType::Entity)
110    ///     }
111    /// }
112    /// # }
113    /// ```
114    fn icon(&self) -> Option<IconType> {
115        None
116    }
117
118    /// Returns the action icons to display on the right side of the node.
119    ///
120    /// These icons are right-aligned and provide quick access to common operations
121    /// like visibility toggling, locking, and selection.
122    ///
123    /// The default implementation returns the standard set of action icons:
124    /// visibility, lock, and selection.
125    fn action_icons(&self) -> Vec<ActionIcon> {
126        vec![
127            ActionIcon::Visibility,
128            ActionIcon::Lock,
129            ActionIcon::Selection,
130        ]
131    }
132}
133
134/// Handles user interactions and state changes for outliner nodes.
135///
136/// This trait defines callbacks for various outliner operations. Users implement
137/// this trait to respond to user actions like renaming, moving, selecting nodes,
138/// and toggling node states.
139///
140/// # Type Parameters
141///
142/// * `N` - The node type that implements [`OutlinerNode`]
143///
144/// # Example
145///
146/// ```rust
147/// use egui_arbor::{OutlinerNode, OutlinerActions, DropPosition};
148/// use std::collections::HashSet;
149///
150/// # struct SceneNode { id: u64, name: String, children: Vec<SceneNode> }
151/// # impl OutlinerNode for SceneNode {
152/// #     type Id = u64;
153/// #     fn id(&self) -> Self::Id { self.id }
154/// #     fn name(&self) -> &str { &self.name }
155/// #     fn is_collection(&self) -> bool { !self.children.is_empty() }
156/// #     fn children(&self) -> &[Self] { &self.children }
157/// #     fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
158/// # }
159/// struct SceneActions {
160///     selection: HashSet<u64>,
161///     visible: HashSet<u64>,
162///     locked: HashSet<u64>,
163/// }
164///
165/// impl OutlinerActions<SceneNode> for SceneActions {
166///     fn on_rename(&mut self, id: &u64, new_name: String) {
167///         // Update node name in your data structure
168///     }
169///
170///     fn on_move(&mut self, id: &u64, target: &u64, position: DropPosition) {
171///         // Handle node reparenting
172///     }
173///
174///     fn on_select(&mut self, id: &u64, selected: bool) {
175///         if selected {
176///             self.selection.insert(*id);
177///         } else {
178///             self.selection.remove(id);
179///         }
180///     }
181///
182///     fn is_selected(&self, id: &u64) -> bool {
183///         self.selection.contains(id)
184///     }
185///
186///     fn is_visible(&self, id: &u64) -> bool {
187///         self.visible.contains(id)
188///     }
189///
190///     fn is_locked(&self, id: &u64) -> bool {
191///         self.locked.contains(id)
192///     }
193///
194///     fn on_visibility_toggle(&mut self, id: &u64) {
195///         if self.visible.contains(id) {
196///             self.visible.remove(id);
197///         } else {
198///             self.visible.insert(*id);
199///         }
200///     }
201///
202///     fn on_lock_toggle(&mut self, id: &u64) {
203///         if self.locked.contains(id) {
204///             self.locked.remove(id);
205///         } else {
206///             self.locked.insert(*id);
207///         }
208///     }
209///
210///     fn on_selection_toggle(&mut self, id: &u64) {
211///         let is_selected = self.is_selected(id);
212///         self.on_select(id, !is_selected);
213///     }
214///
215///     fn on_custom_action(&mut self, _id: &u64, _icon: &str) {
216///         // Handle custom action
217///     }
218/// }
219/// ```
220pub trait OutlinerActions<N: OutlinerNode> {
221    /// Called when a node is renamed by the user.
222    ///
223    /// This is triggered when the user finishes editing a node's name
224    /// (e.g., by pressing Enter or clicking outside the text field).
225    ///
226    /// # Parameters
227    ///
228    /// * `id` - The unique identifier of the node being renamed
229    /// * `new_name` - The new name entered by the user
230    fn on_rename(&mut self, id: &N::Id, new_name: String);
231
232    /// Called when a node is moved via drag-and-drop.
233    ///
234    /// This is triggered when the user successfully completes a drag-drop operation.
235    /// The implementation should update the hierarchy to reflect the new position.
236    ///
237    /// # Parameters
238    ///
239    /// * `id` - The unique identifier of the node being moved
240    /// * `target` - The unique identifier of the target node
241    /// * `position` - Where to place the node relative to the target
242    fn on_move(&mut self, id: &N::Id, target: &N::Id, position: DropPosition);
243
244    /// Called when a node's selection state changes.
245    ///
246    /// This is triggered when the user clicks on a node or uses keyboard navigation
247    /// to change the selection.
248    ///
249    /// # Parameters
250    ///
251    /// * `id` - The unique identifier of the node
252    /// * `selected` - Whether the node should be selected or deselected
253    fn on_select(&mut self, id: &N::Id, selected: bool);
254
255    /// Returns whether a node is currently selected.
256    ///
257    /// This is used to determine visual highlighting and multi-selection state.
258    fn is_selected(&self, id: &N::Id) -> bool;
259
260    /// Returns whether a node is currently visible.
261    ///
262    /// This affects the state of the visibility action icon. The interpretation
263    /// of "visible" is up to the implementation (e.g., visible in a 3D viewport,
264    /// visible in a list, etc.).
265    fn is_visible(&self, id: &N::Id) -> bool;
266
267    /// Returns whether a node is currently locked.
268    ///
269    /// This affects the state of the lock action icon. The interpretation of
270    /// "locked" is up to the implementation (e.g., locked from editing, locked
271    /// from selection, etc.).
272    fn is_locked(&self, id: &N::Id) -> bool;
273
274    /// Called when the visibility action icon is clicked.
275    ///
276    /// This is triggered when the user clicks the visibility icon (eye icon).
277    /// The implementation should toggle the visibility state of the node.
278    ///
279    /// # Parameters
280    ///
281    /// * `id` - The unique identifier of the node whose visibility is being toggled
282    fn on_visibility_toggle(&mut self, id: &N::Id);
283
284    /// Called when the lock action icon is clicked.
285    ///
286    /// This is triggered when the user clicks the lock icon.
287    /// The implementation should toggle the lock state of the node.
288    ///
289    /// # Parameters
290    ///
291    /// * `id` - The unique identifier of the node whose lock state is being toggled
292    fn on_lock_toggle(&mut self, id: &N::Id);
293
294    /// Called when the selection action icon is clicked.
295    ///
296    /// This is triggered when the user clicks the selection icon (checkbox).
297    /// The implementation should toggle the selection state of the node.
298    ///
299    /// # Parameters
300    ///
301    /// * `id` - The unique identifier of the node whose selection is being toggled
302    fn on_selection_toggle(&mut self, id: &N::Id);
303
304    /// Called when a custom action icon is clicked.
305    ///
306    /// This is triggered when the user clicks a custom action icon.
307    /// The implementation should handle the custom action based on the icon identifier.
308    ///
309    /// # Parameters
310    ///
311    /// * `id` - The unique identifier of the node
312    /// * `icon` - The icon identifier from the custom action icon
313    fn on_custom_action(&mut self, id: &N::Id, icon: &str);
314}
315
316/// The type of icon to display next to a node.
317///
318/// This enum defines the built-in icon types that can be used for nodes.
319/// Custom icons can be added using the `Custom` variant.
320#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub enum IconType {
322    /// Icon for collection nodes (nodes that can contain children)
323    Collection,
324    
325    /// Icon for entity nodes (leaf nodes)
326    Entity,
327    
328    /// Custom icon with a user-defined identifier
329    ///
330    /// The string can be used to look up custom icon rendering logic
331    /// or to specify an icon from an external icon set.
332    Custom(String),
333}
334
335/// Action icons displayed on the right side of each node.
336///
337/// These icons provide quick access to common operations and display
338/// the current state of various node properties.
339#[derive(Debug, Clone, PartialEq, Eq, Hash)]
340pub enum ActionIcon {
341    /// Toggle visibility of the node
342    ///
343    /// Typically displayed as an eye icon. The visual state reflects
344    /// the result of [`OutlinerActions::is_visible`].
345    Visibility,
346    
347    /// Toggle lock state of the node
348    ///
349    /// Typically displayed as a lock/unlock icon. The visual state reflects
350    /// the result of [`OutlinerActions::is_locked`].
351    Lock,
352    
353    /// Toggle selection state of the node
354    ///
355    /// Typically displayed as a checkbox or selection indicator. The visual
356    /// state reflects the result of [`OutlinerActions::is_selected`].
357    Selection,
358    
359    /// Custom action icon with user-defined behavior
360    ///
361    /// # Fields
362    ///
363    /// * `icon` - Identifier for the icon (e.g., emoji, icon name, or custom identifier)
364    /// * `tooltip` - Optional tooltip text to display on hover
365    Custom {
366        /// The icon identifier or character to display
367        icon: String,
368        /// Optional tooltip text
369        tooltip: Option<String>,
370    },
371}
372
373/// Specifies where a node should be placed relative to a target during drag-drop.
374///
375/// This enum is used in [`OutlinerActions::on_move`] to indicate the desired
376/// position of the dragged node relative to the drop target.
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
378pub enum DropPosition {
379    /// Place the node before the target (as a sibling)
380    Before,
381    
382    /// Place the node after the target (as a sibling)
383    After,
384    
385    /// Place the node inside the target (as a child)
386    ///
387    /// This is only valid if the target is a collection node.
388    Inside,
389}