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}