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