egui_arbor/default_actions.rs
1//! Default implementation of the OutlinerActions trait.
2//!
3//! This module provides [`DefaultActions`], a ready-to-use implementation of the
4//! [`OutlinerActions`] trait that handles common outliner state management including
5//! selection, visibility, lock states, and optional event logging.
6//!
7//! # Examples
8//!
9//! ```
10//! use egui_arbor::{Outliner, OutlinerNode, default_actions::DefaultActions};
11//!
12//! // Create actions handler with logging
13//! let mut actions = DefaultActions::<u64>::with_logging(10);
14//!
15//! // Use with outliner
16//! // let response = Outliner::new("tree").show(ui, &nodes, &mut actions);
17//!
18//! // Access state
19//! assert_eq!(actions.selected_count(), 0);
20//! assert_eq!(actions.visible_count(), 0);
21//! ```
22
23use crate::event_log::{EventLog, EventType};
24use crate::traits::{DropPosition, OutlinerActions, OutlinerNode};
25use std::collections::HashSet;
26use std::hash::Hash;
27
28/// Default implementation of outliner actions with state tracking.
29///
30/// This struct provides a complete, ready-to-use implementation of the
31/// [`OutlinerActions`] trait. It tracks:
32/// - **Selection state**: Which nodes are currently selected
33/// - **Visibility state**: Which nodes are visible/hidden
34/// - **Lock state**: Which nodes are locked
35/// - **Event log**: Optional logging of all interactions
36///
37/// # Type Parameters
38///
39/// * `Id` - The type used to identify nodes. Must implement `Hash`, `Eq`, and `Clone`.
40///
41/// # Examples
42///
43/// ## Basic Usage
44///
45/// ```
46/// use egui_arbor::default_actions::DefaultActions;
47/// use egui_arbor::OutlinerActions;
48///
49/// # struct TestNode { children: Vec<TestNode> }
50/// # impl egui_arbor::OutlinerNode for TestNode {
51/// # type Id = u64;
52/// # fn id(&self) -> Self::Id { 0 }
53/// # fn name(&self) -> &str { "" }
54/// # fn is_collection(&self) -> bool { false }
55/// # fn children(&self) -> &[Self] { &self.children }
56/// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
57/// # }
58/// let mut actions = DefaultActions::<u64>::new();
59///
60/// // All nodes start unselected, visible, and unlocked
61/// assert!(!OutlinerActions::<TestNode>::is_selected(&actions, &1));
62/// assert!(!OutlinerActions::<TestNode>::is_visible(&actions, &1));
63/// assert!(!OutlinerActions::<TestNode>::is_locked(&actions, &1));
64/// ```
65///
66/// ## With Event Logging
67///
68/// ```
69/// use egui_arbor::default_actions::DefaultActions;
70/// use egui_arbor::OutlinerActions;
71///
72/// # struct TestNode { children: Vec<TestNode> }
73/// # impl egui_arbor::OutlinerNode for TestNode {
74/// # type Id = u64;
75/// # fn id(&self) -> Self::Id { 0 }
76/// # fn name(&self) -> &str { "" }
77/// # fn is_collection(&self) -> bool { false }
78/// # fn children(&self) -> &[Self] { &self.children }
79/// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
80/// # }
81/// let mut actions = DefaultActions::<u64>::with_logging(50);
82///
83/// // Events are automatically logged
84/// OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
85/// assert_eq!(actions.event_log().unwrap().len(), 1);
86/// ```
87///
88/// ## Pre-populate State
89///
90/// ```
91/// use egui_arbor::default_actions::DefaultActions;
92/// use egui_arbor::OutlinerActions;
93/// use std::collections::HashSet;
94///
95/// # struct TestNode { children: Vec<TestNode> }
96/// # impl egui_arbor::OutlinerNode for TestNode {
97/// # type Id = u64;
98/// # fn id(&self) -> Self::Id { 0 }
99/// # fn name(&self) -> &str { "" }
100/// # fn is_collection(&self) -> bool { false }
101/// # fn children(&self) -> &[Self] { &self.children }
102/// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
103/// # }
104/// let mut actions = DefaultActions::<u64>::new();
105///
106/// // Make all nodes visible by default
107/// let visible_ids: HashSet<_> = (0..10).collect();
108/// actions.set_all_visible(visible_ids);
109///
110/// assert!(OutlinerActions::<TestNode>::is_visible(&actions, &5));
111/// ```
112#[derive(Clone, Debug)]
113pub struct DefaultActions<Id>
114where
115 Id: Hash + Eq + Clone + std::fmt::Debug,
116{
117 /// Set of selected node IDs.
118 selected: HashSet<Id>,
119
120 /// Set of visible node IDs.
121 visible: HashSet<Id>,
122
123 /// Set of locked node IDs.
124 locked: HashSet<Id>,
125
126 /// Optional event log for tracking interactions.
127 event_log: Option<EventLog<Id>>,
128}
129
130impl<Id> DefaultActions<Id>
131where
132 Id: Hash + Eq + Clone + std::fmt::Debug,
133{
134 /// Creates a new actions handler with no event logging.
135 ///
136 /// All nodes start unselected, invisible, and unlocked.
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use egui_arbor::default_actions::DefaultActions;
142 ///
143 /// let actions = DefaultActions::<u64>::new();
144 /// ```
145 pub fn new() -> Self {
146 Self {
147 selected: HashSet::new(),
148 visible: HashSet::new(),
149 locked: HashSet::new(),
150 event_log: None,
151 }
152 }
153
154 /// Creates a new actions handler with event logging enabled.
155 ///
156 /// # Arguments
157 ///
158 /// * `max_log_entries` - Maximum number of log entries to keep
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use egui_arbor::default_actions::DefaultActions;
164 ///
165 /// let actions = DefaultActions::<u64>::with_logging(100);
166 /// ```
167 pub fn with_logging(max_log_entries: usize) -> Self {
168 Self {
169 selected: HashSet::new(),
170 visible: HashSet::new(),
171 locked: HashSet::new(),
172 event_log: Some(EventLog::new(max_log_entries)),
173 }
174 }
175
176 /// Returns a reference to the event log, if logging is enabled.
177 ///
178 /// # Examples
179 ///
180 /// ```
181 /// use egui_arbor::default_actions::DefaultActions;
182 ///
183 /// let actions = DefaultActions::<u64>::with_logging(10);
184 /// assert!(actions.event_log().is_some());
185 ///
186 /// let actions = DefaultActions::<u64>::new();
187 /// assert!(actions.event_log().is_none());
188 /// ```
189 pub fn event_log(&self) -> Option<&EventLog<Id>> {
190 self.event_log.as_ref()
191 }
192
193 /// Returns a mutable reference to the event log, if logging is enabled.
194 ///
195 /// # Examples
196 ///
197 /// ```
198 /// use egui_arbor::default_actions::DefaultActions;
199 ///
200 /// let mut actions = DefaultActions::<u64>::with_logging(10);
201 /// if let Some(log) = actions.event_log_mut() {
202 /// log.clear();
203 /// }
204 /// ```
205 pub fn event_log_mut(&mut self) -> Option<&mut EventLog<Id>> {
206 self.event_log.as_mut()
207 }
208
209 /// Returns the number of selected nodes.
210 ///
211 /// # Examples
212 ///
213 /// ```
214 /// use egui_arbor::default_actions::DefaultActions;
215 /// use egui_arbor::OutlinerActions;
216 ///
217 /// # struct TestNode { children: Vec<TestNode> }
218 /// # impl egui_arbor::OutlinerNode for TestNode {
219 /// # type Id = u64;
220 /// # fn id(&self) -> Self::Id { 0 }
221 /// # fn name(&self) -> &str { "" }
222 /// # fn is_collection(&self) -> bool { false }
223 /// # fn children(&self) -> &[Self] { &self.children }
224 /// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
225 /// # }
226 /// let mut actions = DefaultActions::<u64>::new();
227 /// OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
228 /// OutlinerActions::<TestNode>::on_select(&mut actions, &2, true);
229 /// assert_eq!(actions.selected_count(), 2);
230 /// ```
231 pub fn selected_count(&self) -> usize {
232 self.selected.len()
233 }
234
235 /// Returns the number of visible nodes.
236 ///
237 /// # Examples
238 ///
239 /// ```
240 /// use egui_arbor::default_actions::DefaultActions;
241 /// use egui_arbor::OutlinerActions;
242 ///
243 /// # struct TestNode { children: Vec<TestNode> }
244 /// # impl egui_arbor::OutlinerNode for TestNode {
245 /// # type Id = u64;
246 /// # fn id(&self) -> Self::Id { 0 }
247 /// # fn name(&self) -> &str { "" }
248 /// # fn is_collection(&self) -> bool { false }
249 /// # fn children(&self) -> &[Self] { &self.children }
250 /// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
251 /// # }
252 /// let mut actions = DefaultActions::<u64>::new();
253 /// OutlinerActions::<TestNode>::on_visibility_toggle(&mut actions, &1);
254 /// assert_eq!(actions.visible_count(), 1);
255 /// ```
256 pub fn visible_count(&self) -> usize {
257 self.visible.len()
258 }
259
260 /// Returns the number of locked nodes.
261 ///
262 /// # Examples
263 ///
264 /// ```
265 /// use egui_arbor::default_actions::DefaultActions;
266 /// use egui_arbor::OutlinerActions;
267 ///
268 /// # struct TestNode { children: Vec<TestNode> }
269 /// # impl egui_arbor::OutlinerNode for TestNode {
270 /// # type Id = u64;
271 /// # fn id(&self) -> Self::Id { 0 }
272 /// # fn name(&self) -> &str { "" }
273 /// # fn is_collection(&self) -> bool { false }
274 /// # fn children(&self) -> &[Self] { &self.children }
275 /// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
276 /// # }
277 /// let mut actions = DefaultActions::<u64>::new();
278 /// OutlinerActions::<TestNode>::on_lock_toggle(&mut actions, &1);
279 /// assert_eq!(actions.locked_count(), 1);
280 /// ```
281 pub fn locked_count(&self) -> usize {
282 self.locked.len()
283 }
284
285 /// Returns a reference to the set of selected node IDs.
286 ///
287 /// # Examples
288 ///
289 /// ```
290 /// use egui_arbor::default_actions::DefaultActions;
291 /// use egui_arbor::OutlinerActions;
292 ///
293 /// # struct TestNode { children: Vec<TestNode> }
294 /// # impl egui_arbor::OutlinerNode for TestNode {
295 /// # type Id = u64;
296 /// # fn id(&self) -> Self::Id { 0 }
297 /// # fn name(&self) -> &str { "" }
298 /// # fn is_collection(&self) -> bool { false }
299 /// # fn children(&self) -> &[Self] { &self.children }
300 /// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
301 /// # }
302 /// let mut actions = DefaultActions::<u64>::new();
303 /// OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
304 /// OutlinerActions::<TestNode>::on_select(&mut actions, &2, true);
305 ///
306 /// assert!(actions.selected().contains(&1));
307 /// assert!(actions.selected().contains(&2));
308 /// ```
309 pub fn selected(&self) -> &HashSet<Id> {
310 &self.selected
311 }
312
313 /// Returns a reference to the set of visible node IDs.
314 pub fn visible(&self) -> &HashSet<Id> {
315 &self.visible
316 }
317
318 /// Returns a reference to the set of locked node IDs.
319 pub fn locked(&self) -> &HashSet<Id> {
320 &self.locked
321 }
322
323 /// Sets all nodes as visible.
324 ///
325 /// # Arguments
326 ///
327 /// * `ids` - Set of node IDs to mark as visible
328 ///
329 /// # Examples
330 ///
331 /// ```
332 /// use egui_arbor::default_actions::DefaultActions;
333 /// use egui_arbor::OutlinerActions;
334 /// use std::collections::HashSet;
335 ///
336 /// # struct TestNode { children: Vec<TestNode> }
337 /// # impl egui_arbor::OutlinerNode for TestNode {
338 /// # type Id = u64;
339 /// # fn id(&self) -> Self::Id { 0 }
340 /// # fn name(&self) -> &str { "" }
341 /// # fn is_collection(&self) -> bool { false }
342 /// # fn children(&self) -> &[Self] { &self.children }
343 /// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
344 /// # }
345 /// let mut actions = DefaultActions::<u64>::new();
346 /// let visible: HashSet<_> = (0..10).collect();
347 /// actions.set_all_visible(visible);
348 ///
349 /// assert!(OutlinerActions::<TestNode>::is_visible(&actions, &5));
350 /// ```
351 pub fn set_all_visible(&mut self, ids: HashSet<Id>) {
352 self.visible = ids;
353 }
354
355 /// Clears all selections.
356 ///
357 /// # Examples
358 ///
359 /// ```
360 /// use egui_arbor::default_actions::DefaultActions;
361 /// use egui_arbor::OutlinerActions;
362 ///
363 /// # struct TestNode { children: Vec<TestNode> }
364 /// # impl egui_arbor::OutlinerNode for TestNode {
365 /// # type Id = u64;
366 /// # fn id(&self) -> Self::Id { 0 }
367 /// # fn name(&self) -> &str { "" }
368 /// # fn is_collection(&self) -> bool { false }
369 /// # fn children(&self) -> &[Self] { &self.children }
370 /// # fn children_mut(&mut self) -> &mut Vec<Self> { &mut self.children }
371 /// # }
372 /// let mut actions = DefaultActions::<u64>::new();
373 /// OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
374 /// OutlinerActions::<TestNode>::on_select(&mut actions, &2, true);
375 /// assert_eq!(actions.selected_count(), 2);
376 ///
377 /// actions.clear_selection();
378 /// assert_eq!(actions.selected_count(), 0);
379 /// ```
380 pub fn clear_selection(&mut self) {
381 self.selected.clear();
382 }
383
384 /// Logs an event if logging is enabled.
385 fn log_event(&mut self, message: String, event_type: EventType, node_id: Option<Id>) {
386 if let Some(log) = &mut self.event_log {
387 log.log(message, event_type, node_id);
388 }
389 }
390}
391
392impl<Id> Default for DefaultActions<Id>
393where
394 Id: Hash + Eq + Clone + std::fmt::Debug,
395{
396 fn default() -> Self {
397 Self::new()
398 }
399}
400
401impl<Id, N> OutlinerActions<N> for DefaultActions<Id>
402where
403 Id: Hash + Eq + Clone + std::fmt::Debug,
404 N: OutlinerNode<Id = Id>,
405{
406 fn on_rename(&mut self, id: &Id, new_name: String) {
407 self.log_event(
408 format!("Renamed node {:?} to '{}'", id, new_name),
409 EventType::Rename,
410 Some(id.clone()),
411 );
412 }
413
414 fn on_move(&mut self, id: &Id, target: &Id, position: DropPosition) {
415 self.log_event(
416 format!("Move: node {:?} → target {:?} ({:?})", id, target, position),
417 EventType::DragDrop,
418 Some(id.clone()),
419 );
420 }
421
422 fn on_select(&mut self, id: &Id, selected: bool) {
423 if selected {
424 self.selected.insert(id.clone());
425 self.log_event(
426 format!("Selected node {:?}", id),
427 EventType::Selection,
428 Some(id.clone()),
429 );
430 } else {
431 self.selected.remove(id);
432 self.log_event(
433 format!("Deselected node {:?}", id),
434 EventType::Selection,
435 Some(id.clone()),
436 );
437 }
438 }
439
440 fn is_selected(&self, id: &Id) -> bool {
441 self.selected.contains(id)
442 }
443
444 fn is_visible(&self, id: &Id) -> bool {
445 self.visible.contains(id)
446 }
447
448 fn is_locked(&self, id: &Id) -> bool {
449 self.locked.contains(id)
450 }
451
452 fn on_visibility_toggle(&mut self, id: &Id) {
453 let was_visible = self.visible.contains(id);
454 if was_visible {
455 self.visible.remove(id);
456 self.log_event(
457 format!("Hidden node {:?}", id),
458 EventType::Visibility,
459 Some(id.clone()),
460 );
461 } else {
462 self.visible.insert(id.clone());
463 self.log_event(
464 format!("Shown node {:?}", id),
465 EventType::Visibility,
466 Some(id.clone()),
467 );
468 }
469 }
470
471 fn on_lock_toggle(&mut self, id: &Id) {
472 let was_locked = self.locked.contains(id);
473 if was_locked {
474 self.locked.remove(id);
475 self.log_event(
476 format!("Unlocked node {:?}", id),
477 EventType::Lock,
478 Some(id.clone()),
479 );
480 } else {
481 self.locked.insert(id.clone());
482 self.log_event(
483 format!("Locked node {:?}", id),
484 EventType::Lock,
485 Some(id.clone()),
486 );
487 }
488 }
489
490 fn on_selection_toggle(&mut self, id: &Id) {
491 let is_selected = OutlinerActions::<N>::is_selected(self, id);
492 OutlinerActions::<N>::on_select(self, id, !is_selected);
493 }
494
495 fn on_custom_action(&mut self, id: &Id, icon: &str) {
496 self.log_event(
497 format!("Custom action '{}' on node {:?}", icon, id),
498 EventType::Custom(icon.to_string()),
499 Some(id.clone()),
500 );
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use super::*;
507 use crate::traits::{ActionIcon, IconType};
508
509 #[derive(Clone, Debug)]
510 struct TestNode {
511 id: u64,
512 name: String,
513 children: Vec<TestNode>,
514 }
515
516 impl OutlinerNode for TestNode {
517 type Id = u64;
518
519 fn id(&self) -> Self::Id {
520 self.id
521 }
522
523 fn name(&self) -> &str {
524 &self.name
525 }
526
527 fn is_collection(&self) -> bool {
528 !self.children.is_empty()
529 }
530
531 fn children(&self) -> &[Self] {
532 &self.children
533 }
534
535 fn children_mut(&mut self) -> &mut Vec<Self> {
536 &mut self.children
537 }
538
539 fn icon(&self) -> Option<IconType> {
540 None
541 }
542
543 fn action_icons(&self) -> Vec<ActionIcon> {
544 vec![]
545 }
546 }
547
548 #[test]
549 fn test_new() {
550 let actions = DefaultActions::<u64>::new();
551 assert_eq!(actions.selected_count(), 0);
552 assert_eq!(actions.visible_count(), 0);
553 assert_eq!(actions.locked_count(), 0);
554 assert!(actions.event_log().is_none());
555 }
556
557 #[test]
558 fn test_with_logging() {
559 let actions = DefaultActions::<u64>::with_logging(10);
560 assert!(actions.event_log().is_some());
561 assert_eq!(actions.event_log().unwrap().max_entries(), 10);
562 }
563
564 #[test]
565 fn test_selection() {
566 let mut actions = DefaultActions::<u64>::new();
567
568 assert!(!OutlinerActions::<TestNode>::is_selected(&actions, &1));
569
570 OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
571 assert!(OutlinerActions::<TestNode>::is_selected(&actions, &1));
572 assert_eq!(actions.selected_count(), 1);
573
574 OutlinerActions::<TestNode>::on_select(&mut actions, &2, true);
575 assert_eq!(actions.selected_count(), 2);
576
577 OutlinerActions::<TestNode>::on_select(&mut actions, &1, false);
578 assert!(!OutlinerActions::<TestNode>::is_selected(&actions, &1));
579 assert_eq!(actions.selected_count(), 1);
580 }
581
582 #[test]
583 fn test_visibility() {
584 let mut actions = DefaultActions::<u64>::new();
585
586 assert!(!OutlinerActions::<TestNode>::is_visible(&actions, &1));
587
588 OutlinerActions::<TestNode>::on_visibility_toggle(&mut actions, &1);
589 assert!(OutlinerActions::<TestNode>::is_visible(&actions, &1));
590 assert_eq!(actions.visible_count(), 1);
591
592 OutlinerActions::<TestNode>::on_visibility_toggle(&mut actions, &1);
593 assert!(!OutlinerActions::<TestNode>::is_visible(&actions, &1));
594 assert_eq!(actions.visible_count(), 0);
595 }
596
597 #[test]
598 fn test_lock() {
599 let mut actions = DefaultActions::<u64>::new();
600
601 assert!(!OutlinerActions::<TestNode>::is_locked(&actions, &1));
602
603 OutlinerActions::<TestNode>::on_lock_toggle(&mut actions, &1);
604 assert!(OutlinerActions::<TestNode>::is_locked(&actions, &1));
605 assert_eq!(actions.locked_count(), 1);
606
607 OutlinerActions::<TestNode>::on_lock_toggle(&mut actions, &1);
608 assert!(!OutlinerActions::<TestNode>::is_locked(&actions, &1));
609 assert_eq!(actions.locked_count(), 0);
610 }
611
612 #[test]
613 fn test_selection_toggle() {
614 let mut actions = DefaultActions::<u64>::new();
615
616 OutlinerActions::<TestNode>::on_selection_toggle(&mut actions, &1);
617 assert!(OutlinerActions::<TestNode>::is_selected(&actions, &1));
618
619 OutlinerActions::<TestNode>::on_selection_toggle(&mut actions, &1);
620 assert!(!OutlinerActions::<TestNode>::is_selected(&actions, &1));
621 }
622
623 #[test]
624 fn test_clear_selection() {
625 let mut actions = DefaultActions::<u64>::new();
626
627 OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
628 OutlinerActions::<TestNode>::on_select(&mut actions, &2, true);
629 OutlinerActions::<TestNode>::on_select(&mut actions, &3, true);
630 assert_eq!(actions.selected_count(), 3);
631
632 actions.clear_selection();
633 assert_eq!(actions.selected_count(), 0);
634 }
635
636 #[test]
637 fn test_set_all_visible() {
638 let mut actions = DefaultActions::<u64>::new();
639
640 let visible: HashSet<_> = (0..5).collect();
641 actions.set_all_visible(visible);
642
643 assert_eq!(actions.visible_count(), 5);
644 assert!(OutlinerActions::<TestNode>::is_visible(&actions, &0));
645 assert!(OutlinerActions::<TestNode>::is_visible(&actions, &4));
646 assert!(!OutlinerActions::<TestNode>::is_visible(&actions, &5));
647 }
648
649 #[test]
650 fn test_event_logging() {
651 let mut actions = DefaultActions::<u64>::with_logging(10);
652
653 OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
654 OutlinerActions::<TestNode>::on_visibility_toggle(&mut actions, &2);
655 OutlinerActions::<TestNode>::on_lock_toggle(&mut actions, &3);
656
657 let log = actions.event_log().unwrap();
658 assert_eq!(log.len(), 3);
659
660 let entries: Vec<_> = log.entries().collect();
661 assert_eq!(entries[0].event_type, EventType::Lock);
662 assert_eq!(entries[1].event_type, EventType::Visibility);
663 assert_eq!(entries[2].event_type, EventType::Selection);
664 }
665
666 #[test]
667 fn test_on_rename_logging() {
668 let mut actions = DefaultActions::<u64>::with_logging(10);
669
670 OutlinerActions::<TestNode>::on_rename(&mut actions, &1, "New Name".to_string());
671
672 let log = actions.event_log().unwrap();
673 assert_eq!(log.len(), 1);
674 let entries: Vec<_> = log.entries().collect();
675 assert_eq!(entries[0].event_type, EventType::Rename);
676 }
677
678 #[test]
679 fn test_on_move_logging() {
680 let mut actions = DefaultActions::<u64>::with_logging(10);
681
682 OutlinerActions::<TestNode>::on_move(&mut actions, &1, &2, DropPosition::Inside);
683
684 let log = actions.event_log().unwrap();
685 assert_eq!(log.len(), 1);
686 let entries: Vec<_> = log.entries().collect();
687 assert_eq!(entries[0].event_type, EventType::DragDrop);
688 }
689
690 #[test]
691 fn test_on_custom_action() {
692 let mut actions = DefaultActions::<u64>::with_logging(10);
693
694 OutlinerActions::<TestNode>::on_custom_action(&mut actions, &1, "custom_icon");
695
696 let log = actions.event_log().unwrap();
697 assert_eq!(log.len(), 1);
698 let entries: Vec<_> = log.entries().collect();
699 assert!(matches!(entries[0].event_type, EventType::Custom(_)));
700 }
701
702 #[test]
703 fn test_default() {
704 let actions = DefaultActions::<u64>::default();
705 assert_eq!(actions.selected_count(), 0);
706 assert!(actions.event_log().is_none());
707 }
708
709 #[test]
710 fn test_selected_reference() {
711 let mut actions = DefaultActions::<u64>::new();
712 OutlinerActions::<TestNode>::on_select(&mut actions, &1, true);
713 OutlinerActions::<TestNode>::on_select(&mut actions, &2, true);
714
715 let selected = actions.selected();
716 assert_eq!(selected.len(), 2);
717 assert!(selected.contains(&1));
718 assert!(selected.contains(&2));
719 }
720}