Skip to main content

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