zng_ext_input/focus/
focus_info.rs

1use std::fmt;
2use std::sync::atomic::Ordering::Relaxed;
3
4use atomic::Atomic;
5use parking_lot::Mutex;
6use zng_app::{
7    widget::{
8        WidgetId,
9        info::{TreeFilter, Visibility, WeakWidgetInfoTree, WidgetInfo, WidgetInfoBuilder, WidgetInfoTree, WidgetPath},
10    },
11    window::WindowId,
12};
13use zng_ext_window::NestedWindowWidgetInfoExt;
14use zng_layout::unit::{DistanceKey, Orientation2D, Px, PxBox, PxPoint, PxRect, PxSize};
15use zng_state_map::{StateId, static_id};
16use zng_unique_id::IdSet;
17use zng_var::impl_from_and_into_var;
18use zng_view_api::window::FocusIndicator;
19
20use zng_app::widget::info::iter as w_iter;
21
22use super::iter::IterFocusableExt;
23
24/// Widget tab navigation position within a focus scope.
25///
26/// The index is zero based, zero first.
27#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
28pub struct TabIndex(pub u32);
29impl TabIndex {
30    /// Widget is skipped during tab navigation.
31    ///
32    /// The integer value is `u32::MAX`.
33    pub const SKIP: TabIndex = TabIndex(u32::MAX);
34
35    /// Default focusable widget index.
36    ///
37    /// Tab navigation uses the widget position in the widget tree when multiple widgets have the same index
38    /// so if no widget index is explicitly set they get auto-sorted by their position.
39    ///
40    /// The integer value is `u32::MAX / 2`.
41    pub const AUTO: TabIndex = TabIndex(u32::MAX / 2);
42
43    /// Last possible widget index.
44    pub const LAST: TabIndex = TabIndex(u32::MAX - 1);
45
46    /// If is [`SKIP`](TabIndex::SKIP).
47    pub fn is_skip(self) -> bool {
48        self == Self::SKIP
49    }
50
51    /// If is [`AUTO`](TabIndex::AUTO).
52    pub fn is_auto(self) -> bool {
53        self == Self::AUTO
54    }
55
56    /// If is a custom index placed [before auto](Self::before_auto).
57    pub fn is_before_auto(self) -> bool {
58        self.0 < Self::AUTO.0
59    }
60
61    /// If is a custom index placed [after auto](Self::after_auto).
62    pub fn is_after_auto(self) -> bool {
63        self.0 > Self::AUTO.0
64    }
65
66    /// Create a new tab index that is guaranteed to not be [`SKIP`](Self::SKIP).
67    ///
68    /// Returns `SKIP - 1` if `index` is `SKIP`.
69    pub fn not_skip(index: u32) -> Self {
70        TabIndex(if index == Self::SKIP.0 { Self::SKIP.0 - 1 } else { index })
71    }
72
73    /// Create a new tab index that is guaranteed to be before [`AUTO`](Self::AUTO).
74    ///
75    /// Returns `AUTO - 1` if `index` is equal to or greater then `AUTO`.
76    pub fn before_auto(index: u32) -> Self {
77        TabIndex(if index >= Self::AUTO.0 { Self::AUTO.0 - 1 } else { index })
78    }
79
80    /// Create a new tab index that is guaranteed to be after [`AUTO`](Self::AUTO) and not [`SKIP`](Self::SKIP).
81    ///
82    /// The `index` argument is zero based here.
83    ///
84    /// Returns `not_skip(AUTO + 1 + index)`.
85    pub fn after_auto(index: u32) -> Self {
86        Self::not_skip((Self::AUTO.0 + 1).saturating_add(index))
87    }
88}
89impl fmt::Debug for TabIndex {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        if f.alternate() {
92            if self.is_auto() {
93                write!(f, "TabIndex::AUTO")
94            } else if self.is_skip() {
95                write!(f, "TabIndex::SKIP")
96            } else if self.is_after_auto() {
97                write!(f, "TabIndex::after_auto({})", self.0 - Self::AUTO.0 - 1)
98            } else {
99                write!(f, "TabIndex({})", self.0)
100            }
101        } else {
102            //
103            if self.is_auto() {
104                write!(f, "AUTO")
105            } else if self.is_skip() {
106                write!(f, "SKIP")
107            } else if self.is_after_auto() {
108                write!(f, "after_auto({})", self.0 - Self::AUTO.0 - 1)
109            } else {
110                write!(f, "{}", self.0)
111            }
112        }
113    }
114}
115impl Default for TabIndex {
116    /// `AUTO`
117    fn default() -> Self {
118        TabIndex::AUTO
119    }
120}
121impl_from_and_into_var! {
122    /// Calls [`TabIndex::not_skip`].
123    fn from(index: u32) -> TabIndex {
124        TabIndex::not_skip(index)
125    }
126}
127#[derive(serde::Serialize, serde::Deserialize)]
128#[serde(untagged)]
129enum TabIndexSerde<'s> {
130    Named(&'s str),
131    Unnamed(u32),
132}
133impl serde::Serialize for TabIndex {
134    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135    where
136        S: serde::Serializer,
137    {
138        if serializer.is_human_readable() {
139            let name = if self.is_auto() {
140                Some("AUTO")
141            } else if self.is_skip() {
142                Some("SKIP")
143            } else {
144                None
145            };
146            if let Some(name) = name {
147                return TabIndexSerde::Named(name).serialize(serializer);
148            }
149        }
150        TabIndexSerde::Unnamed(self.0).serialize(serializer)
151    }
152}
153impl<'de> serde::Deserialize<'de> for TabIndex {
154    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
155    where
156        D: serde::Deserializer<'de>,
157    {
158        use serde::de::Error;
159
160        match TabIndexSerde::deserialize(deserializer)? {
161            TabIndexSerde::Named(name) => match name {
162                "AUTO" => Ok(TabIndex::AUTO),
163                "SKIP" => Ok(TabIndex::SKIP),
164                unknown => Err(D::Error::unknown_variant(unknown, &["AUTO", "SKIP"])),
165            },
166            TabIndexSerde::Unnamed(i) => Ok(TabIndex(i)),
167        }
168    }
169}
170
171/// Tab navigation configuration of a focus scope.
172///
173/// See the [module level](../#tab-navigation) for an overview of tab navigation.
174#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
175pub enum TabNav {
176    /// Tab can move into the scope, but does not move the focus inside the scope.
177    None,
178    /// Tab moves the focus through the scope continuing out after the last item.
179    Continue,
180    /// Tab is contained in the scope, does not move after the last item.
181    Contained,
182    /// Tab is contained in the scope, after the last item moves to the first item in the scope.
183    Cycle,
184    /// Tab moves into the scope once but then moves out of the scope.
185    Once,
186}
187impl fmt::Debug for TabNav {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        if f.alternate() {
190            write!(f, "TabNav::")?;
191        }
192        match self {
193            TabNav::None => write!(f, "None"),
194            TabNav::Continue => write!(f, "Continue"),
195            TabNav::Contained => write!(f, "Contained"),
196            TabNav::Cycle => write!(f, "Cycle"),
197            TabNav::Once => write!(f, "Once"),
198        }
199    }
200}
201
202/// Directional navigation configuration of a focus scope.
203///
204/// See the [module level](../#directional-navigation) for an overview of directional navigation.
205#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
206pub enum DirectionalNav {
207    /// Arrows can move into the scope, but does not move the focus inside the scope.
208    None,
209    /// Arrows move the focus through the scope continuing out of the edges.
210    Continue,
211    /// Arrows move the focus inside the scope only, stops at the edges.
212    Contained,
213    /// Arrows move the focus inside the scope only, cycles back to opposite edges.
214    Cycle,
215}
216impl fmt::Debug for DirectionalNav {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        if f.alternate() {
219            write!(f, "DirectionalNav::")?;
220        }
221        match self {
222            DirectionalNav::None => write!(f, "None"),
223            DirectionalNav::Continue => write!(f, "Continue"),
224            DirectionalNav::Contained => write!(f, "Contained"),
225            DirectionalNav::Cycle => write!(f, "Cycle"),
226        }
227    }
228}
229
230/// Focus change request.
231///
232/// See [`FOCUS`] for details.
233///
234/// [`FOCUS`]: crate::focus::FOCUS::focus
235#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
236pub struct FocusRequest {
237    /// Where to move the focus.
238    pub target: FocusTarget,
239    /// If the widget should visually indicate that it has keyboard focus.
240    pub highlight: bool,
241
242    /// If the window should be focused even if another app has focus. By default the window
243    /// is only focused if the app has keyboard focus in any of the open windows, if this is enabled
244    /// and the operating system supports it forces focus on the window, potentially stealing keyboard focus from another app
245    /// and disrupting the user.
246    pub force_window_focus: bool,
247
248    /// Focus indicator to set on the target window if the app does not have keyboard focus and
249    /// `force_window_focus` is disabled.
250    pub window_indicator: Option<FocusIndicator>,
251
252    /// Only fulfill this request is no other focus requests are made in the same update pass.
253    pub fallback_only: bool,
254}
255
256impl FocusRequest {
257    /// New request from target and highlight.
258    pub fn new(target: FocusTarget, highlight: bool) -> Self {
259        Self {
260            target,
261            highlight,
262            force_window_focus: false,
263            window_indicator: None,
264            fallback_only: false,
265        }
266    }
267
268    /// New [`FocusTarget::Direct`] request.
269    pub fn direct(widget_id: WidgetId, highlight: bool) -> Self {
270        Self::new(FocusTarget::Direct { target: widget_id }, highlight)
271    }
272    /// New [`FocusTarget::DirectOrExit`] request.
273    pub fn direct_or_exit(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
274        Self::new(
275            FocusTarget::DirectOrExit {
276                target: widget_id,
277                navigation_origin,
278            },
279            highlight,
280        )
281    }
282    /// New [`FocusTarget::DirectOrEnter`] request.
283    pub fn direct_or_enter(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
284        Self::new(
285            FocusTarget::DirectOrEnter {
286                target: widget_id,
287                navigation_origin,
288            },
289            highlight,
290        )
291    }
292    /// New [`FocusTarget::DirectOrRelated`] request.
293    pub fn direct_or_related(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
294        Self::new(
295            FocusTarget::DirectOrRelated {
296                target: widget_id,
297                navigation_origin,
298            },
299            highlight,
300        )
301    }
302    /// New [`FocusTarget::Enter`] request.
303    pub fn enter(highlight: bool) -> Self {
304        Self::new(FocusTarget::Enter, highlight)
305    }
306    /// New [`FocusTarget::Exit`] request.
307    pub fn exit(recursive_alt: bool, highlight: bool) -> Self {
308        Self::new(FocusTarget::Exit { recursive_alt }, highlight)
309    }
310    /// New [`FocusTarget::Next`] request.
311    pub fn next(highlight: bool) -> Self {
312        Self::new(FocusTarget::Next, highlight)
313    }
314    /// New [`FocusTarget::Prev`] request.
315    pub fn prev(highlight: bool) -> Self {
316        Self::new(FocusTarget::Prev, highlight)
317    }
318    /// New [`FocusTarget::Up`] request.
319    pub fn up(highlight: bool) -> Self {
320        Self::new(FocusTarget::Up, highlight)
321    }
322    /// New [`FocusTarget::Right`] request.
323    pub fn right(highlight: bool) -> Self {
324        Self::new(FocusTarget::Right, highlight)
325    }
326    /// New [`FocusTarget::Down`] request.
327    pub fn down(highlight: bool) -> Self {
328        Self::new(FocusTarget::Down, highlight)
329    }
330    /// New [`FocusTarget::Left`] request.
331    pub fn left(highlight: bool) -> Self {
332        Self::new(FocusTarget::Left, highlight)
333    }
334    /// New [`FocusTarget::Alt`] request.
335    pub fn alt(highlight: bool) -> Self {
336        Self::new(FocusTarget::Alt, highlight)
337    }
338
339    /// Sets [`FocusRequest::force_window_focus`] to `true`.
340    pub fn with_force_window_focus(mut self) -> Self {
341        self.force_window_focus = true;
342        self
343    }
344
345    /// Sets the [`FocusRequest::window_indicator`].
346    pub fn with_indicator(mut self, indicator: FocusIndicator) -> Self {
347        self.window_indicator = Some(indicator);
348        self
349    }
350}
351
352/// Focus request target.
353#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
354pub enum FocusTarget {
355    /// Move focus to widget.
356    Direct {
357        /// Focusable widget.
358        target: WidgetId,
359    },
360    /// Move focus to the widget if it is focusable or to the first focusable ancestor.
361    DirectOrExit {
362        /// Maybe focusable widget.
363        target: WidgetId,
364        /// If `true` the `target` always becomes the [`navigation_origin`], even when it is not focusable.
365        ///
366        /// [`navigation_origin`]: crate::focus::FOCUS::navigation_origin
367        navigation_origin: bool,
368    },
369    /// Move focus to the widget if it is focusable or to first focusable descendant.
370    DirectOrEnter {
371        /// Maybe focusable widget.
372        target: WidgetId,
373        /// If `true` the `target` becomes the [`navigation_origin`] when it is not focusable and has no
374        /// focusable descendant.
375        ///
376        /// [`navigation_origin`]: crate::focus::FOCUS::navigation_origin
377        navigation_origin: bool,
378    },
379    /// Move focus to the widget if it is focusable, or to the first focusable descendant or
380    /// to the first focusable ancestor.
381    DirectOrRelated {
382        /// Maybe focusable widget.
383        target: WidgetId,
384        /// If `true` the `target` becomes the [`navigation_origin`] when it is not focusable and has no
385        /// focusable descendant.
386        ///
387        /// [`navigation_origin`]: crate::focus::FOCUS::navigation_origin
388        navigation_origin: bool,
389    },
390
391    /// Move focus to the first focusable descendant of the current focus.
392    Enter,
393    /// Move focus to the first focusable ancestor of the current focus, or the return focus from ALT scopes.
394    Exit {
395        /// If exiting from an ALT scope recursively seek the return widget that is not inside any ALT scope.
396        recursive_alt: bool,
397    },
398
399    /// Move focus to next from current in scope.
400    Next,
401    /// Move focus to previous from current in scope.
402    Prev,
403
404    /// Move focus above current.
405    Up,
406    /// Move focus to the right of current.
407    Right,
408    /// Move focus bellow current.
409    Down,
410    /// Move focus to the left of current.
411    Left,
412
413    /// Move focus to the current widget ALT scope or out of it.
414    Alt,
415}
416
417bitflags! {
418    /// Represents the [`FocusTarget`] actions that move focus from the current focused widget.
419    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
420    pub struct FocusNavAction: u16 {
421        /// [`FocusTarget::Enter`]
422        const ENTER = 0b0000_0000_0001;
423        /// [`FocusTarget::Exit`]
424        const EXIT = 0b0000_0000_0010;
425
426        /// [`FocusTarget::Next`]
427        const NEXT = 0b0000_0000_0100;
428        /// [`FocusTarget::Prev`]
429        const PREV = 0b0000_0000_1000;
430
431        /// [`FocusTarget::Up`]
432        const UP = 0b0000_0001_0000;
433        /// [`FocusTarget::Right`]
434        const RIGHT = 0b0000_0010_0000;
435        /// [`FocusTarget::Down`]
436        const DOWN = 0b0000_0100_0000;
437        /// [`FocusTarget::Left`]
438        const LEFT = 0b0000_1000_0000;
439
440        /// [`FocusTarget::Alt`]
441        const ALT = 0b0001_0000_0000;
442
443        /// Up, right, down, left.
444        const DIRECTIONAL =
445            FocusNavAction::UP.bits() | FocusNavAction::RIGHT.bits() | FocusNavAction::DOWN.bits() | FocusNavAction::LEFT.bits();
446    }
447}
448
449bitflags! {
450    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
451    pub(super) struct FocusMode: u8 {
452        /// Allow focus in disabled widgets.
453        const DISABLED = 1;
454        /// Allow focus in hidden widgets.
455        const HIDDEN = 2;
456    }
457}
458impl FocusMode {
459    pub fn new(focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
460        let mut mode = FocusMode::empty();
461        mode.set(FocusMode::DISABLED, focus_disabled_widgets);
462        mode.set(FocusMode::HIDDEN, focus_hidden_widgets);
463        mode
464    }
465}
466
467/// A [`WidgetInfoTree`] wrapper for querying focus info out of the widget tree.
468///
469/// [`WidgetInfoTree`]: zng_app::widget::info::WidgetInfoTree
470#[derive(Clone, Debug)]
471pub struct FocusInfoTree {
472    tree: WidgetInfoTree,
473    mode: FocusMode,
474}
475impl FocusInfoTree {
476    /// Wrap a `widget_info` reference to enable focus info querying.
477    ///
478    /// See the [`FOCUS.focus_disabled_widgets`] and [`FOCUS.focus_hidden_widgets`] config for more details on the parameters.
479    ///
480    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
481    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
482    pub fn new(tree: WidgetInfoTree, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
483        FocusInfoTree {
484            tree,
485            mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
486        }
487    }
488
489    /// Full widget info.
490    pub fn tree(&self) -> &WidgetInfoTree {
491        &self.tree
492    }
493
494    /// If [`DISABLED`] widgets are focusable in this tree.
495    ///
496    /// See the [`FOCUS.focus_disabled_widgets`] config for more details.
497    ///
498    /// [`DISABLED`]: zng_app::widget::info::Interactivity::DISABLED
499    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
500    pub fn focus_disabled_widgets(&self) -> bool {
501        self.mode.contains(FocusMode::DISABLED)
502    }
503
504    /// If [`Hidden`] widgets are focusable in this tree.
505    ///
506    /// See the [`FOCUS.focus_hidden_widgets`] config for more details.
507    ///
508    /// [`Hidden`]: Visibility::Hidden
509    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
510    pub fn focus_hidden_widgets(&self) -> bool {
511        self.mode.contains(FocusMode::HIDDEN)
512    }
513
514    /// Reference to the root widget in the tree.
515    ///
516    /// The root is usually a focusable focus scope but it may not be. This
517    /// is the only method that returns a [`WidgetFocusInfo`] that may not be focusable.
518    pub fn root(&self) -> WidgetFocusInfo {
519        WidgetFocusInfo {
520            info: self.tree.root(),
521            mode: self.mode,
522        }
523    }
524
525    /// Reference the focusable widget closest to the window root.
526    ///
527    /// When the window root is not focusable, but a descendant widget is, this method returns
528    /// the focusable closest to the root counting previous siblings then parents.
529    pub fn focusable_root(&self) -> Option<WidgetFocusInfo> {
530        let root = self.root();
531        if root.is_focusable() {
532            return Some(root);
533        }
534
535        let mut candidate = None;
536        let mut candidate_weight = usize::MAX;
537
538        for w in root.descendants().tree_filter(|_| TreeFilter::SkipDescendants) {
539            let weight = w.info.prev_siblings().count() + w.info.ancestors().count();
540            if weight < candidate_weight {
541                candidate = Some(w);
542                candidate_weight = weight;
543            }
544        }
545
546        candidate
547    }
548
549    /// Reference to the widget in the tree, if it is present and is focusable.
550    pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetFocusInfo> {
551        self.tree
552            .get(widget_id)
553            .and_then(|i| i.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
554    }
555
556    /// Reference to the first focusable widget or parent in the tree.
557    pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetFocusInfo> {
558        self.get(path.widget_id())
559            .or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
560    }
561
562    /// If the tree info contains the widget and it is focusable.
563    pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
564        self.get(widget_id).is_some()
565    }
566}
567
568/// [`WidgetInfo`] extensions that build a [`WidgetFocusInfo`].
569///
570/// [`WidgetInfo`]: zng_app::widget::info::WidgetInfo
571pub trait WidgetInfoFocusExt {
572    /// Wraps the [`WidgetInfo`] in a [`WidgetFocusInfo`] even if it is not focusable.
573    ///
574    /// See the [`FOCUS.focus_disabled_widgets`] and [`FOCUS.focus_hidden_widgets`] config for more details on the parameters.
575    ///
576    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
577    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
578    /// [`WidgetInfo`]: zng_app::widget::info::WidgetInfo
579    fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo;
580    /// Returns a wrapped [`WidgetFocusInfo`] if the [`WidgetInfo`] is focusable.
581    ///
582    /// See the [`FOCUS.focus_disabled_widgets`] and [`FOCUS.focus_hidden_widgets`] config for more details on the parameters.
583    ///
584    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
585    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
586    /// [`WidgetInfo`]: zng_app::widget::info::WidgetInfo
587    fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo>;
588}
589impl WidgetInfoFocusExt for WidgetInfo {
590    fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo {
591        WidgetFocusInfo::new(self, focus_disabled_widgets, focus_hidden_widgets)
592    }
593    fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo> {
594        let r = self.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
595        if r.is_focusable() { Some(r) } else { None }
596    }
597}
598
599/// [`WidgetInfo`] wrapper that adds focus information for each widget.
600///
601/// [`WidgetInfo`]: zng_app::widget::info::WidgetInfo
602#[derive(Clone, Eq, PartialEq, Hash, Debug)]
603pub struct WidgetFocusInfo {
604    info: WidgetInfo,
605    mode: FocusMode,
606}
607impl WidgetFocusInfo {
608    /// Wrap a `widget_info` reference to enable focus info querying.
609    ///
610    /// See the [`FOCUS.focus_disabled_widgets`] and [`FOCUS.focus_hidden_widgets`] config for more details on the parameters.
611    ///
612    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
613    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
614    pub fn new(widget_info: WidgetInfo, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
615        WidgetFocusInfo {
616            info: widget_info,
617            mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
618        }
619    }
620
621    /// Full widget info.
622    pub fn info(&self) -> &WidgetInfo {
623        &self.info
624    }
625
626    /// If [`DISABLED`] widgets are focusable in this tree.
627    ///
628    /// See the [`FOCUS.focus_disabled_widgets`] config for more details.
629    ///
630    /// [`DISABLED`]: zng_app::widget::info::Interactivity::DISABLED
631    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
632    pub fn focus_disabled_widgets(&self) -> bool {
633        self.mode.contains(FocusMode::DISABLED)
634    }
635
636    /// If [`Hidden`] widgets are focusable in this tree.
637    ///
638    /// See the [`FOCUS.focus_hidden_widgets`] config for more details.
639    ///
640    /// [`Hidden`]: Visibility::Hidden
641    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
642    pub fn focus_hidden_widgets(&self) -> bool {
643        self.mode.contains(FocusMode::HIDDEN)
644    }
645
646    /// Root focusable.
647    pub fn root(&self) -> Self {
648        self.ancestors().last().unwrap_or_else(|| self.clone())
649    }
650
651    /// Clone a reference to the [`FocusInfoTree`] that owns this widget.
652    pub fn focus_tree(&self) -> FocusInfoTree {
653        FocusInfoTree {
654            tree: self.info.tree().clone(),
655            mode: self.mode,
656        }
657    }
658
659    /// If the widget is focusable.
660    ///
661    /// ## Note
662    ///
663    /// This is probably `true`, the only way to get a [`WidgetFocusInfo`] for a non-focusable widget is by
664    /// calling [`into_focus_info`](WidgetInfoFocusExt::into_focus_info) or explicitly constructing one.
665    ///
666    /// Focus scopes are also focusable.
667    pub fn is_focusable(&self) -> bool {
668        self.focus_info().is_focusable()
669    }
670
671    /// Is focus scope.
672    pub fn is_scope(&self) -> bool {
673        self.focus_info().is_scope()
674    }
675
676    /// Is ALT focus scope.
677    pub fn is_alt_scope(&self) -> bool {
678        self.focus_info().is_alt_scope()
679    }
680
681    /// Gets the nested window ID, if this widget hosts a nested window.
682    ///
683    /// Nested window hosts always focus the nested window on focus.
684    pub fn nested_window(&self) -> Option<WindowId> {
685        self.info.nested_window()
686    }
687
688    /// Gets the nested window focus tree, if this widget hosts a nested window.
689    pub fn nested_window_tree(&self) -> Option<FocusInfoTree> {
690        self.info
691            .nested_window_tree()
692            .map(|t| FocusInfoTree::new(t, self.focus_disabled_widgets(), self.focus_hidden_widgets()))
693    }
694
695    fn mode_allows_focus(&self) -> bool {
696        let int = self.info.interactivity();
697        if self.mode.contains(FocusMode::DISABLED) {
698            if int.is_blocked() {
699                return false;
700            }
701        } else if !int.is_enabled() {
702            return false;
703        }
704
705        let vis = self.info.visibility();
706        if self.mode.contains(FocusMode::HIDDEN) {
707            if vis == Visibility::Collapsed {
708                return false;
709            }
710        } else if vis != Visibility::Visible {
711            return false;
712        }
713
714        true
715    }
716
717    fn mode_allows_focus_ignore_blocked(&self) -> bool {
718        let int = self.info.interactivity();
719        if !self.mode.contains(FocusMode::DISABLED) && int.is_vis_disabled() {
720            return false;
721        }
722
723        let vis = self.info.visibility();
724        if self.mode.contains(FocusMode::HIDDEN) {
725            if vis == Visibility::Collapsed {
726                return false;
727            }
728        } else if vis != Visibility::Visible {
729            return false;
730        }
731
732        true
733    }
734
735    /// Widget focus metadata.
736    pub fn focus_info(&self) -> FocusInfo {
737        if self.mode_allows_focus() {
738            if let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID) {
739                return builder.build();
740            } else if self.info.nested_window().is_some() {
741                // service will actually focus nested window
742                return FocusInfo::FocusScope {
743                    tab_index: TabIndex::AUTO,
744                    skip_directional: false,
745                    tab_nav: TabNav::Contained,
746                    directional_nav: DirectionalNav::Contained,
747                    on_focus: FocusScopeOnFocus::FirstDescendant,
748                    alt: false,
749                };
750            }
751        }
752        FocusInfo::NotFocusable
753    }
754
755    /// Widget focus metadata, all things equal except the widget interactivity is blocked.
756    pub fn focus_info_ignore_blocked(&self) -> FocusInfo {
757        if self.mode_allows_focus_ignore_blocked()
758            && let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID)
759        {
760            return builder.build();
761        }
762        FocusInfo::NotFocusable
763    }
764
765    /// Iterator over focusable parent -> grandparent -> .. -> root.
766    pub fn ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
767        let focus_disabled_widgets = self.focus_disabled_widgets();
768        let focus_hidden_widgets = self.focus_hidden_widgets();
769        self.info.ancestors().focusable(focus_disabled_widgets, focus_hidden_widgets)
770    }
771
772    /// Iterator over self -> focusable parent -> grandparent -> .. -> root.
773    pub fn self_and_ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
774        [self.clone()].into_iter().chain(self.ancestors())
775    }
776
777    /// Iterator over focus scopes parent -> grandparent -> .. -> root.
778    pub fn scopes(&self) -> impl Iterator<Item = WidgetFocusInfo> {
779        let focus_disabled_widgets = self.focus_disabled_widgets();
780        let focus_hidden_widgets = self.focus_hidden_widgets();
781        self.info.ancestors().filter_map(move |i| {
782            let i = i.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
783            if i.is_scope() { Some(i) } else { None }
784        })
785    }
786
787    /// Reference to the focusable parent that contains this widget.
788    pub fn parent(&self) -> Option<WidgetFocusInfo> {
789        self.ancestors().next()
790    }
791
792    /// Reference the focus scope parent that contains the widget.
793    pub fn scope(&self) -> Option<WidgetFocusInfo> {
794        self.scopes().next()
795    }
796
797    /// Reference the ALT focus scope *closest* with the current widget.
798    ///
799    /// # Closest Alt Scope
800    ///
801    /// - If `self` is already an ALT scope or is in one, moves to a sibling ALT scope, nested ALT scopes are ignored.
802    /// - If `self` is a normal scope, moves to the first descendant ALT scope, otherwise..
803    /// - Recursively searches for an ALT scope sibling up the scope tree.
804    pub fn alt_scope(&self) -> Option<WidgetFocusInfo> {
805        if self.in_alt_scope() {
806            // We do not allow nested alt scopes, search for sibling focus scope.
807            let mut alt_scope = self.clone();
808            for scope in self.scopes() {
809                if scope.is_alt_scope() {
810                    alt_scope = scope;
811                } else {
812                    return scope.inner_alt_scope_skip(&alt_scope);
813                }
814            }
815            return None;
816        }
817
818        if self.is_scope() {
819            // if we are a normal scope, try for an inner ALT scope descendant first.
820            let r = self.inner_alt_scope();
821            if r.is_some() {
822                return r;
823            }
824        }
825
826        // try each parent scope up the tree
827        let mut skip = self.clone();
828        for scope in self.scopes() {
829            let r = scope.inner_alt_scope_skip(&skip);
830            if r.is_some() {
831                return r;
832            }
833            skip = scope;
834        }
835
836        None
837    }
838    fn inner_alt_scope(&self) -> Option<WidgetFocusInfo> {
839        let inner_alt = self.info.meta().get(*FOCUS_INFO_ID)?.inner_alt.load(Relaxed);
840        if let Some(id) = inner_alt
841            && let Some(wgt) = self.info.tree().get(id)
842        {
843            let wgt = wgt.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
844            if wgt.is_alt_scope() && wgt.info.is_descendant(&self.info) {
845                return Some(wgt);
846            }
847        }
848        None
849    }
850    fn inner_alt_scope_skip(&self, skip: &WidgetFocusInfo) -> Option<WidgetFocusInfo> {
851        if let Some(alt) = self.inner_alt_scope()
852            && !alt.info.is_descendant(&skip.info)
853            && alt.info != skip.info
854        {
855            return Some(alt);
856        }
857        None
858    }
859
860    /// Widget is in a ALT scope or is an ALT scope.
861    pub fn in_alt_scope(&self) -> bool {
862        self.is_alt_scope() || self.scopes().any(|s| s.is_alt_scope())
863    }
864
865    /// Widget the focus needs to move to when `self` gets focused.
866    ///
867    /// # Input
868    ///
869    /// * `last_focused`: A function that returns the last focused widget within a focus scope identified by `WidgetId`.
870    /// * `is_tab_cycle_reentry`: If the focus returned to `self` immediately after leaving because the parent scope is `TabNav::Cycle`.
871    /// * `reverse`: If the focus *reversed* into `self`.
872    ///
873    /// # Returns
874    ///
875    /// Returns the different widget the focus must move to after focusing in `self` that is a focus scope.
876    ///
877    /// If `self` is not a [`FocusScope`](FocusInfo::FocusScope) always returns `None`.
878    pub fn on_focus_scope_move(
879        &self,
880        last_focused: impl FnOnce(WidgetId) -> Option<WidgetId>,
881        is_tab_cycle_reentry: bool,
882        reverse: bool,
883    ) -> Option<WidgetFocusInfo> {
884        match self.focus_info() {
885            FocusInfo::FocusScope { on_focus, .. } => {
886                let candidate = match on_focus {
887                    FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::FirstDescendantIgnoreBounds => {
888                        if reverse {
889                            self.last_tab_descendant()
890                        } else {
891                            self.first_tab_descendant()
892                        }
893                    }
894                    FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds => {
895                        if is_tab_cycle_reentry { None } else { last_focused(self.info.id()) }
896                            .and_then(|id| self.info.tree().get(id))
897                            .and_then(|w| w.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
898                            .and_then(|f| {
899                                if f.info.is_descendant(&self.info) {
900                                    Some(f) // valid last focused
901                                } else {
902                                    None
903                                }
904                            })
905                            .or_else(|| {
906                                if reverse {
907                                    self.last_tab_descendant()
908                                } else {
909                                    self.first_tab_descendant()
910                                }
911                            })
912                    } // fallback
913                    FocusScopeOnFocus::Widget => None,
914                };
915
916                // if not IgnoreBounds and some candidate
917                if let FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::LastFocused = on_focus
918                    && let Some(candidate) = &candidate
919                    && !self.info.inner_bounds().contains_rect(&candidate.info().inner_bounds())
920                {
921                    // not fully in bounds.
922                    return None;
923                }
924
925                candidate
926            }
927            FocusInfo::NotFocusable | FocusInfo::Focusable { .. } => None,
928        }
929    }
930
931    /// Iterator over the focusable widgets contained by this widget.
932    pub fn descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
933        super::iter::FocusTreeIter::new(self.info.descendants(), self.mode)
934    }
935
936    /// Iterator over self and the focusable widgets contained by it.
937    pub fn self_and_descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
938        super::iter::FocusTreeIter::new(self.info.self_and_descendants(), self.mode)
939    }
940
941    /// If the focusable has any focusable descendant that is not [`TabIndex::SKIP`]
942    pub fn has_tab_descendant(&self) -> bool {
943        self.descendants().tree_find(Self::filter_tab_skip).is_some()
944    }
945
946    /// First descendant considering TAB index.
947    pub fn first_tab_descendant(&self) -> Option<WidgetFocusInfo> {
948        let mut best = (TabIndex::SKIP, self.clone());
949
950        for d in self.descendants().tree_filter(Self::filter_tab_skip) {
951            let idx = d.focus_info().tab_index();
952
953            if idx < best.0 {
954                best = (idx, d);
955            }
956        }
957
958        if best.0.is_skip() { None } else { Some(best.1) }
959    }
960
961    /// Last descendant considering TAB index.
962    pub fn last_tab_descendant(&self) -> Option<WidgetFocusInfo> {
963        let mut best = (-1i64, self.clone());
964
965        for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
966            let idx = d.focus_info().tab_index().0 as i64;
967
968            if idx > best.0 {
969                best = (idx, d);
970            }
971        }
972
973        if best.0 < 0 { None } else { Some(best.1) }
974    }
975
976    /// Iterator over all focusable widgets in the same scope after this widget.
977    pub fn next_focusables(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
978        if let Some(scope) = self.scope() {
979            super::iter::FocusTreeIter::new(self.info.next_siblings_in(&scope.info), self.mode)
980        } else {
981            // empty
982            super::iter::FocusTreeIter::new(self.info.next_siblings_in(&self.info), self.mode)
983        }
984    }
985
986    /// Next focusable in the same scope after this widget.
987    pub fn next_focusable(&self) -> Option<WidgetFocusInfo> {
988        self.next_focusables().next()
989    }
990
991    fn filter_tab_skip(w: &WidgetFocusInfo) -> TreeFilter {
992        if w.focus_info().tab_index().is_skip() {
993            TreeFilter::SkipAll
994        } else {
995            TreeFilter::Include
996        }
997    }
998
999    /// Next focusable in the same scope after this widget respecting the TAB index.
1000    ///
1001    /// If `self` is set to [`TabIndex::SKIP`] returns the next non-skip focusable in the same scope after this widget.
1002    ///
1003    /// If `skip_self` is `true`, does not include widgets inside `self`.
1004    pub fn next_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1005        self.next_tab_focusable_impl(skip_self, false)
1006    }
1007    fn next_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
1008        let self_index = self.focus_info().tab_index();
1009
1010        if self_index == TabIndex::SKIP {
1011            // TAB from skip, goes to next in widget tree.
1012            return self.next_focusables().tree_find(Self::filter_tab_skip);
1013        }
1014
1015        let mut best = (TabIndex::SKIP, self.clone());
1016
1017        if !skip_self {
1018            for d in self.descendants().tree_filter(Self::filter_tab_skip) {
1019                let idx = d.focus_info().tab_index();
1020
1021                if idx == self_index {
1022                    return Some(d);
1023                } else if idx < best.0 && idx > self_index {
1024                    if any {
1025                        return Some(d);
1026                    }
1027                    best = (idx, d);
1028                }
1029            }
1030        }
1031
1032        for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
1033            let idx = s.focus_info().tab_index();
1034
1035            if idx == self_index {
1036                return Some(s);
1037            } else if idx < best.0 && idx > self_index {
1038                if any {
1039                    return Some(s);
1040                }
1041                best = (idx, s);
1042            }
1043        }
1044
1045        for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
1046            let idx = s.focus_info().tab_index();
1047
1048            if idx <= best.0 && idx > self_index {
1049                if any {
1050                    return Some(s);
1051                }
1052                best = (idx, s);
1053            }
1054        }
1055
1056        if best.0.is_skip() { None } else { Some(best.1) }
1057    }
1058
1059    /// Iterator over all focusable widgets in the same scope before this widget in reverse.
1060    pub fn prev_focusables(&self) -> super::iter::FocusTreeIter<w_iter::RevTreeIter> {
1061        if let Some(scope) = self.scope() {
1062            super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&scope.info), self.mode)
1063        } else {
1064            // empty
1065            super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&self.info), self.mode)
1066        }
1067    }
1068
1069    /// Previous focusable in the same scope before this widget.
1070    pub fn prev_focusable(&self) -> Option<WidgetFocusInfo> {
1071        self.prev_focusables().next()
1072    }
1073
1074    /// Previous focusable in the same scope after this widget respecting the TAB index.
1075    ///
1076    /// If `self` is set to [`TabIndex::SKIP`] returns the previous non-skip focusable in the same scope before this widget.
1077    ///
1078    /// If `skip_self` is `true`, does not include widgets inside `self`.
1079    pub fn prev_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1080        self.prev_tab_focusable_impl(skip_self, false)
1081    }
1082    fn prev_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
1083        let self_index = self.focus_info().tab_index();
1084
1085        if self_index == TabIndex::SKIP {
1086            // TAB from skip, goes to prev in widget tree.
1087            return self.prev_focusables().tree_find(Self::filter_tab_skip);
1088        }
1089
1090        let self_index = self_index.0 as i64;
1091        let mut best = (-1i64, self.clone());
1092
1093        if !skip_self {
1094            for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
1095                let idx = d.focus_info().tab_index().0 as i64;
1096
1097                if idx == self_index {
1098                    return Some(d);
1099                } else if idx > best.0 && idx < self_index {
1100                    if any {
1101                        return Some(d);
1102                    }
1103                    best = (idx, d);
1104                }
1105            }
1106        }
1107
1108        for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
1109            let idx = s.focus_info().tab_index().0 as i64;
1110
1111            if idx == self_index {
1112                return Some(s);
1113            } else if idx > best.0 && idx < self_index {
1114                if any {
1115                    return Some(s);
1116                }
1117                best = (idx, s);
1118            }
1119        }
1120
1121        for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
1122            let idx = s.focus_info().tab_index().0 as i64;
1123
1124            if idx >= best.0 && idx < self_index {
1125                if any {
1126                    return Some(s);
1127                }
1128                best = (idx, s);
1129            }
1130        }
1131
1132        if best.0 < 0 { None } else { Some(best.1) }
1133    }
1134
1135    /// Widget to focus when pressing TAB from this widget.
1136    ///
1137    /// Set `skip_self` to not enter `self`, that is, the focus goes to the next sibling or next sibling descendant.
1138    ///
1139    /// Returns `None` if the focus does not move to another widget.
1140    pub fn next_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1141        let _span = tracing::trace_span!("next_tab").entered();
1142
1143        if let Some(scope) = self.scope() {
1144            let scope_info = scope.focus_info();
1145            match scope_info.tab_nav() {
1146                TabNav::None => None,
1147                TabNav::Continue => self.next_tab_focusable(skip_self).or_else(|| scope.next_tab(true)),
1148                TabNav::Contained => self.next_tab_focusable(skip_self),
1149                TabNav::Cycle => self.next_tab_focusable(skip_self).or_else(|| scope.first_tab_descendant()),
1150                TabNav::Once => scope.next_tab(true),
1151            }
1152        } else {
1153            None
1154        }
1155    }
1156
1157    /// Widget to focus when pressing SHIFT+TAB from this widget.
1158    ///
1159    /// Set `skip_self` to not enter `self`, that is, the focus goes to the previous sibling or previous sibling descendant.
1160    ///
1161    /// Returns `None` if the focus does not move to another widget.
1162    pub fn prev_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1163        let _span = tracing::trace_span!("prev_tab").entered();
1164        if let Some(scope) = self.scope() {
1165            let scope_info = scope.focus_info();
1166            match scope_info.tab_nav() {
1167                TabNav::None => None,
1168                TabNav::Continue => self.prev_tab_focusable(skip_self).or_else(|| scope.prev_tab(true)),
1169                TabNav::Contained => self.prev_tab_focusable(skip_self),
1170                TabNav::Cycle => self.prev_tab_focusable(skip_self).or_else(|| scope.last_tab_descendant()),
1171                TabNav::Once => scope.prev_tab(true),
1172            }
1173        } else {
1174            None
1175        }
1176    }
1177
1178    /// Find the focusable descendant with center point nearest of `origin` within the `max_radius`.
1179    pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetFocusInfo> {
1180        let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1181        self.info
1182            .nearest_filtered(origin, max_radius, |w| cast(w.clone()).is_focusable())
1183            .map(cast)
1184    }
1185
1186    /// Find the descendant with center point nearest of `origin` within the `max_radius` and approved by the `filter` closure.
1187    pub fn nearest_filtered(
1188        &self,
1189        origin: PxPoint,
1190        max_radius: Px,
1191        mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1192    ) -> Option<WidgetFocusInfo> {
1193        let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1194        self.info
1195            .nearest_filtered(origin, max_radius, |w| {
1196                let w = cast(w.clone());
1197                w.is_focusable() && filter(w)
1198            })
1199            .map(cast)
1200    }
1201
1202    /// Find the descendant with center point nearest of `origin` within the `max_radius` and inside `bounds`; and approved by the `filter` closure.
1203    pub fn nearest_bounded_filtered(
1204        &self,
1205        origin: PxPoint,
1206        max_radius: Px,
1207        bounds: PxRect,
1208        mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1209    ) -> Option<WidgetFocusInfo> {
1210        let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1211        self.info
1212            .nearest_bounded_filtered(origin, max_radius, bounds, move |w| {
1213                let w = cast(w.clone());
1214                w.is_focusable() && filter(w)
1215            })
1216            .map(cast)
1217    }
1218
1219    /// Find the focusable descendant with center point nearest of `origin` within the `max_distance` and with `orientation` to origin.
1220    pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
1221        let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1222        self.info
1223            .nearest_oriented_filtered(origin, max_distance, orientation, |w| cast(w.clone()).is_focusable())
1224            .map(cast)
1225    }
1226
1227    /// Find the focusable descendant with center point nearest of `origin` within the `max_distance` and with `orientation`
1228    /// to origin that passes the `filter`.
1229    pub fn nearest_oriented_filtered(
1230        &self,
1231        origin: PxPoint,
1232        max_distance: Px,
1233        orientation: Orientation2D,
1234        mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1235    ) -> Option<WidgetFocusInfo> {
1236        let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1237        self.info
1238            .nearest_oriented_filtered(origin, max_distance, orientation, |w| {
1239                let w = cast(w.clone());
1240                w.is_focusable() && filter(w)
1241            })
1242            .map(cast)
1243    }
1244
1245    fn directional_from(
1246        &self,
1247        scope: &WidgetFocusInfo,
1248        origin: PxBox,
1249        orientation: Orientation2D,
1250        skip_self: bool,
1251        any: bool,
1252    ) -> Option<WidgetFocusInfo> {
1253        let self_id = self.info.id();
1254        let scope_id = scope.info.id();
1255
1256        // don't return focus to parent from non-focusable child.
1257        let skip_parent = if self.is_focusable() {
1258            None
1259        } else {
1260            self.ancestors().next().map(|w| w.info.id())
1261        };
1262
1263        let filter = |w: &WidgetFocusInfo| {
1264            let mut up_to_scope = w.self_and_ancestors().take_while(|w| w.info.id() != scope_id);
1265
1266            if skip_self {
1267                up_to_scope.all(|w| w.info.id() != self_id && !w.focus_info().skip_directional())
1268            } else {
1269                up_to_scope.all(|w| !w.focus_info().skip_directional())
1270            }
1271        };
1272
1273        let origin_center = origin.center();
1274
1275        let mut oriented = scope
1276            .info
1277            .oriented(origin_center, Px::MAX, orientation)
1278            .chain(
1279                // nearby boxes (not overlapped)
1280                scope
1281                    .info
1282                    .oriented_box(origin, origin.width().max(origin.height()) * Px(2), orientation)
1283                    .filter(|w| !w.inner_bounds().to_box2d().intersects(&origin)),
1284            )
1285            .focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets())
1286            .filter(|w| w.info.id() != scope_id && Some(w.info.id()) != skip_parent);
1287
1288        if any {
1289            return oriented.find(filter);
1290        }
1291
1292        let parent_range = self.parent().map(|w| w.info.descendants_range()).unwrap_or_default();
1293
1294        let mut ancestor_dist = DistanceKey::NONE_MAX;
1295        let mut ancestor = None;
1296        let mut sibling_dist = DistanceKey::NONE_MAX;
1297        let mut sibling = None;
1298        let mut other_dist = DistanceKey::NONE_MAX;
1299        let mut other = None;
1300
1301        for w in oriented {
1302            if filter(&w) {
1303                let dist = w.info.distance_key(origin_center);
1304
1305                let mut is_ancestor = None;
1306                let mut is_ancestor = || *is_ancestor.get_or_insert_with(|| w.info.is_ancestor(&self.info));
1307
1308                let mut is_sibling = None;
1309                let mut is_sibling = || *is_sibling.get_or_insert_with(|| parent_range.contains(&w.info));
1310
1311                if dist <= ancestor_dist && is_ancestor() {
1312                    ancestor_dist = dist;
1313                    ancestor = Some(w);
1314                } else if dist <= sibling_dist && is_sibling() {
1315                    sibling_dist = dist;
1316                    sibling = Some(w);
1317                } else if dist <= other_dist && !is_ancestor() && !is_sibling() {
1318                    other_dist = dist;
1319                    other = Some(w);
1320                }
1321            }
1322        }
1323
1324        if other_dist <= ancestor_dist && other_dist <= sibling_dist {
1325            other
1326        } else {
1327            sibling.or(ancestor)
1328        }
1329    }
1330
1331    fn directional_next(&self, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
1332        self.directional_next_from(orientation, self.info.inner_bounds().to_box2d())
1333    }
1334
1335    fn directional_next_from(&self, orientation: Orientation2D, from: PxBox) -> Option<WidgetFocusInfo> {
1336        self.scope()
1337            .and_then(|s| self.directional_from(&s, from, orientation, false, false))
1338    }
1339
1340    /// Closest focusable in the same scope above this widget.
1341    pub fn focusable_up(&self) -> Option<WidgetFocusInfo> {
1342        self.directional_next(Orientation2D::Above)
1343    }
1344
1345    /// Closest focusable in the same scope below this widget.
1346    pub fn focusable_down(&self) -> Option<WidgetFocusInfo> {
1347        self.directional_next(Orientation2D::Below)
1348    }
1349
1350    /// Closest focusable in the same scope to the left of this widget.
1351    pub fn focusable_left(&self) -> Option<WidgetFocusInfo> {
1352        self.directional_next(Orientation2D::Left)
1353    }
1354
1355    /// Closest focusable in the same scope to the right of this widget.
1356    pub fn focusable_right(&self) -> Option<WidgetFocusInfo> {
1357        self.directional_next(Orientation2D::Right)
1358    }
1359
1360    /// Widget to focus when pressing the arrow up key from this widget.
1361    pub fn next_up(&self) -> Option<WidgetFocusInfo> {
1362        let _span = tracing::trace_span!("next_up").entered();
1363        self.next_up_from(self.info.inner_bounds().to_box2d())
1364    }
1365    fn next_up_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1366        if let Some(scope) = self.scope() {
1367            let scope_info = scope.focus_info();
1368            match scope_info.directional_nav() {
1369                DirectionalNav::None => None,
1370                DirectionalNav::Continue => self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
1371                    let mut from = scope.info.inner_bounds();
1372                    from.origin.y -= Px(1);
1373                    from.size.height = Px(1);
1374                    scope.next_up_from(from.to_box2d())
1375                }),
1376                DirectionalNav::Contained => self.directional_next_from(Orientation2D::Above, origin),
1377                DirectionalNav::Cycle => {
1378                    self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
1379                        // next up from the same X but from the bottom segment of scope spatial bounds.
1380                        let mut from_pt = origin.center();
1381                        from_pt.y = scope.info.spatial_bounds().max.y;
1382                        self.directional_from(
1383                            &scope,
1384                            PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1385                            Orientation2D::Above,
1386                            false,
1387                            false,
1388                        )
1389                    })
1390                }
1391            }
1392        } else {
1393            None
1394        }
1395    }
1396
1397    /// Widget to focus when pressing the arrow right key from this widget.
1398    pub fn next_right(&self) -> Option<WidgetFocusInfo> {
1399        let _span = tracing::trace_span!("next_right").entered();
1400        self.next_right_from(self.info.inner_bounds().to_box2d())
1401    }
1402    fn next_right_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1403        if let Some(scope) = self.scope() {
1404            let scope_info = scope.focus_info();
1405            match scope_info.directional_nav() {
1406                DirectionalNav::None => None,
1407                DirectionalNav::Continue => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
1408                    let mut from = scope.info.inner_bounds();
1409                    from.origin.x += from.size.width + Px(1);
1410                    from.size.width = Px(1);
1411                    scope.next_right_from(from.to_box2d())
1412                }),
1413                DirectionalNav::Contained => self.directional_next_from(Orientation2D::Right, origin),
1414                DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
1415                    // next right from the same Y but from the left segment of scope spatial bounds.
1416                    let mut from_pt = origin.center();
1417                    from_pt.x = scope.info.spatial_bounds().min.x;
1418                    self.directional_from(
1419                        &scope,
1420                        PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1421                        Orientation2D::Right,
1422                        false,
1423                        false,
1424                    )
1425                }),
1426            }
1427        } else {
1428            None
1429        }
1430    }
1431
1432    /// Widget to focus when pressing the arrow down key from this widget.
1433    pub fn next_down(&self) -> Option<WidgetFocusInfo> {
1434        let _span = tracing::trace_span!("next_down").entered();
1435        self.next_down_from(self.info.inner_bounds().to_box2d())
1436    }
1437    fn next_down_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1438        if let Some(scope) = self.scope() {
1439            let scope_info = scope.focus_info();
1440            match scope_info.directional_nav() {
1441                DirectionalNav::None => None,
1442                DirectionalNav::Continue => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
1443                    let mut from = scope.info.inner_bounds();
1444                    from.origin.y += from.size.height + Px(1);
1445                    from.size.height = Px(1);
1446                    scope.next_down_from(from.to_box2d())
1447                }),
1448                DirectionalNav::Contained => self.directional_next_from(Orientation2D::Below, origin),
1449                DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
1450                    // next down from the same X but from the top segment of scope spatial bounds.
1451                    let mut from_pt = origin.center();
1452                    from_pt.y = scope.info.spatial_bounds().min.y;
1453                    self.directional_from(
1454                        &scope,
1455                        PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1456                        Orientation2D::Below,
1457                        false,
1458                        false,
1459                    )
1460                }),
1461            }
1462        } else {
1463            None
1464        }
1465    }
1466
1467    /// Widget to focus when pressing the arrow left key from this widget.
1468    pub fn next_left(&self) -> Option<WidgetFocusInfo> {
1469        let _span = tracing::trace_span!("next_left").entered();
1470        self.next_left_from(self.info.inner_bounds().to_box2d())
1471    }
1472    fn next_left_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1473        if let Some(scope) = self.scope() {
1474            let scope_info = scope.focus_info();
1475            match scope_info.directional_nav() {
1476                DirectionalNav::None => None,
1477                DirectionalNav::Continue => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
1478                    let mut from = scope.info.inner_bounds();
1479                    from.origin.x -= Px(1);
1480                    from.size.width = Px(1);
1481                    scope.next_left_from(from.to_box2d())
1482                }),
1483                DirectionalNav::Contained => self.directional_next_from(Orientation2D::Left, origin),
1484                DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
1485                    // next left from the same Y but from the right segment of scope spatial bounds.
1486                    let mut from_pt = origin.center();
1487                    from_pt.x = scope.info.spatial_bounds().max.x;
1488                    self.directional_from(
1489                        &scope,
1490                        PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1491                        Orientation2D::Left,
1492                        false,
1493                        false,
1494                    )
1495                }),
1496            }
1497        } else {
1498            None
1499        }
1500    }
1501
1502    fn enabled_tab_nav(
1503        &self,
1504        scope: &WidgetFocusInfo,
1505        scope_info: FocusInfo,
1506        skip_self: bool,
1507        already_found: FocusNavAction,
1508    ) -> FocusNavAction {
1509        match scope_info.tab_nav() {
1510            TabNav::None => FocusNavAction::empty(),
1511            tab_nav @ (TabNav::Continue | TabNav::Contained) => {
1512                let mut nav = already_found;
1513
1514                if !nav.contains(FocusNavAction::PREV) && self.prev_tab_focusable_impl(skip_self, true).is_some() {
1515                    nav |= FocusNavAction::PREV;
1516                }
1517                if !nav.contains(FocusNavAction::NEXT) && self.next_tab_focusable_impl(skip_self, true).is_some() {
1518                    nav |= FocusNavAction::NEXT;
1519                }
1520
1521                if !nav.contains(FocusNavAction::PREV | FocusNavAction::NEXT)
1522                    && tab_nav == TabNav::Continue
1523                    && let Some(p_scope) = scope.scope()
1524                {
1525                    nav |= scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, nav)
1526                }
1527                nav
1528            }
1529            TabNav::Cycle => {
1530                if scope.descendants().tree_filter(Self::filter_tab_skip).any(|w| &w != self) {
1531                    FocusNavAction::PREV | FocusNavAction::NEXT
1532                } else {
1533                    FocusNavAction::empty()
1534                }
1535            }
1536            TabNav::Once => {
1537                if let Some(p_scope) = scope.scope() {
1538                    scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, already_found)
1539                } else {
1540                    FocusNavAction::empty()
1541                }
1542            }
1543        }
1544    }
1545
1546    fn enabled_directional_nav(
1547        &self,
1548        scope: &WidgetFocusInfo,
1549        scope_info: FocusInfo,
1550        skip_self: bool,
1551        already_found: FocusNavAction,
1552    ) -> FocusNavAction {
1553        let directional_nav = scope_info.directional_nav();
1554
1555        if directional_nav == DirectionalNav::None {
1556            return FocusNavAction::empty();
1557        }
1558
1559        let mut nav = already_found;
1560        let from_pt = self.info.inner_bounds().to_box2d();
1561
1562        if !nav.contains(FocusNavAction::UP)
1563            && self
1564                .directional_from(scope, from_pt, Orientation2D::Above, skip_self, true)
1565                .is_some()
1566        {
1567            nav |= FocusNavAction::UP;
1568        }
1569        if !nav.contains(FocusNavAction::RIGHT)
1570            && self
1571                .directional_from(scope, from_pt, Orientation2D::Right, skip_self, true)
1572                .is_some()
1573        {
1574            nav |= FocusNavAction::RIGHT;
1575        }
1576        if !nav.contains(FocusNavAction::DOWN)
1577            && self
1578                .directional_from(scope, from_pt, Orientation2D::Below, skip_self, true)
1579                .is_some()
1580        {
1581            nav |= FocusNavAction::DOWN;
1582        }
1583        if !nav.contains(FocusNavAction::LEFT)
1584            && self
1585                .directional_from(scope, from_pt, Orientation2D::Left, skip_self, true)
1586                .is_some()
1587        {
1588            nav |= FocusNavAction::LEFT;
1589        }
1590
1591        if !nav.contains(FocusNavAction::DIRECTIONAL) {
1592            match directional_nav {
1593                DirectionalNav::Continue => {
1594                    if let Some(p_scope) = scope.scope() {
1595                        nav |= scope.enabled_directional_nav(&p_scope, p_scope.focus_info(), true, nav);
1596                    }
1597                }
1598                DirectionalNav::Cycle => {
1599                    let scope_bounds = scope.info.inner_bounds();
1600                    if !nav.contains(FocusNavAction::UP) {
1601                        let mut from_pt = from_pt.center();
1602                        from_pt.y = scope_bounds.max().y;
1603                        if self
1604                            .directional_from(
1605                                scope,
1606                                PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1607                                Orientation2D::Above,
1608                                true,
1609                                true,
1610                            )
1611                            .is_some()
1612                        {
1613                            nav |= FocusNavAction::UP;
1614                        }
1615                    }
1616                    if !nav.contains(FocusNavAction::RIGHT) {
1617                        let mut from_pt = from_pt.center();
1618                        from_pt.x = scope_bounds.min().x;
1619                        if self
1620                            .directional_from(
1621                                scope,
1622                                PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1623                                Orientation2D::Right,
1624                                true,
1625                                true,
1626                            )
1627                            .is_some()
1628                        {
1629                            nav |= FocusNavAction::RIGHT;
1630                        }
1631                    }
1632                    if !nav.contains(FocusNavAction::DOWN) {
1633                        let mut from_pt = from_pt.center();
1634                        from_pt.y = scope_bounds.min().y;
1635                        if self
1636                            .directional_from(
1637                                scope,
1638                                PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1639                                Orientation2D::Below,
1640                                true,
1641                                true,
1642                            )
1643                            .is_some()
1644                        {
1645                            nav |= FocusNavAction::DOWN;
1646                        }
1647                    }
1648                    if !nav.contains(FocusNavAction::LEFT) {
1649                        let mut from_pt = from_pt.center();
1650                        from_pt.x = scope_bounds.max().x;
1651                        if self
1652                            .directional_from(
1653                                scope,
1654                                PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1655                                Orientation2D::Left,
1656                                true,
1657                                true,
1658                            )
1659                            .is_some()
1660                        {
1661                            nav |= FocusNavAction::LEFT;
1662                        }
1663                    }
1664
1665                    if !nav.contains(FocusNavAction::DIRECTIONAL) {
1666                        let info = self.focus_info();
1667
1668                        if info.is_scope() && matches!(info.directional_nav(), DirectionalNav::Continue) {
1669                            // continue scope as single child of cycle scope.
1670                            if nav.contains(FocusNavAction::UP) || nav.contains(FocusNavAction::DOWN) {
1671                                nav |= FocusNavAction::UP | FocusNavAction::DOWN;
1672                            }
1673                            if nav.contains(FocusNavAction::LEFT) || nav.contains(FocusNavAction::RIGHT) {
1674                                nav |= FocusNavAction::LEFT | FocusNavAction::RIGHT;
1675                            }
1676                        }
1677                    }
1678                }
1679                _ => {}
1680            }
1681        }
1682
1683        nav
1684    }
1685
1686    /// Focus navigation actions that can move the focus away from this item.
1687    pub fn enabled_nav(&self) -> FocusNavAction {
1688        let _span = tracing::trace_span!("enabled_nav").entered();
1689
1690        let mut nav = FocusNavAction::empty();
1691
1692        if let Some(scope) = self.scope() {
1693            nav |= FocusNavAction::EXIT;
1694            nav.set(FocusNavAction::ENTER, self.descendants().next().is_some());
1695
1696            let scope_info = scope.focus_info();
1697
1698            nav |= self.enabled_tab_nav(&scope, scope_info, false, FocusNavAction::empty());
1699            nav |= self.enabled_directional_nav(&scope, scope_info, false, FocusNavAction::empty());
1700        }
1701
1702        nav.set(FocusNavAction::ALT, self.in_alt_scope() || self.alt_scope().is_some());
1703
1704        nav
1705    }
1706}
1707impl_from_and_into_var! {
1708    fn from(focus_info: WidgetFocusInfo) -> WidgetInfo {
1709        focus_info.info
1710    }
1711}
1712
1713/// Focus metadata associated with a widget info tree.
1714#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
1715pub enum FocusInfo {
1716    /// The widget is not focusable.
1717    NotFocusable,
1718    /// The widget is focusable as a single item.
1719    Focusable {
1720        /// Tab index of the widget.
1721        tab_index: TabIndex,
1722        /// If the widget is skipped during directional navigation from outside.
1723        skip_directional: bool,
1724    },
1725    /// The widget is a focusable focus scope.
1726    FocusScope {
1727        /// Tab index of the widget.
1728        tab_index: TabIndex,
1729        /// If the widget is skipped during directional navigation from outside.
1730        skip_directional: bool,
1731        /// Tab navigation inside the focus scope.
1732        tab_nav: TabNav,
1733        /// Directional navigation inside the focus scope.
1734        directional_nav: DirectionalNav,
1735        /// Behavior of the widget when receiving direct focus.
1736        on_focus: FocusScopeOnFocus,
1737        /// If this scope is focused when the ALT key is pressed.
1738        alt: bool,
1739    },
1740}
1741
1742/// Behavior of a focus scope when it receives direct focus.
1743#[derive(Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
1744pub enum FocusScopeOnFocus {
1745    /// Just focus the scope widget.
1746    Widget,
1747    /// Focus the first descendant considering the TAB index, if the scope has no descendants
1748    /// behaves like [`Widget`].
1749    ///
1750    /// Focus the last descendant if the focus is *reversing* in, e.g. in a SHIFT+TAB action.
1751    ///
1752    /// Behaves like [`Widget`] if the first(or last) descendant inner-bounds is not fully contained
1753    /// by the scope inner-bounds.
1754    ///
1755    /// [`Widget`]: Self::Widget
1756    FirstDescendant,
1757    /// Focus the descendant that was last focused before focus moved out of the scope. If the
1758    /// scope cannot return focus, behaves like [`FirstDescendant`].
1759    ///
1760    /// If the scope is the only child of a parent that is `TabNav::Cycle` and the focus just exited and
1761    /// returned in a cycle action, behaves like [`FirstDescendant`].
1762    ///
1763    /// Behaves like [`Widget`] if the first(or last) descendant inner-bounds is not fully contained
1764    /// by the scope inner-bounds.
1765    ///
1766    /// [`Widget`]: Self::Widget
1767    /// [`FirstDescendant`]: Self::FirstDescendant
1768    LastFocused,
1769
1770    /// Like [`FirstDescendant`], but also focus the descendant even if it's inner-bounds
1771    /// is not fully contained by the scope inner-bounds.
1772    ///
1773    /// The expectation is that the descendant is already visible or will be made visible when
1774    /// it receives focus, a scroll scope will scroll to make the descendant visible for example.
1775    ///
1776    /// [`FirstDescendant`]: Self::FirstDescendant
1777    FirstDescendantIgnoreBounds,
1778
1779    /// Like [`LastFocused`], but also focus the descendant even if it's inner-bounds
1780    /// is not fully contained by the scope inner-bounds.
1781    ///
1782    /// The expectation is that the descendant is already visible or will be made visible when
1783    /// it receives focus, a scroll scope will scroll to make the descendant visible for example.
1784    ///
1785    /// [`LastFocused`]: Self::LastFocused
1786    LastFocusedIgnoreBounds,
1787}
1788impl fmt::Debug for FocusScopeOnFocus {
1789    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1790        if f.alternate() {
1791            write!(f, "FocusScopeOnFocus::")?;
1792        }
1793        match self {
1794            FocusScopeOnFocus::Widget => write!(f, "Widget"),
1795            FocusScopeOnFocus::FirstDescendant => write!(f, "FirstDescendant"),
1796            FocusScopeOnFocus::LastFocused => write!(f, "LastFocused"),
1797            FocusScopeOnFocus::FirstDescendantIgnoreBounds => write!(f, "FirstDescendantIgnoreBounds"),
1798            FocusScopeOnFocus::LastFocusedIgnoreBounds => write!(f, "LastFocusedIgnoreBounds"),
1799        }
1800    }
1801}
1802impl Default for FocusScopeOnFocus {
1803    /// [`FirstDescendant`](Self::FirstDescendant)
1804    fn default() -> Self {
1805        FocusScopeOnFocus::FirstDescendant
1806    }
1807}
1808
1809impl FocusInfo {
1810    /// If is focusable or a focus scope.
1811    pub fn is_focusable(self) -> bool {
1812        !matches!(self, FocusInfo::NotFocusable)
1813    }
1814
1815    /// If is a focus scope.
1816    pub fn is_scope(self) -> bool {
1817        matches!(self, FocusInfo::FocusScope { .. })
1818    }
1819
1820    /// If is an ALT focus scope.
1821    pub fn is_alt_scope(self) -> bool {
1822        match self {
1823            FocusInfo::FocusScope { alt, .. } => alt,
1824            _ => false,
1825        }
1826    }
1827
1828    /// Tab navigation mode.
1829    ///
1830    /// | Variant                   | Returns                                 |
1831    /// |---------------------------|-----------------------------------------|
1832    /// | Focus scope               | Associated value, default is `Continue` |
1833    /// | Focusable                 | `TabNav::Continue`                      |
1834    /// | Not-Focusable             | `TabNav::None`                          |
1835    pub fn tab_nav(self) -> TabNav {
1836        match self {
1837            FocusInfo::FocusScope { tab_nav, .. } => tab_nav,
1838            FocusInfo::Focusable { .. } => TabNav::Continue,
1839            FocusInfo::NotFocusable => TabNav::None,
1840        }
1841    }
1842
1843    /// Directional navigation mode.
1844    ///
1845    /// | Variant                   | Returns                             |
1846    /// |---------------------------|-------------------------------------|
1847    /// | Focus scope               | Associated value, default is `None` |
1848    /// | Focusable                 | `DirectionalNav::Continue`          |
1849    /// | Not-Focusable             | `DirectionalNav::None`              |
1850    pub fn directional_nav(self) -> DirectionalNav {
1851        match self {
1852            FocusInfo::FocusScope { directional_nav, .. } => directional_nav,
1853            FocusInfo::Focusable { .. } => DirectionalNav::Continue,
1854            FocusInfo::NotFocusable => DirectionalNav::None,
1855        }
1856    }
1857
1858    /// Tab navigation index.
1859    ///
1860    /// | Variant           | Returns                                       |
1861    /// |-------------------|-----------------------------------------------|
1862    /// | Focusable & Scope | Associated value, default is `TabIndex::AUTO` |
1863    /// | Not-Focusable     | `TabIndex::SKIP`                              |
1864    pub fn tab_index(self) -> TabIndex {
1865        match self {
1866            FocusInfo::Focusable { tab_index, .. } => tab_index,
1867            FocusInfo::FocusScope { tab_index, .. } => tab_index,
1868            FocusInfo::NotFocusable => TabIndex::SKIP,
1869        }
1870    }
1871
1872    /// If directional navigation skips over this widget.
1873    ///
1874    /// | Variant           | Returns                                       |
1875    /// |-------------------|-----------------------------------------------|
1876    /// | Focusable & Scope | Associated value, default is `false`          |
1877    /// | Not-Focusable     | `true`                                        |
1878    pub fn skip_directional(self) -> bool {
1879        match self {
1880            FocusInfo::Focusable { skip_directional, .. } => skip_directional,
1881            FocusInfo::FocusScope { skip_directional, .. } => skip_directional,
1882            FocusInfo::NotFocusable => true,
1883        }
1884    }
1885
1886    /// Focus scope behavior when it receives direct focus.
1887    ///
1888    /// | Variant                   | Returns                                                           |
1889    /// |---------------------------|-------------------------------------------------------------------|
1890    /// | Scope                     | Associated value, default is `FocusScopeOnFocus::FirstDescendant` |
1891    /// | Focusable & Not-Focusable | `FocusScopeOnFocus::Self_`                                        |
1892    pub fn scope_on_focus(self) -> FocusScopeOnFocus {
1893        match self {
1894            FocusInfo::FocusScope { on_focus, .. } => on_focus,
1895            _ => FocusScopeOnFocus::Widget,
1896        }
1897    }
1898}
1899
1900static_id! {
1901    static ref FOCUS_INFO_ID: StateId<FocusInfoData>;
1902    static ref FOCUS_TREE_ID: StateId<FocusTreeData>;
1903}
1904
1905#[derive(Default)]
1906pub(super) struct FocusTreeData {
1907    alt_scopes: Mutex<IdSet<WidgetId>>,
1908}
1909impl FocusTreeData {
1910    pub(super) fn consolidate_alt_scopes(prev_tree: &WeakWidgetInfoTree, new_tree: &WidgetInfoTree) {
1911        // reused widgets don't insert build-meta, so we add the previous ALT scopes and validate everything.
1912
1913        let prev = prev_tree
1914            .upgrade()
1915            .and_then(|t| t.build_meta().get(*FOCUS_TREE_ID).map(|d| d.alt_scopes.lock().clone()))
1916            .unwrap_or_default();
1917
1918        let mut alt_scopes = prev;
1919        if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
1920            alt_scopes.extend(data.alt_scopes.lock().iter());
1921        }
1922
1923        alt_scopes.retain(|id| {
1924            if let Some(wgt) = new_tree.get(*id)
1925                && let Some(info) = wgt.meta().get(*FOCUS_INFO_ID)
1926                && info.build().is_alt_scope()
1927            {
1928                for parent in wgt.ancestors() {
1929                    if let Some(info) = parent.meta().get(*FOCUS_INFO_ID)
1930                        && info.build().is_scope()
1931                    {
1932                        info.inner_alt.store(Some(*id), Relaxed);
1933                        break;
1934                    }
1935                }
1936
1937                return true;
1938            }
1939            false
1940        });
1941
1942        if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
1943            *data.alt_scopes.lock() = alt_scopes;
1944        }
1945    }
1946}
1947
1948#[derive(Default, Debug)]
1949struct FocusInfoData {
1950    focusable: Option<bool>,
1951    scope: Option<bool>,
1952    alt_scope: bool,
1953    on_focus: FocusScopeOnFocus,
1954    tab_index: Option<TabIndex>,
1955    tab_nav: Option<TabNav>,
1956    directional_nav: Option<DirectionalNav>,
1957    skip_directional: Option<bool>,
1958
1959    inner_alt: Atomic<Option<WidgetId>>,
1960
1961    access_handler_registered: bool,
1962}
1963impl FocusInfoData {
1964    /// Build a [`FocusInfo`] from the collected configuration in `self`.
1965    ///
1966    /// See [`FocusInfoBuilder`] for a review of the algorithm.
1967    pub fn build(&self) -> FocusInfo {
1968        match (self.focusable, self.scope, self.tab_index, self.tab_nav, self.directional_nav) {
1969            // Set as not focusable.
1970            (Some(false), _, _, _, _) => FocusInfo::NotFocusable,
1971
1972            // Set as focus scope and not set as not focusable
1973            // or set tab navigation and did not set as not focus scope
1974            // or set directional navigation and did not set as not focus scope.
1975            (_, Some(true), idx, tab, dir) | (_, None, idx, tab @ Some(_), dir) | (_, None, idx, tab, dir @ Some(_)) => {
1976                FocusInfo::FocusScope {
1977                    tab_index: idx.unwrap_or(TabIndex::AUTO),
1978                    skip_directional: self.skip_directional.unwrap_or_default(),
1979                    tab_nav: tab.unwrap_or(TabNav::Continue),
1980                    directional_nav: dir.unwrap_or(DirectionalNav::Continue),
1981                    alt: self.alt_scope,
1982                    on_focus: self.on_focus,
1983                }
1984            }
1985
1986            // Set as focusable and was not focus scope
1987            // or set tab index and was not focus scope and did not set as not focusable.
1988            (Some(true), _, idx, _, _) | (_, _, idx @ Some(_), _, _) => FocusInfo::Focusable {
1989                tab_index: idx.unwrap_or(TabIndex::AUTO),
1990                skip_directional: self.skip_directional.unwrap_or_default(),
1991            },
1992
1993            _ => FocusInfo::NotFocusable,
1994        }
1995    }
1996}
1997
1998/// Builder for [`FocusInfo`] accessible in a [`WidgetInfoBuilder`].
1999///
2000/// There are multiple focusable metadata that can be set on a widget. These rules define how the focusable
2001/// state of a widget is derived from the focusable metadata.
2002///
2003/// ### Rules
2004///
2005/// The widget is not focusable nor a focus scope if it set [`focusable`](Self::focusable) to `false`.
2006///
2007/// The widget is a *focus scope* if it set [`scope`](Self::scope) to `true` **or** if it set [`tab_nav`](Self::tab_nav) or
2008/// [`directional_nav`](Self::directional_nav) and did not set [`scope`](Self::scope) to `false`.
2009///
2010/// The widget is *focusable* if it set [`focusable`](Self::focusable) to `true` **or** if it set the [`tab_index`](Self::tab_index).
2011///
2012/// The widget is a *focus scope* if it sets [`nested_window`](NestedWindowWidgetInfoExt::nested_window), but the focus will always move inside
2013/// the nested window.
2014///
2015/// The widget is not focusable if it did not set any of the members mentioned.
2016///
2017/// ##### Tab Index
2018///
2019/// If the [`tab_index`](Self::tab_index) was not set but the widget is focusable or a focus scope, the [`TabIndex::AUTO`]
2020/// is used for the widget.
2021///
2022/// ##### Skip Directional
2023///
2024/// If the [`skip_directional`](Self::skip_directional) was not set but the widget is focusable or a focus scope, it is
2025/// set to `false` for the widget.
2026///
2027/// ##### Focus Scope
2028///
2029/// If the widget is a focus scope, it is configured using [`alt_scope`](Self::alt_scope) and [`on_focus`](Self::on_focus).
2030/// If the widget is not a scope these members are ignored.
2031///
2032/// ##### Tab Navigation
2033///
2034/// If [`tab_nav`](Self::tab_nav) is not set but the widget is a focus scope, [`TabNav::Continue`] is used.
2035///
2036/// ##### Directional Navigation
2037///
2038/// If [`directional_nav`](Self::directional_nav) is not set but the widget is a focus scope, [`DirectionalNav::Continue`] is used.
2039///
2040/// [`WidgetInfoBuilder`]: zng_app::widget::info::WidgetInfoBuilder
2041/// [`new`]: Self::new
2042pub struct FocusInfoBuilder<'a>(&'a mut WidgetInfoBuilder);
2043impl<'a> FocusInfoBuilder<'a> {
2044    /// New the builder.
2045    pub fn new(builder: &'a mut WidgetInfoBuilder) -> Self {
2046        let mut r = Self(builder);
2047        r.with_tree_data(|_| {}); // ensure that build meta is allocated.
2048        r
2049    }
2050
2051    fn with_data<R>(&mut self, visitor: impl FnOnce(&mut FocusInfoData) -> R) -> R {
2052        let mut access = self.0.access().is_some();
2053
2054        let r = self.0.with_meta(|m| {
2055            let data = m.into_entry(*FOCUS_INFO_ID).or_default();
2056
2057            if access {
2058                access = !std::mem::replace(&mut data.access_handler_registered, true);
2059            }
2060
2061            visitor(data)
2062        });
2063
2064        if access {
2065            // access info required and not registered
2066            self.0.access().unwrap().on_access_build(|args| {
2067                if args.widget.info().clone().into_focusable(true, false).is_some() {
2068                    args.node.commands.push(zng_view_api::access::AccessCmdName::Focus);
2069                }
2070            });
2071        }
2072
2073        r
2074    }
2075
2076    fn with_tree_data<R>(&mut self, visitor: impl FnOnce(&mut FocusTreeData) -> R) -> R {
2077        self.0.with_build_meta(|m| visitor(m.into_entry(*FOCUS_TREE_ID).or_default()))
2078    }
2079
2080    /// If the widget is definitely focusable or not.
2081    pub fn focusable(&mut self, is_focusable: bool) -> &mut Self {
2082        self.with_data(|data| {
2083            data.focusable = Some(is_focusable);
2084        });
2085        self
2086    }
2087
2088    /// Sets [`focusable`], only if it was not already set.
2089    ///
2090    /// [`focusable`]: Self::focusable
2091    pub fn focusable_passive(&mut self, is_focusable: bool) -> &mut Self {
2092        self.with_data(|data| {
2093            if data.focusable.is_none() {
2094                data.focusable = Some(is_focusable);
2095            }
2096        });
2097        self
2098    }
2099
2100    /// If the widget is definitely a focus scope or not.
2101    pub fn scope(&mut self, is_focus_scope: bool) -> &mut Self {
2102        self.with_data(|data| {
2103            data.scope = Some(is_focus_scope);
2104        });
2105        self
2106    }
2107
2108    /// If the widget is definitely an ALT focus scope or not.
2109    ///
2110    /// If `true` this also sets `TabIndex::SKIP`, `skip_directional_nav`, `TabNav::Cycle` and `DirectionalNav::Cycle` as default.
2111    pub fn alt_scope(&mut self, is_alt_focus_scope: bool) -> &mut Self {
2112        self.with_data(|data| {
2113            data.alt_scope = is_alt_focus_scope;
2114            if is_alt_focus_scope {
2115                data.scope = Some(true);
2116
2117                if data.tab_index.is_none() {
2118                    data.tab_index = Some(TabIndex::SKIP);
2119                }
2120                if data.tab_nav.is_none() {
2121                    data.tab_nav = Some(TabNav::Cycle);
2122                }
2123                if data.directional_nav.is_none() {
2124                    data.directional_nav = Some(DirectionalNav::Cycle);
2125                }
2126                if data.skip_directional.is_none() {
2127                    data.skip_directional = Some(true);
2128                }
2129            }
2130        });
2131        if is_alt_focus_scope {
2132            let wgt_id = self.0.widget_id();
2133            self.with_tree_data(|d| d.alt_scopes.lock().insert(wgt_id));
2134        }
2135        self
2136    }
2137
2138    /// When the widget is a focus scope, its behavior on receiving direct focus.
2139    pub fn on_focus(&mut self, as_focus_scope_on_focus: FocusScopeOnFocus) -> &mut Self {
2140        self.with_data(|data| {
2141            data.on_focus = as_focus_scope_on_focus;
2142        });
2143        self
2144    }
2145
2146    /// Widget TAB index.
2147    pub fn tab_index(&mut self, tab_index: TabIndex) -> &mut Self {
2148        self.with_data(|data| {
2149            data.tab_index = Some(tab_index);
2150        });
2151        self
2152    }
2153
2154    /// TAB navigation within this widget, if set turns the widget into a focus scope.
2155    pub fn tab_nav(&mut self, scope_tab_nav: TabNav) -> &mut Self {
2156        self.with_data(|data| {
2157            data.tab_nav = Some(scope_tab_nav);
2158        });
2159        self
2160    }
2161
2162    /// Directional navigation within this widget, if set turns the widget into a focus scope.
2163    pub fn directional_nav(&mut self, scope_directional_nav: DirectionalNav) -> &mut Self {
2164        self.with_data(|data| {
2165            data.directional_nav = Some(scope_directional_nav);
2166        });
2167        self
2168    }
2169    /// If directional navigation skips over this widget.
2170    pub fn skip_directional(&mut self, skip: bool) -> &mut Self {
2171        self.with_data(|data| {
2172            data.skip_directional = Some(skip);
2173        });
2174        self
2175    }
2176}