vizia_core/tree/
focus_iter.rs

1use crate::entity::Entity;
2use crate::prelude::Style;
3use crate::style::{Abilities, Display};
4use vizia_id::GenerationalId;
5use vizia_storage::{
6    DoubleEndedTreeTour, FocusTreeIterator, TourDirection, Tree, TreeExt, TreeTour,
7};
8
9/// Should the user be able to navigate to the entity with tab?
10pub(crate) fn is_navigatable(
11    tree: &Tree<Entity>,
12    style: &Style,
13    node: Entity,
14    lock_focus_to: Entity,
15) -> bool {
16    // Skip invisible widgets
17    // if cx.cache.get_visibility(node) == Visibility::Hidden {
18    //     return false;
19    // }
20
21    // Skip disabled widgets
22    if style.disabled.get(node).cloned().unwrap_or_default() {
23        return false;
24    }
25
26    // Skip non-displayed widgets
27    if style.display.get(node).copied().unwrap_or_default() == Display::None {
28        return false;
29    }
30
31    // Skip nodes outside of the subtree
32    if !node.is_descendant_of(tree, lock_focus_to) {
33        return false;
34    }
35
36    // Skip ignored widgets
37    if tree.is_ignored(node) {
38        return false;
39    }
40
41    style
42        .abilities
43        .get(node)
44        .map(|abilities| abilities.contains(Abilities::NAVIGABLE))
45        .unwrap_or(false)
46}
47
48/// Get the next entity to be focused during forward keyboard navigation.
49pub(crate) fn focus_forward(
50    tree: &Tree<Entity>,
51    style: &Style,
52    node: Entity,
53    lock_focus_to: Entity,
54) -> Option<Entity> {
55    FocusTreeIterator::new(
56        tree,
57        DoubleEndedTreeTour::new(Some(node), Some(Entity::root())),
58        |node| {
59            style.display.get(node).copied().unwrap_or_default() == Display::None
60            // false
61        },
62    )
63    .skip(1)
64    .find(|node| is_navigatable(tree, style, *node, lock_focus_to))
65}
66
67/// Get the next entity to be focused during backward keybaord navigation.
68pub(crate) fn focus_backward(
69    tree: &Tree<Entity>,
70    style: &Style,
71    node: Entity,
72    lock_focus_to: Entity,
73) -> Option<Entity> {
74    let mut iter = FocusTreeIterator::new(
75        tree,
76        DoubleEndedTreeTour::new_raw(
77            TreeTour::new(Some(Entity::root())),
78            TreeTour::with_direction(Some(node), TourDirection::Leaving),
79        ),
80        |node| {
81            // Check if any ancestors are not displayed.
82            // TODO: Think of a better way to do thus.
83            for ancestor in node.parent_iter(tree) {
84                if style.display.get(ancestor).copied().unwrap_or_default() == Display::None {
85                    return true;
86                }
87            }
88
89            false
90        },
91    );
92    iter.next_back();
93    iter.filter(|node| is_navigatable(tree, style, *node, lock_focus_to)).next_back()
94}