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}