fyrox_ui/tree.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Tree widget allows you to create views for hierarchical data. See [`Tree`] docs for more info
22//! and usage examples.
23
24#![warn(missing_docs)]
25
26use crate::{
27 border::BorderBuilder,
28 brush::Brush,
29 check_box::{CheckBoxBuilder, CheckBoxMessage},
30 core::{
31 algebra::Vector2, color::Color, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
32 visitor::prelude::*,
33 },
34 decorator::{DecoratorBuilder, DecoratorMessage},
35 define_constructor,
36 grid::{Column, GridBuilder, Row},
37 message::KeyCode,
38 message::{MessageDirection, UiMessage},
39 stack_panel::StackPanelBuilder,
40 style::resource::StyleResourceExt,
41 style::Style,
42 utils::{make_arrow, ArrowDirection},
43 widget::{Widget, WidgetBuilder, WidgetMessage},
44 BuildContext, Control, MouseButton, Thickness, UiNode, UserInterface, VerticalAlignment,
45};
46use fyrox_core::uuid_provider;
47use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
48use fyrox_graph::{BaseSceneGraph, SceneGraph, SceneGraphNode};
49use std::collections::VecDeque;
50use std::ops::{Deref, DerefMut};
51
52/// Opaque selection state of a tree.
53#[derive(Debug, Copy, Clone, PartialEq, Eq)]
54pub struct SelectionState(pub(crate) bool);
55
56/// Expansion strategy for a hierarchical structure.
57#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug)]
58pub enum TreeExpansionStrategy {
59 /// Expand a single item.
60 Direct,
61 /// Expand an item and its descendants.
62 RecursiveDescendants,
63 /// Expand an item and its ancestors (chain of parent trees).
64 RecursiveAncestors,
65}
66
67/// A set of messages, that could be used to alternate the state of a [`Tree`] widget.
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub enum TreeMessage {
70 /// A message, that is used to expand a tree. Exact expansion behavior depends on the expansion
71 /// strategy (see [`TreeExpansionStrategy`] docs for more info).
72 Expand {
73 /// Expand (`true`) or collapse (`false`) a tree.
74 expand: bool,
75 /// Expansion strategy.
76 expansion_strategy: TreeExpansionStrategy,
77 },
78 /// A message, that is used to add an item to a tree.
79 AddItem(Handle<UiNode>),
80 /// A message, that is used to remove an item from a tree.
81 RemoveItem(Handle<UiNode>),
82 /// A message, that is used to prevent expander from being hidden when a tree does not have
83 /// any child items.
84 SetExpanderShown(bool),
85 /// A message, that is used to specify a new set of children items of a tree.
86 SetItems {
87 /// A set of handles to new tree items.
88 items: Vec<Handle<UiNode>>,
89 /// A flag, that defines whether the previous items should be deleted or not. `false` is
90 /// usually used to reorder existing items.
91 remove_previous: bool,
92 },
93 // Private, do not use. For internal needs only. Use TreeRootMessage::Selected.
94 #[doc(hidden)]
95 Select(SelectionState),
96}
97
98impl TreeMessage {
99 define_constructor!(
100 /// Creates [`TreeMessage::Expand`] message.
101 TreeMessage:Expand => fn expand(expand: bool, expansion_strategy: TreeExpansionStrategy), layout: false
102 );
103 define_constructor!(
104 /// Creates [`TreeMessage::AddItem`] message.
105 TreeMessage:AddItem => fn add_item(Handle<UiNode>), layout: false
106 );
107 define_constructor!(
108 /// Creates [`TreeMessage::RemoveItem`] message.
109 TreeMessage:RemoveItem => fn remove_item(Handle<UiNode>), layout: false
110 );
111 define_constructor!(
112 /// Creates [`TreeMessage::SetExpanderShown`] message.
113 TreeMessage:SetExpanderShown => fn set_expander_shown(bool), layout: false
114 );
115 define_constructor!(
116 /// Creates [`TreeMessage::SetItems`] message.
117 TreeMessage:SetItems => fn set_items(items: Vec<Handle<UiNode>>, remove_previous: bool), layout: false
118 );
119 define_constructor!(
120 /// Creates [`TreeMessage::Select`] message.
121 TreeMessage:Select => fn select(SelectionState), layout: false
122 );
123}
124
125/// A set of messages, that could be used to alternate the state of a [`TreeRoot`] widget.
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub enum TreeRootMessage {
128 /// A message, that is used to add a child item to a tree root.
129 AddItem(Handle<UiNode>),
130 /// A message, that is used to remove a child item from a tree root.
131 RemoveItem(Handle<UiNode>),
132 /// A message, that is used to specify a new set of children items of a tree root.
133 Items(Vec<Handle<UiNode>>),
134 /// A message, that it is used to fetch or set current selection of a tree root.
135 Selected(Vec<Handle<UiNode>>),
136 /// A message, that is used to expand all descendant trees in the hierarchy.
137 ExpandAll,
138 /// A message, that is used to collapse all descendant trees in the hierarchy.
139 CollapseAll,
140 /// A message, that is used as a notification when tree root's items has changed.
141 ItemsChanged,
142}
143
144impl TreeRootMessage {
145 define_constructor!(
146 /// Creates [`TreeRootMessage::AddItem`] message.
147 TreeRootMessage:AddItem => fn add_item(Handle<UiNode>), layout: false
148 );
149 define_constructor!(
150 /// Creates [`TreeRootMessage::RemoveItem`] message.
151 TreeRootMessage:RemoveItem=> fn remove_item(Handle<UiNode>), layout: false
152 );
153 define_constructor!(
154 /// Creates [`TreeRootMessage::Items`] message.
155 TreeRootMessage:Items => fn items(Vec<Handle<UiNode >>), layout: false
156 );
157 define_constructor!(
158 /// Creates [`TreeRootMessage::Selected`] message.
159 TreeRootMessage:Selected => fn select(Vec<Handle<UiNode >>), layout: false
160 );
161 define_constructor!(
162 /// Creates [`TreeRootMessage::ExpandAll`] message.
163 TreeRootMessage:ExpandAll => fn expand_all(), layout: false
164 );
165 define_constructor!(
166 /// Creates [`TreeRootMessage::CollapseAll`] message.
167 TreeRootMessage:CollapseAll => fn collapse_all(), layout: false
168 );
169 define_constructor!(
170 /// Creates [`TreeRootMessage::ItemsChanged`] message.
171 TreeRootMessage:ItemsChanged => fn items_changed(), layout: false
172 );
173}
174
175/// Tree widget allows you to create views for hierarchical data. It could be used to show file
176/// system entries, graphs, and anything else that could be represented as a tree.
177///
178/// ## Examples
179///
180/// A simple tree with one root and two children items could be created like so:
181///
182/// ```rust
183/// # use fyrox_ui::{
184/// # core::pool::Handle,
185/// # text::TextBuilder,
186/// # tree::{TreeBuilder, TreeRootBuilder},
187/// # widget::WidgetBuilder,
188/// # BuildContext, UiNode,
189/// # };
190/// #
191/// fn create_tree(ctx: &mut BuildContext) -> Handle<UiNode> {
192/// // Note, that `TreeRoot` widget is mandatory here. Otherwise some functionality of
193/// // descendant trees won't work.
194/// TreeRootBuilder::new(WidgetBuilder::new())
195/// .with_items(vec![TreeBuilder::new(WidgetBuilder::new())
196/// .with_content(
197/// TextBuilder::new(WidgetBuilder::new())
198/// .with_text("Root Item 0")
199/// .build(ctx),
200/// )
201/// .with_items(vec![
202/// TreeBuilder::new(WidgetBuilder::new())
203/// .with_content(
204/// TextBuilder::new(WidgetBuilder::new())
205/// .with_text("Child Item 0")
206/// .build(ctx),
207/// )
208/// .build(ctx),
209/// TreeBuilder::new(WidgetBuilder::new())
210/// .with_content(
211/// TextBuilder::new(WidgetBuilder::new())
212/// .with_text("Child Item 1")
213/// .build(ctx),
214/// )
215/// .build(ctx),
216/// ])
217/// .build(ctx)])
218/// .build(ctx)
219/// }
220/// ```
221///
222/// Note, that `TreeRoot` widget is mandatory here. Otherwise, some functionality of descendant trees
223/// won't work (primarily - selection). See [`TreeRoot`] docs for more detailed explanation.
224///
225/// ## Built-in controls
226///
227/// Tree widget is a rich control element, which has its own set of controls:
228///
229/// `Any Mouse Button` - select.
230/// `Ctrl+Click` - enables multi-selection.
231/// `Alt+Click` - prevents selection allowing you to use drag'n'drop.
232/// `Shift+Click` - selects a span of items.
233/// `ArrowUp` - navigate up from the topmost selection.
234/// `ArrowDown` - navigate down from the lowermost selection.
235/// `ArrowRight` - expand the selected item (first from the selection) or (if it is expanded), go
236/// down the tree.
237/// `ArrowLeft` - collapse the selected item or (if it is collapsed), go up the tree.
238///
239/// ## Adding Items
240///
241/// An item could be added to a tree using [`TreeMessage::AddItem`] message like so:
242///
243/// ```rust
244/// # use fyrox_ui::{
245/// # core::pool::Handle,
246/// # message::MessageDirection,
247/// # text::TextBuilder,
248/// # tree::{TreeBuilder, TreeMessage},
249/// # widget::WidgetBuilder,
250/// # UiNode, UserInterface,
251/// # };
252/// #
253/// fn add_item(tree: Handle<UiNode>, ui: &mut UserInterface) {
254/// let ctx = &mut ui.build_ctx();
255///
256/// let item = TreeBuilder::new(WidgetBuilder::new())
257/// .with_content(
258/// TextBuilder::new(WidgetBuilder::new())
259/// .with_text("Some New Item")
260/// .build(ctx),
261/// )
262/// .build(ctx);
263///
264/// ui.send_message(TreeMessage::add_item(
265/// tree,
266/// MessageDirection::ToWidget,
267/// item,
268/// ));
269/// }
270/// ```
271///
272/// ## Removing Items
273///
274/// An item could be removed from a tree using [`TreeMessage::RemoveItem`] message like so:
275///
276/// ```rust
277/// # use fyrox_ui::{
278/// # core::pool::Handle, message::MessageDirection, tree::TreeMessage, UiNode, UserInterface,
279/// # };
280/// #
281/// fn remove_item(tree: Handle<UiNode>, item_to_remove: Handle<UiNode>, ui: &UserInterface) {
282/// // Note that the `ui` is borrowed as immutable here, which means that the item will **not**
283/// // be removed immediately, but on the next update call.
284/// ui.send_message(TreeMessage::remove_item(
285/// tree,
286/// MessageDirection::ToWidget,
287/// item_to_remove,
288/// ));
289/// }
290/// ```
291///
292/// ## Setting New Items
293///
294/// Tree's items could be changed all at once using the [`TreeMessage::SetItems`] message like so:
295///
296/// ```rust
297/// # use fyrox_ui::{
298/// # core::pool::Handle,
299/// # message::MessageDirection,
300/// # text::TextBuilder,
301/// # tree::{TreeBuilder, TreeMessage},
302/// # widget::WidgetBuilder,
303/// # UiNode, UserInterface,
304/// # };
305/// #
306/// fn set_items(tree: Handle<UiNode>, ui: &mut UserInterface) {
307/// let ctx = &mut ui.build_ctx();
308///
309/// let items = vec![
310/// TreeBuilder::new(WidgetBuilder::new())
311/// .with_content(
312/// TextBuilder::new(WidgetBuilder::new())
313/// .with_text("Item 0")
314/// .build(ctx),
315/// )
316/// .build(ctx),
317/// TreeBuilder::new(WidgetBuilder::new())
318/// .with_content(
319/// TextBuilder::new(WidgetBuilder::new())
320/// .with_text("Item 1")
321/// .build(ctx),
322/// )
323/// .build(ctx),
324/// ];
325///
326/// // A flag, that tells that the UI system must destroy previous items first.
327/// let remove_previous = true;
328/// ui.send_message(TreeMessage::set_items(
329/// tree,
330/// MessageDirection::ToWidget,
331/// items,
332/// remove_previous,
333/// ));
334/// }
335/// ```
336///
337/// ## Expanding Items
338///
339/// It is possible to expand/collapse trees at runtime using [`TreeMessage::Expand`] message. It provides
340/// different expansion strategies, see docs for [`TreeExpansionStrategy`] for more info. Tree expansion
341/// could useful to highlight something visually.
342///
343/// ```rust
344/// # use fyrox_ui::{
345/// # core::pool::Handle,
346/// # message::MessageDirection,
347/// # tree::{TreeExpansionStrategy, TreeMessage},
348/// # UiNode, UserInterface,
349/// # };
350/// #
351/// fn expand_tree(tree: Handle<UiNode>, ui: &UserInterface) {
352/// ui.send_message(TreeMessage::expand(
353/// tree,
354/// MessageDirection::ToWidget,
355/// true,
356/// TreeExpansionStrategy::RecursiveAncestors,
357/// ));
358/// }
359/// ```
360#[derive(Default, Debug, Clone, Visit, Reflect, ComponentProvider)]
361pub struct Tree {
362 /// Base widget of the tree.
363 pub widget: Widget,
364 /// Current expander of the tree. Usually, it is just a handle of CheckBox widget.
365 pub expander: Handle<UiNode>,
366 /// Current content of the tree.
367 pub content: Handle<UiNode>,
368 /// Current layout panel, that used to arrange children items.
369 pub panel: Handle<UiNode>,
370 /// A flag, that indicates whether the tree is expanded or not.
371 pub is_expanded: bool,
372 /// Current background widget of the tree.
373 pub background: Handle<UiNode>,
374 /// Current set of items of the tree.
375 pub items: Vec<Handle<UiNode>>,
376 /// A flag, that defines whether the tree is selected or not.
377 pub is_selected: bool,
378 /// A flag, that defines whether the tree should always show its expander, even if there's no
379 /// children elements, or not.
380 pub always_show_expander: bool,
381}
382
383impl ConstructorProvider<UiNode, UserInterface> for Tree {
384 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
385 GraphNodeConstructor::new::<Self>()
386 .with_variant("Tree", |ui| {
387 TreeBuilder::new(WidgetBuilder::new().with_name("Tree"))
388 .build(&mut ui.build_ctx())
389 .into()
390 })
391 .with_group("Visual")
392 }
393}
394
395crate::define_widget_deref!(Tree);
396
397uuid_provider!(Tree = "e090e913-393a-4192-a220-e1d87e272170");
398
399impl Control for Tree {
400 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
401 let size = self.widget.arrange_override(ui, final_size);
402
403 let expander_visibility = !self.items.is_empty() || self.always_show_expander;
404 ui.send_message(WidgetMessage::visibility(
405 self.expander,
406 MessageDirection::ToWidget,
407 expander_visibility,
408 ));
409
410 size
411 }
412
413 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
414 self.widget.handle_routed_message(ui, message);
415
416 if let Some(CheckBoxMessage::Check(Some(expanded))) = message.data() {
417 if message.destination() == self.expander
418 && message.direction == MessageDirection::FromWidget
419 {
420 ui.send_message(TreeMessage::expand(
421 self.handle(),
422 MessageDirection::ToWidget,
423 *expanded,
424 TreeExpansionStrategy::Direct,
425 ));
426 }
427 } else if let Some(msg) = message.data::<WidgetMessage>() {
428 if !message.handled() {
429 match msg {
430 WidgetMessage::MouseDown { .. } => {
431 let keyboard_modifiers = ui.keyboard_modifiers();
432 // Prevent selection changes by Alt+Click to be able to drag'n'drop tree items.
433 if !keyboard_modifiers.alt {
434 if let Some((tree_root_handle, tree_root)) =
435 ui.find_component_up::<TreeRoot>(self.parent())
436 {
437 let selection = if keyboard_modifiers.control {
438 let mut selection = tree_root.selected.clone();
439 if let Some(existing) =
440 selection.iter().position(|&h| h == self.handle)
441 {
442 selection.remove(existing);
443 } else {
444 selection.push(self.handle);
445 }
446 Some(selection)
447 } else if keyboard_modifiers.shift {
448 // Select range.
449 let mut first_position = None;
450 let mut this_position = None;
451 let mut flat_hierarchy = Vec::new();
452
453 fn visit_widget(
454 this_tree: &Tree,
455 handle: Handle<UiNode>,
456 ui: &UserInterface,
457 selection: &[Handle<UiNode>],
458 hierarchy: &mut Vec<Handle<UiNode>>,
459 first_position: &mut Option<usize>,
460 this_position: &mut Option<usize>,
461 ) {
462 let node = if handle == this_tree.handle {
463 *this_position = Some(hierarchy.len());
464
465 hierarchy.push(handle);
466
467 &this_tree.widget
468 } else {
469 let node = ui.node(handle);
470
471 if let Some(first) = selection.first() {
472 if *first == handle {
473 *first_position = Some(hierarchy.len());
474 }
475 }
476
477 if node.query_component::<Tree>().is_some() {
478 hierarchy.push(handle);
479 }
480
481 node
482 };
483
484 for &child in node.children() {
485 visit_widget(
486 this_tree,
487 child,
488 ui,
489 selection,
490 hierarchy,
491 first_position,
492 this_position,
493 );
494 }
495 }
496
497 visit_widget(
498 self,
499 tree_root_handle,
500 ui,
501 &tree_root.selected,
502 &mut flat_hierarchy,
503 &mut first_position,
504 &mut this_position,
505 );
506
507 if let (Some(this_position), Some(first_position)) =
508 (this_position, first_position)
509 {
510 Some(if first_position < this_position {
511 flat_hierarchy[first_position..=this_position].to_vec()
512 } else {
513 flat_hierarchy[this_position..=first_position].to_vec()
514 })
515 } else {
516 Some(vec![])
517 }
518 } else if !self.is_selected {
519 Some(vec![self.handle()])
520 } else {
521 None
522 };
523 if let Some(selection) = selection {
524 ui.send_message(TreeRootMessage::select(
525 tree_root_handle,
526 MessageDirection::ToWidget,
527 selection,
528 ));
529 }
530 message.set_handled(true);
531 }
532 }
533 }
534 WidgetMessage::DoubleClick { button } => {
535 if *button == MouseButton::Left {
536 // Mimic click on expander button to have uniform behavior.
537 ui.send_message(CheckBoxMessage::checked(
538 self.expander,
539 MessageDirection::ToWidget,
540 Some(!self.is_expanded),
541 ));
542
543 message.set_handled(true);
544 }
545 }
546 _ => (),
547 }
548 }
549 } else if let Some(msg) = message.data::<TreeMessage>() {
550 if message.destination() == self.handle() {
551 match msg {
552 &TreeMessage::Expand {
553 expand,
554 expansion_strategy,
555 } => {
556 self.is_expanded = expand;
557
558 ui.send_message(WidgetMessage::visibility(
559 self.panel,
560 MessageDirection::ToWidget,
561 self.is_expanded,
562 ));
563
564 ui.send_message(CheckBoxMessage::checked(
565 self.expander,
566 MessageDirection::ToWidget,
567 Some(expand),
568 ));
569
570 match expansion_strategy {
571 TreeExpansionStrategy::RecursiveDescendants => {
572 for &item in &self.items {
573 ui.send_message(TreeMessage::expand(
574 item,
575 MessageDirection::ToWidget,
576 expand,
577 expansion_strategy,
578 ));
579 }
580 }
581 TreeExpansionStrategy::RecursiveAncestors => {
582 // CAVEAT: This may lead to potential false expansions when there are
583 // trees inside trees (this is insane, but possible) because we're searching
584 // up on visual tree and don't care about search bounds, ideally we should
585 // stop search if we're found TreeRoot.
586 let parent_tree =
587 self.find_by_criteria_up(ui, |n| n.cast::<Tree>().is_some());
588 if parent_tree.is_some() {
589 ui.send_message(TreeMessage::expand(
590 parent_tree,
591 MessageDirection::ToWidget,
592 expand,
593 expansion_strategy,
594 ));
595 }
596 }
597 TreeExpansionStrategy::Direct => {
598 // Handle this variant too instead of using _ => (),
599 // to force compiler to notify if new strategy is added.
600 }
601 }
602 }
603 &TreeMessage::SetExpanderShown(show) => {
604 self.always_show_expander = show;
605 self.invalidate_arrange();
606 }
607 &TreeMessage::AddItem(item) => {
608 ui.send_message(WidgetMessage::link(
609 item,
610 MessageDirection::ToWidget,
611 self.panel,
612 ));
613
614 self.items.push(item);
615 }
616 &TreeMessage::RemoveItem(item) => {
617 if let Some(pos) = self.items.iter().position(|&i| i == item) {
618 ui.send_message(WidgetMessage::remove(
619 item,
620 MessageDirection::ToWidget,
621 ));
622 self.items.remove(pos);
623 }
624 }
625 TreeMessage::SetItems {
626 items,
627 remove_previous,
628 } => {
629 if *remove_previous {
630 for &item in self.items.iter() {
631 ui.send_message(WidgetMessage::remove(
632 item,
633 MessageDirection::ToWidget,
634 ));
635 }
636 }
637 for &item in items {
638 ui.send_message(WidgetMessage::link(
639 item,
640 MessageDirection::ToWidget,
641 self.panel,
642 ));
643 }
644 self.items.clone_from(items);
645 }
646 &TreeMessage::Select(state) => {
647 if self.is_selected != state.0 {
648 self.is_selected = state.0;
649 ui.send_message(DecoratorMessage::select(
650 self.background,
651 MessageDirection::ToWidget,
652 self.is_selected,
653 ));
654 }
655 }
656 }
657 }
658 }
659 }
660}
661
662impl Tree {
663 /// Adds new item to given tree. This method is meant to be used only on widget build stage,
664 /// any runtime actions should be done via messages.
665 pub fn add_item(tree: Handle<UiNode>, item: Handle<UiNode>, ctx: &mut BuildContext) {
666 if let Some(tree) = ctx[tree].cast_mut::<Tree>() {
667 tree.items.push(item);
668 let panel = tree.panel;
669 ctx.link(item, panel);
670 }
671 }
672}
673
674/// Tree builder creates [`Tree`] widget instances and adds them to the user interface.
675pub struct TreeBuilder {
676 widget_builder: WidgetBuilder,
677 items: Vec<Handle<UiNode>>,
678 content: Handle<UiNode>,
679 is_expanded: bool,
680 always_show_expander: bool,
681 back: Option<Handle<UiNode>>,
682}
683
684impl TreeBuilder {
685 /// Creates a new tree builder instance.
686 pub fn new(widget_builder: WidgetBuilder) -> Self {
687 Self {
688 widget_builder,
689 items: Default::default(),
690 content: Default::default(),
691 is_expanded: true,
692 always_show_expander: false,
693 back: None,
694 }
695 }
696
697 /// Sets the desired children items of the tree.
698 pub fn with_items(mut self, items: Vec<Handle<UiNode>>) -> Self {
699 self.items = items;
700 self
701 }
702
703 /// Sets the desired content of the tree.
704 pub fn with_content(mut self, content: Handle<UiNode>) -> Self {
705 self.content = content;
706 self
707 }
708
709 /// Sets the desired expansion state of the tree.
710 pub fn with_expanded(mut self, expanded: bool) -> Self {
711 self.is_expanded = expanded;
712 self
713 }
714
715 /// Sets whether the tree should always show its expander, no matter if has children items or
716 /// not.
717 pub fn with_always_show_expander(mut self, state: bool) -> Self {
718 self.always_show_expander = state;
719 self
720 }
721
722 /// Sets the desired background of the tree.
723 pub fn with_back(mut self, back: Handle<UiNode>) -> Self {
724 self.back = Some(back);
725 self
726 }
727
728 /// Builds the tree widget, but does not add it to user interface.
729 pub fn build_tree(self, ctx: &mut BuildContext) -> Tree {
730 let expander = build_expander(
731 self.always_show_expander,
732 !self.items.is_empty(),
733 self.is_expanded,
734 ctx,
735 );
736
737 if self.content.is_some() {
738 ctx[self.content].set_row(0).set_column(1);
739 };
740
741 let internals = GridBuilder::new(
742 WidgetBuilder::new()
743 .on_column(0)
744 .on_row(0)
745 .with_margin(Thickness {
746 left: 1.0,
747 top: 1.0,
748 right: 0.0,
749 bottom: 1.0,
750 })
751 .with_child(expander)
752 .with_child(self.content),
753 )
754 .add_column(Column::strict(11.0))
755 .add_column(Column::stretch())
756 .add_row(Row::strict(20.0))
757 .build(ctx);
758
759 let item_background = self.back.unwrap_or_else(|| {
760 DecoratorBuilder::new(BorderBuilder::new(
761 WidgetBuilder::new()
762 .with_foreground(Brush::Solid(Color::TRANSPARENT).into())
763 .with_background(Brush::Solid(Color::TRANSPARENT).into()),
764 ))
765 .with_selected_brush(ctx.style.property(Style::BRUSH_DIM_BLUE))
766 .with_hover_brush(ctx.style.property(Style::BRUSH_DARK))
767 .with_normal_brush(Brush::Solid(Color::TRANSPARENT).into())
768 .with_pressed_brush(Brush::Solid(Color::TRANSPARENT).into())
769 .with_pressable(false)
770 .build(ctx)
771 });
772
773 ctx.link(internals, item_background);
774
775 let panel;
776 let grid = GridBuilder::new(
777 WidgetBuilder::new()
778 .with_child(item_background)
779 .with_child({
780 panel = StackPanelBuilder::new(
781 WidgetBuilder::new()
782 .on_row(1)
783 .on_column(0)
784 .with_margin(Thickness::left(15.0))
785 .with_visibility(self.is_expanded)
786 .with_children(self.items.iter().cloned()),
787 )
788 .build(ctx);
789 panel
790 }),
791 )
792 .add_column(Column::stretch())
793 .add_row(Row::strict(24.0))
794 .add_row(Row::stretch())
795 .build(ctx);
796
797 Tree {
798 widget: self
799 .widget_builder
800 .with_allow_drag(true)
801 .with_allow_drop(true)
802 .with_child(grid)
803 .build(ctx),
804 content: self.content,
805 panel,
806 is_expanded: self.is_expanded,
807 expander,
808 background: item_background,
809 items: self.items,
810 is_selected: false,
811 always_show_expander: self.always_show_expander,
812 }
813 }
814
815 /// Finishes widget building and adds it to the user interface, returning a handle to the new
816 /// instance.
817 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
818 let tree = self.build_tree(ctx);
819 ctx.add_node(UiNode::new(tree))
820 }
821}
822
823fn build_expander(
824 always_show_expander: bool,
825 items_populated: bool,
826 is_expanded: bool,
827 ctx: &mut BuildContext,
828) -> Handle<UiNode> {
829 let down_arrow = make_arrow(ctx, ArrowDirection::Bottom, 8.0);
830 ctx[down_arrow].set_vertical_alignment(VerticalAlignment::Center);
831
832 let right_arrow = make_arrow(ctx, ArrowDirection::Right, 8.0);
833 ctx[right_arrow].set_vertical_alignment(VerticalAlignment::Center);
834
835 CheckBoxBuilder::new(
836 WidgetBuilder::new()
837 .on_row(0)
838 .on_column(0)
839 .with_visibility(always_show_expander || items_populated),
840 )
841 .with_background(
842 BorderBuilder::new(
843 WidgetBuilder::new()
844 .with_background(Brush::Solid(Color::TRANSPARENT).into())
845 .with_min_size(Vector2::new(10.0, 4.0)),
846 )
847 .with_stroke_thickness(Thickness::zero().into())
848 .build(ctx),
849 )
850 .checked(Some(is_expanded))
851 .with_check_mark(down_arrow)
852 .with_uncheck_mark(right_arrow)
853 .build(ctx)
854}
855
856/// Tree root is special widget that handles the entire hierarchy of descendant [`Tree`] widgets. Its
857/// main purpose is to handle selection of descendant [`Tree`] widgets. Tree root cannot have a
858/// content and it only could have children tree items. See docs for [`Tree`] for usage examples.
859///
860/// ## Selection
861///
862/// Tree root handles selection in the entire descendant hierarchy, and you can use [`TreeRootMessage::Selected`]
863/// message to manipulate (or listen for changes) the current selection.
864///
865/// ### Listening for Changes
866///
867/// All that is needed is to check a UI message that comes from the common message queue like so:
868///
869/// ```rust
870/// # use fyrox_ui::{
871/// # core::pool::Handle,
872/// # message::{MessageDirection, UiMessage},
873/// # tree::TreeRootMessage,
874/// # UiNode,
875/// # };
876/// #
877/// fn listen_for_selection_changes(tree_root: Handle<UiNode>, message: &UiMessage) {
878/// if let Some(TreeRootMessage::Selected(new_selection)) = message.data() {
879/// if message.destination() == tree_root
880/// && message.direction() == MessageDirection::FromWidget
881/// {
882/// println!("Selection has changed: {new_selection:?}");
883/// }
884/// }
885/// }
886/// ```
887///
888/// ### Changing Selection
889///
890/// To change a selection of the entire tree use something like this:
891///
892/// ```rust
893/// # use fyrox_ui::{
894/// # core::pool::Handle, message::MessageDirection, tree::TreeRootMessage, UiNode, UserInterface,
895/// # };
896/// #
897/// fn change_selection(
898/// tree: Handle<UiNode>,
899/// new_selection: Vec<Handle<UiNode>>,
900/// ui: &UserInterface,
901/// ) {
902/// ui.send_message(TreeRootMessage::select(
903/// tree,
904/// MessageDirection::ToWidget,
905/// new_selection,
906/// ));
907/// }
908/// ```
909#[derive(Default, Debug, Clone, Visit, Reflect, ComponentProvider)]
910pub struct TreeRoot {
911 /// Base widget of the tree root.
912 pub widget: Widget,
913 /// Current layout panel of the tree root, that is used to arrange children trees.
914 pub panel: Handle<UiNode>,
915 /// Current items of the tree root.
916 pub items: Vec<Handle<UiNode>>,
917 /// Selected items of the tree root.
918 pub selected: Vec<Handle<UiNode>>,
919}
920
921impl ConstructorProvider<UiNode, UserInterface> for TreeRoot {
922 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
923 GraphNodeConstructor::new::<Self>()
924 .with_variant("Tree Root", |ui| {
925 TreeRootBuilder::new(WidgetBuilder::new().with_name("Tree Root"))
926 .build(&mut ui.build_ctx())
927 .into()
928 })
929 .with_group("Visual")
930 }
931}
932
933crate::define_widget_deref!(TreeRoot);
934
935uuid_provider!(TreeRoot = "cf7c0476-f779-4e4b-8b7e-01a23ff51a72");
936
937impl Control for TreeRoot {
938 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
939 self.widget.handle_routed_message(ui, message);
940
941 if let Some(msg) = message.data::<TreeRootMessage>() {
942 if message.destination() == self.handle()
943 && message.direction() == MessageDirection::ToWidget
944 {
945 match msg {
946 &TreeRootMessage::AddItem(item) => {
947 ui.send_message(WidgetMessage::link(
948 item,
949 MessageDirection::ToWidget,
950 self.panel,
951 ));
952
953 self.items.push(item);
954 ui.send_message(TreeRootMessage::items_changed(
955 self.handle,
956 MessageDirection::FromWidget,
957 ));
958 }
959 &TreeRootMessage::RemoveItem(item) => {
960 if let Some(pos) = self.items.iter().position(|&i| i == item) {
961 ui.send_message(WidgetMessage::remove(
962 item,
963 MessageDirection::ToWidget,
964 ));
965
966 self.items.remove(pos);
967 ui.send_message(TreeRootMessage::items_changed(
968 self.handle,
969 MessageDirection::FromWidget,
970 ));
971 }
972 }
973 TreeRootMessage::Items(items) => {
974 for &item in self.items.iter() {
975 ui.send_message(WidgetMessage::remove(
976 item,
977 MessageDirection::ToWidget,
978 ));
979 }
980 for &item in items {
981 ui.send_message(WidgetMessage::link(
982 item,
983 MessageDirection::ToWidget,
984 self.panel,
985 ));
986 }
987
988 self.items = items.to_vec();
989 ui.send_message(TreeRootMessage::items_changed(
990 self.handle,
991 MessageDirection::FromWidget,
992 ));
993 }
994 TreeRootMessage::Selected(selected) => {
995 if &self.selected != selected {
996 let mut items = self.items.clone();
997 while let Some(handle) = items.pop() {
998 if let Some(tree_ref) = ui.try_get_of_type::<Tree>(handle) {
999 items.extend_from_slice(&tree_ref.items);
1000
1001 let new_selection_state = if selected.contains(&handle) {
1002 SelectionState(true)
1003 } else {
1004 SelectionState(false)
1005 };
1006
1007 if tree_ref.is_selected != new_selection_state.0 {
1008 ui.send_message(TreeMessage::select(
1009 handle,
1010 MessageDirection::ToWidget,
1011 new_selection_state,
1012 ));
1013 }
1014 }
1015 }
1016
1017 self.selected.clone_from(selected);
1018 ui.send_message(message.reverse());
1019 }
1020 }
1021 TreeRootMessage::CollapseAll => {
1022 self.expand_all(ui, false);
1023 }
1024 TreeRootMessage::ExpandAll => {
1025 self.expand_all(ui, true);
1026 }
1027 TreeRootMessage::ItemsChanged => {
1028 // Do nothing.
1029 }
1030 }
1031 }
1032 } else if let Some(WidgetMessage::KeyDown(key_code)) = message.data() {
1033 if !message.handled() {
1034 match *key_code {
1035 KeyCode::ArrowRight => {
1036 self.move_selection(ui, Direction::Down, true);
1037 message.set_handled(true);
1038 }
1039 KeyCode::ArrowLeft => {
1040 if let Some(selection) = self.selected.first() {
1041 if let Some(item) = ui
1042 .try_get(*selection)
1043 .and_then(|n| n.component_ref::<Tree>())
1044 {
1045 if item.is_expanded {
1046 ui.send_message(TreeMessage::expand(
1047 *selection,
1048 MessageDirection::ToWidget,
1049 false,
1050 TreeExpansionStrategy::Direct,
1051 ));
1052 message.set_handled(true);
1053 } else if let Some((parent_handle, _)) =
1054 ui.find_component_up::<Tree>(item.parent())
1055 {
1056 ui.send_message(TreeRootMessage::select(
1057 self.handle,
1058 MessageDirection::ToWidget,
1059 vec![parent_handle],
1060 ));
1061 message.set_handled(true);
1062 }
1063 }
1064 }
1065 }
1066 KeyCode::ArrowUp => {
1067 self.move_selection(ui, Direction::Up, false);
1068 message.set_handled(true);
1069 }
1070 KeyCode::ArrowDown => {
1071 self.move_selection(ui, Direction::Down, false);
1072 message.set_handled(true);
1073 }
1074 _ => (),
1075 }
1076 }
1077 }
1078 }
1079}
1080
1081enum Direction {
1082 Up,
1083 Down,
1084}
1085
1086impl TreeRoot {
1087 fn expand_all(&self, ui: &UserInterface, expand: bool) {
1088 for &item in self.items.iter() {
1089 ui.send_message(TreeMessage::expand(
1090 item,
1091 MessageDirection::ToWidget,
1092 expand,
1093 TreeExpansionStrategy::RecursiveDescendants,
1094 ));
1095 }
1096 }
1097
1098 fn select(&self, ui: &UserInterface, item: Handle<UiNode>) {
1099 ui.send_message(TreeRootMessage::select(
1100 self.handle,
1101 MessageDirection::ToWidget,
1102 vec![item],
1103 ));
1104 }
1105
1106 fn move_selection(&self, ui: &UserInterface, direction: Direction, expand: bool) {
1107 if let Some(selected_item) = self.selected.first() {
1108 let Some(item) = ui
1109 .try_get(*selected_item)
1110 .and_then(|n| n.component_ref::<Tree>())
1111 else {
1112 return;
1113 };
1114
1115 if !item.is_expanded && expand {
1116 ui.send_message(TreeMessage::expand(
1117 *selected_item,
1118 MessageDirection::ToWidget,
1119 true,
1120 TreeExpansionStrategy::Direct,
1121 ));
1122 return;
1123 }
1124
1125 let (parent_handle, parent_items, parent_ancestor, is_parent_root) = ui
1126 .find_component_up::<Tree>(item.parent())
1127 .map(|(tree_handle, tree)| (tree_handle, &tree.items, tree.parent, false))
1128 .unwrap_or_else(|| (self.handle, &self.items, self.parent, true));
1129
1130 let Some(selected_item_position) =
1131 parent_items.iter().position(|c| *c == *selected_item)
1132 else {
1133 return;
1134 };
1135
1136 match direction {
1137 Direction::Up => {
1138 if let Some(prev) = selected_item_position
1139 .checked_sub(1)
1140 .and_then(|prev| parent_items.get(prev))
1141 {
1142 let mut last_descendant_item = None;
1143 let mut queue = VecDeque::new();
1144 queue.push_back(*prev);
1145 while let Some(item) = queue.pop_front() {
1146 if let Some(item_ref) = ui.node(item).component_ref::<Tree>() {
1147 if item_ref.is_expanded {
1148 queue.extend(item_ref.items.iter());
1149 }
1150 last_descendant_item = Some(item);
1151 }
1152 }
1153
1154 if let Some(last_descendant_item) = last_descendant_item {
1155 self.select(ui, last_descendant_item);
1156 }
1157 } else if !is_parent_root {
1158 self.select(ui, parent_handle);
1159 }
1160 }
1161 Direction::Down => {
1162 if let (Some(first_item), true) = (item.items.first(), item.is_expanded) {
1163 self.select(ui, *first_item);
1164 } else if let Some(next) =
1165 parent_items.get(selected_item_position.saturating_add(1))
1166 {
1167 self.select(ui, *next);
1168 } else {
1169 let mut current_ancestor = parent_handle;
1170 let mut current_ancestor_parent = parent_ancestor;
1171 while let Some((ancestor_handle, ancestor)) =
1172 ui.find_component_up::<Tree>(current_ancestor_parent)
1173 {
1174 if ancestor.is_expanded {
1175 if let Some(current_ancestor_position) =
1176 ancestor.items.iter().position(|c| *c == current_ancestor)
1177 {
1178 if let Some(next) = ancestor
1179 .items
1180 .get(current_ancestor_position.saturating_add(1))
1181 {
1182 self.select(ui, *next);
1183 break;
1184 }
1185 }
1186 }
1187
1188 current_ancestor_parent = ancestor.parent();
1189 current_ancestor = ancestor_handle;
1190 }
1191 }
1192 }
1193 }
1194 } else if let Some(first_item) = self.items.first() {
1195 self.select(ui, *first_item);
1196 }
1197 }
1198}
1199
1200/// Tree root builder creates [`TreeRoot`] instances and adds them to the user interface.
1201pub struct TreeRootBuilder {
1202 widget_builder: WidgetBuilder,
1203 items: Vec<Handle<UiNode>>,
1204}
1205
1206impl TreeRootBuilder {
1207 /// Creates new tree root builder.
1208 pub fn new(widget_builder: WidgetBuilder) -> Self {
1209 Self {
1210 widget_builder,
1211 items: Default::default(),
1212 }
1213 }
1214
1215 /// Sets the desired items of the tree root.
1216 pub fn with_items(mut self, items: Vec<Handle<UiNode>>) -> Self {
1217 self.items = items;
1218 self
1219 }
1220
1221 /// Finishes widget building and adds the new instance to the user interface, returning its handle.
1222 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
1223 let panel =
1224 StackPanelBuilder::new(WidgetBuilder::new().with_children(self.items.iter().cloned()))
1225 .build(ctx);
1226
1227 let tree = TreeRoot {
1228 widget: self.widget_builder.with_child(panel).build(ctx),
1229 panel,
1230 items: self.items,
1231 selected: Default::default(),
1232 };
1233
1234 ctx.add_node(UiNode::new(tree))
1235 }
1236}
1237
1238#[cfg(test)]
1239mod test {
1240 use crate::tree::{TreeBuilder, TreeRootBuilder};
1241 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
1242
1243 #[test]
1244 fn test_deletion() {
1245 test_widget_deletion(|ctx| TreeRootBuilder::new(WidgetBuilder::new()).build(ctx));
1246 test_widget_deletion(|ctx| TreeBuilder::new(WidgetBuilder::new()).build(ctx));
1247 }
1248}