Skip to main content

zng_ext_input/focus/
focus_info.rs

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