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#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
28pub struct TabIndex(pub u32);
29impl TabIndex {
30 pub const SKIP: TabIndex = TabIndex(u32::MAX);
34
35 pub const AUTO: TabIndex = TabIndex(u32::MAX / 2);
42
43 pub const LAST: TabIndex = TabIndex(u32::MAX - 1);
45
46 pub const FIRST: TabIndex = TabIndex(0);
48
49 pub fn is_skip(self) -> bool {
51 self == Self::SKIP
52 }
53
54 pub fn is_auto(self) -> bool {
56 self == Self::AUTO
57 }
58
59 pub fn is_before_auto(self) -> bool {
61 self.0 < Self::AUTO.0
62 }
63
64 pub fn is_after_auto(self) -> bool {
66 self.0 > Self::AUTO.0
67 }
68
69 pub fn not_skip(index: u32) -> Self {
73 TabIndex(if index == Self::SKIP.0 { Self::SKIP.0 - 1 } else { index })
74 }
75
76 pub fn before_auto(index: u32) -> Self {
80 TabIndex(if index >= Self::AUTO.0 { Self::AUTO.0 - 1 } else { index })
81 }
82
83 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 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 fn default() -> Self {
121 TabIndex::AUTO
122 }
123}
124impl_from_and_into_var! {
125 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#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
202pub enum TabNav {
203 None,
205 Continue,
207 Contained,
209 Cycle,
211 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#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
233pub enum DirectionalNav {
234 None,
236 Continue,
238 Contained,
240 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#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
263pub struct FocusRequest {
264 pub target: FocusTarget,
266 pub highlight: bool,
268
269 pub force_window_focus: bool,
274
275 pub window_indicator: Option<FocusIndicator>,
278
279 pub fallback_only: bool,
281}
282
283impl FocusRequest {
284 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 pub fn direct(widget_id: WidgetId, highlight: bool) -> Self {
297 Self::new(FocusTarget::Direct { target: widget_id }, highlight)
298 }
299 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 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 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 pub fn enter(highlight: bool) -> Self {
331 Self::new(FocusTarget::Enter, highlight)
332 }
333 pub fn exit(recursive_alt: bool, highlight: bool) -> Self {
335 Self::new(FocusTarget::Exit { recursive_alt }, highlight)
336 }
337 pub fn next(highlight: bool) -> Self {
339 Self::new(FocusTarget::Next, highlight)
340 }
341 pub fn prev(highlight: bool) -> Self {
343 Self::new(FocusTarget::Prev, highlight)
344 }
345 pub fn up(highlight: bool) -> Self {
347 Self::new(FocusTarget::Up, highlight)
348 }
349 pub fn right(highlight: bool) -> Self {
351 Self::new(FocusTarget::Right, highlight)
352 }
353 pub fn down(highlight: bool) -> Self {
355 Self::new(FocusTarget::Down, highlight)
356 }
357 pub fn left(highlight: bool) -> Self {
359 Self::new(FocusTarget::Left, highlight)
360 }
361 pub fn alt(highlight: bool) -> Self {
363 Self::new(FocusTarget::Alt, highlight)
364 }
365
366 pub fn with_force_window_focus(mut self) -> Self {
368 self.force_window_focus = true;
369 self
370 }
371
372 pub fn with_indicator(mut self, indicator: FocusIndicator) -> Self {
374 self.window_indicator = Some(indicator);
375 self
376 }
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
381pub enum FocusTarget {
382 Direct {
384 target: WidgetId,
386 },
387 DirectOrExit {
389 target: WidgetId,
391 navigation_origin: bool,
395 },
396 DirectOrEnter {
398 target: WidgetId,
400 navigation_origin: bool,
405 },
406 DirectOrRelated {
409 target: WidgetId,
411 navigation_origin: bool,
416 },
417
418 Enter,
420 Exit {
422 recursive_alt: bool,
424 },
425
426 Next,
428 Prev,
430
431 Up,
433 Right,
435 Down,
437 Left,
439
440 Alt,
442}
443
444bitflags! {
445 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
447 pub struct FocusNavAction: u16 {
448 const ENTER = 0b0000_0000_0001;
450 const EXIT = 0b0000_0000_0010;
452
453 const NEXT = 0b0000_0000_0100;
455 const PREV = 0b0000_0000_1000;
457
458 const UP = 0b0000_0001_0000;
460 const RIGHT = 0b0000_0010_0000;
462 const DOWN = 0b0000_0100_0000;
464 const LEFT = 0b0000_1000_0000;
466
467 const ALT = 0b0001_0000_0000;
469
470 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 const DISABLED = 1;
481 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#[derive(Clone, Debug)]
498pub struct FocusInfoTree {
499 tree: WidgetInfoTree,
500 mode: FocusMode,
501}
502impl FocusInfoTree {
503 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 pub fn tree(&self) -> &WidgetInfoTree {
518 &self.tree
519 }
520
521 pub fn focus_disabled_widgets(&self) -> bool {
528 self.mode.contains(FocusMode::DISABLED)
529 }
530
531 pub fn focus_hidden_widgets(&self) -> bool {
538 self.mode.contains(FocusMode::HIDDEN)
539 }
540
541 pub fn root(&self) -> WidgetFocusInfo {
546 WidgetFocusInfo {
547 info: self.tree.root(),
548 mode: self.mode,
549 }
550 }
551
552 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 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 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 pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
591 self.get(widget_id).is_some()
592 }
593}
594
595pub trait WidgetInfoFocusExt {
599 fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo;
607 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#[derive(Clone, Eq, PartialEq, Hash, Debug)]
630pub struct WidgetFocusInfo {
631 info: WidgetInfo,
632 mode: FocusMode,
633}
634impl WidgetFocusInfo {
635 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 pub fn info(&self) -> &WidgetInfo {
650 &self.info
651 }
652
653 pub fn focus_disabled_widgets(&self) -> bool {
660 self.mode.contains(FocusMode::DISABLED)
661 }
662
663 pub fn focus_hidden_widgets(&self) -> bool {
670 self.mode.contains(FocusMode::HIDDEN)
671 }
672
673 pub fn root(&self) -> Self {
675 self.ancestors().last().unwrap_or_else(|| self.clone())
676 }
677
678 pub fn focus_tree(&self) -> FocusInfoTree {
680 FocusInfoTree {
681 tree: self.info.tree().clone(),
682 mode: self.mode,
683 }
684 }
685
686 pub fn is_focusable(&self) -> bool {
695 self.focus_info().is_focusable()
696 }
697
698 pub fn is_scope(&self) -> bool {
700 self.focus_info().is_scope()
701 }
702
703 pub fn is_alt_scope(&self) -> bool {
705 self.focus_info().is_alt_scope()
706 }
707
708 pub fn nested_window(&self) -> Option<WindowId> {
712 self.info.nested_window()
713 }
714
715 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 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 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 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 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 pub fn self_and_ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
801 [self.clone()].into_iter().chain(self.ancestors())
802 }
803
804 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 pub fn parent(&self) -> Option<WidgetFocusInfo> {
816 self.ancestors().next()
817 }
818
819 pub fn scope(&self) -> Option<WidgetFocusInfo> {
821 self.scopes().next()
822 }
823
824 pub fn alt_scope(&self) -> Option<WidgetFocusInfo> {
832 if self.in_alt_scope() {
833 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 let r = self.inner_alt_scope();
848 if r.is_some() {
849 return r;
850 }
851 }
852
853 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 pub fn in_alt_scope(&self) -> bool {
889 self.is_alt_scope() || self.scopes().any(|s| s.is_alt_scope())
890 }
891
892 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 } FocusScopeOnFocus::Widget => None,
935 };
936
937 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 return None;
944 }
945
946 candidate
947 }
948 FocusInfo::NotFocusable | FocusInfo::Focusable { .. } => None,
949 }
950 }
951
952 pub fn descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
954 super::iter::FocusTreeIter::new(self.info.descendants(), self.mode)
955 }
956
957 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 pub fn has_tab_descendant(&self) -> bool {
964 self.descendants().tree_find(Self::filter_tab_skip).is_some()
965 }
966
967 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 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 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 super::iter::FocusTreeIter::new(self.info.next_siblings_in(&self.info), self.mode)
1004 }
1005 }
1006
1007 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 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 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 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 super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&self.info), self.mode)
1087 }
1088 }
1089
1090 pub fn prev_focusable(&self) -> Option<WidgetFocusInfo> {
1092 self.prev_focusables().next()
1093 }
1094
1095 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 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 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 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 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 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 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 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 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 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 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 pub fn focusable_up(&self) -> Option<WidgetFocusInfo> {
1338 self.directional_next(Orientation2D::Above)
1339 }
1340
1341 pub fn focusable_down(&self) -> Option<WidgetFocusInfo> {
1343 self.directional_next(Orientation2D::Below)
1344 }
1345
1346 pub fn focusable_left(&self) -> Option<WidgetFocusInfo> {
1348 self.directional_next(Orientation2D::Left)
1349 }
1350
1351 pub fn focusable_right(&self) -> Option<WidgetFocusInfo> {
1353 self.directional_next(Orientation2D::Right)
1354 }
1355
1356 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 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 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 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 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 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 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 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 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 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#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
1711pub enum FocusInfo {
1712 NotFocusable,
1714 Focusable {
1716 tab_index: TabIndex,
1718 skip_directional: bool,
1720 },
1721 FocusScope {
1723 tab_index: TabIndex,
1725 skip_directional: bool,
1727 tab_nav: TabNav,
1729 directional_nav: DirectionalNav,
1731 on_focus: FocusScopeOnFocus,
1733 alt: bool,
1735 },
1736}
1737
1738#[derive(Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
1740pub enum FocusScopeOnFocus {
1741 Widget,
1743 FirstDescendant,
1753 LastFocused,
1765
1766 FirstDescendantIgnoreBounds,
1774
1775 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 fn default() -> Self {
1801 FocusScopeOnFocus::FirstDescendant
1802 }
1803}
1804
1805impl FocusInfo {
1806 pub fn is_focusable(self) -> bool {
1808 !matches!(self, FocusInfo::NotFocusable)
1809 }
1810
1811 pub fn is_scope(self) -> bool {
1813 matches!(self, FocusInfo::FocusScope { .. })
1814 }
1815
1816 pub fn is_alt_scope(self) -> bool {
1818 match self {
1819 FocusInfo::FocusScope { alt, .. } => alt,
1820 _ => false,
1821 }
1822 }
1823
1824 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 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 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 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 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 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 pub fn build(&self) -> FocusInfo {
1964 match (self.focusable, self.scope, self.tab_index, self.tab_nav, self.directional_nav) {
1965 (Some(false), _, _, _, _) => FocusInfo::NotFocusable,
1967
1968 (_, 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 (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
1994pub struct FocusInfoBuilder<'a>(&'a mut WidgetInfoBuilder);
2039impl<'a> FocusInfoBuilder<'a> {
2040 pub fn new(builder: &'a mut WidgetInfoBuilder) -> Self {
2042 let mut r = Self(builder);
2043 r.with_tree_data(|_| {}); 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 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 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 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 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 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 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 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 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 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 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}