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}