1use std::fmt;
2use std::sync::atomic::Ordering::Relaxed;
3
4use atomic::Atomic;
5use parking_lot::Mutex;
6use zng_app::{
7 widget::{
8 WidgetId,
9 info::{TreeFilter, Visibility, WidgetInfo, WidgetInfoBuilder, WidgetInfoTree, WidgetPath},
10 },
11 window::WindowId,
12};
13use zng_ext_window::NestedWindowWidgetInfoExt;
14use zng_layout::unit::{DistanceKey, Orientation2D, Px, PxBox, PxPoint, PxRect, PxSize};
15use zng_state_map::{StateId, static_id};
16use zng_unique_id::IdSet;
17use zng_var::impl_from_and_into_var;
18use zng_view_api::window::FocusIndicator;
19
20use zng_app::widget::info::iter as w_iter;
21
22use super::iter::IterFocusableExt;
23
24#[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 fn is_skip(self) -> bool {
48 self == Self::SKIP
49 }
50
51 pub fn is_auto(self) -> bool {
53 self == Self::AUTO
54 }
55
56 pub fn is_before_auto(self) -> bool {
58 self.0 < Self::AUTO.0
59 }
60
61 pub fn is_after_auto(self) -> bool {
63 self.0 > Self::AUTO.0
64 }
65
66 pub fn not_skip(index: u32) -> Self {
70 TabIndex(if index == Self::SKIP.0 { Self::SKIP.0 - 1 } else { index })
71 }
72
73 pub fn before_auto(index: u32) -> Self {
77 TabIndex(if index >= Self::AUTO.0 { Self::AUTO.0 - 1 } else { index })
78 }
79
80 pub fn after_auto(index: u32) -> Self {
86 Self::not_skip((Self::AUTO.0 + 1).saturating_add(index))
87 }
88}
89impl fmt::Debug for TabIndex {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 if f.alternate() {
92 if self.is_auto() {
93 write!(f, "TabIndex::AUTO")
94 } else if self.is_skip() {
95 write!(f, "TabIndex::SKIP")
96 } else if self.is_after_auto() {
97 write!(f, "TabIndex::after_auto({})", self.0 - Self::AUTO.0 - 1)
98 } else {
99 write!(f, "TabIndex({})", self.0)
100 }
101 } else {
102 if self.is_auto() {
104 write!(f, "AUTO")
105 } else if self.is_skip() {
106 write!(f, "SKIP")
107 } else if self.is_after_auto() {
108 write!(f, "after_auto({})", self.0 - Self::AUTO.0 - 1)
109 } else {
110 write!(f, "{}", self.0)
111 }
112 }
113 }
114}
115impl Default for TabIndex {
116 fn default() -> Self {
118 TabIndex::AUTO
119 }
120}
121impl_from_and_into_var! {
122 fn from(index: u32) -> TabIndex {
124 TabIndex::not_skip(index)
125 }
126}
127#[derive(serde::Serialize, serde::Deserialize)]
128#[serde(untagged)]
129enum TabIndexSerde<'s> {
130 Named(&'s str),
131 Unnamed(u32),
132}
133impl serde::Serialize for TabIndex {
134 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135 where
136 S: serde::Serializer,
137 {
138 if serializer.is_human_readable() {
139 let name = if self.is_auto() {
140 Some("AUTO")
141 } else if self.is_skip() {
142 Some("SKIP")
143 } else {
144 None
145 };
146 if let Some(name) = name {
147 return TabIndexSerde::Named(name).serialize(serializer);
148 }
149 }
150 TabIndexSerde::Unnamed(self.0).serialize(serializer)
151 }
152}
153impl<'de> serde::Deserialize<'de> for TabIndex {
154 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
155 where
156 D: serde::Deserializer<'de>,
157 {
158 use serde::de::Error;
159
160 match TabIndexSerde::deserialize(deserializer)? {
161 TabIndexSerde::Named(name) => match name {
162 "AUTO" => Ok(TabIndex::AUTO),
163 "SKIP" => Ok(TabIndex::SKIP),
164 unknown => Err(D::Error::unknown_variant(unknown, &["AUTO", "SKIP"])),
165 },
166 TabIndexSerde::Unnamed(i) => Ok(TabIndex(i)),
167 }
168 }
169}
170
171#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
175pub enum TabNav {
176 None,
178 Continue,
180 Contained,
182 Cycle,
184 Once,
186}
187impl fmt::Debug for TabNav {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 if f.alternate() {
190 write!(f, "TabNav::")?;
191 }
192 match self {
193 TabNav::None => write!(f, "None"),
194 TabNav::Continue => write!(f, "Continue"),
195 TabNav::Contained => write!(f, "Contained"),
196 TabNav::Cycle => write!(f, "Cycle"),
197 TabNav::Once => write!(f, "Once"),
198 }
199 }
200}
201
202#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
206pub enum DirectionalNav {
207 None,
209 Continue,
211 Contained,
213 Cycle,
215}
216impl fmt::Debug for DirectionalNav {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 if f.alternate() {
219 write!(f, "DirectionalNav::")?;
220 }
221 match self {
222 DirectionalNav::None => write!(f, "None"),
223 DirectionalNav::Continue => write!(f, "Continue"),
224 DirectionalNav::Contained => write!(f, "Contained"),
225 DirectionalNav::Cycle => write!(f, "Cycle"),
226 }
227 }
228}
229
230#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
236pub struct FocusRequest {
237 pub target: FocusTarget,
239 pub highlight: bool,
241
242 pub force_window_focus: bool,
249
250 pub window_indicator: Option<FocusIndicator>,
258}
259
260impl FocusRequest {
261 pub fn new(target: FocusTarget, highlight: bool) -> Self {
263 Self {
264 target,
265 highlight,
266 force_window_focus: false,
267 window_indicator: None,
268 }
269 }
270
271 pub fn direct(widget_id: WidgetId, highlight: bool) -> Self {
273 Self::new(FocusTarget::Direct { target: widget_id }, highlight)
274 }
275 pub fn direct_or_exit(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
277 Self::new(
278 FocusTarget::DirectOrExit {
279 target: widget_id,
280 navigation_origin,
281 },
282 highlight,
283 )
284 }
285 pub fn direct_or_enter(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
287 Self::new(
288 FocusTarget::DirectOrEnter {
289 target: widget_id,
290 navigation_origin,
291 },
292 highlight,
293 )
294 }
295 pub fn direct_or_related(widget_id: WidgetId, navigation_origin: bool, highlight: bool) -> Self {
297 Self::new(
298 FocusTarget::DirectOrRelated {
299 target: widget_id,
300 navigation_origin,
301 },
302 highlight,
303 )
304 }
305 pub fn enter(highlight: bool) -> Self {
307 Self::new(FocusTarget::Enter, highlight)
308 }
309 pub fn exit(highlight: bool) -> Self {
311 Self::new(FocusTarget::Exit, highlight)
312 }
313 pub fn next(highlight: bool) -> Self {
315 Self::new(FocusTarget::Next, highlight)
316 }
317 pub fn prev(highlight: bool) -> Self {
319 Self::new(FocusTarget::Prev, highlight)
320 }
321 pub fn up(highlight: bool) -> Self {
323 Self::new(FocusTarget::Up, highlight)
324 }
325 pub fn right(highlight: bool) -> Self {
327 Self::new(FocusTarget::Right, highlight)
328 }
329 pub fn down(highlight: bool) -> Self {
331 Self::new(FocusTarget::Down, highlight)
332 }
333 pub fn left(highlight: bool) -> Self {
335 Self::new(FocusTarget::Left, highlight)
336 }
337 pub fn alt(highlight: bool) -> Self {
339 Self::new(FocusTarget::Alt, highlight)
340 }
341
342 pub fn with_force_window_focus(mut self) -> Self {
344 self.force_window_focus = true;
345 self
346 }
347
348 pub fn with_indicator(mut self, indicator: FocusIndicator) -> Self {
350 self.window_indicator = Some(indicator);
351 self
352 }
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
357pub enum FocusTarget {
358 Direct {
360 target: WidgetId,
362 },
363 DirectOrExit {
365 target: WidgetId,
367 navigation_origin: bool,
372 },
373 DirectOrEnter {
375 target: WidgetId,
377 navigation_origin: bool,
382 },
383 DirectOrRelated {
386 target: WidgetId,
388 navigation_origin: bool,
393 },
394
395 Enter,
397 Exit,
399
400 Next,
402 Prev,
404
405 Up,
407 Right,
409 Down,
411 Left,
413
414 Alt,
416}
417
418bitflags! {
419 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
421 pub struct FocusNavAction: u16 {
422 const ENTER = 0b0000_0000_0001;
424 const EXIT = 0b0000_0000_0010;
426
427 const NEXT = 0b0000_0000_0100;
429 const PREV = 0b0000_0000_1000;
431
432 const UP = 0b0000_0001_0000;
434 const RIGHT = 0b0000_0010_0000;
436 const DOWN = 0b0000_0100_0000;
438 const LEFT = 0b0000_1000_0000;
440
441 const ALT = 0b0001_0000_0000;
443
444 const DIRECTIONAL = FocusNavAction::UP.bits() | FocusNavAction::RIGHT.bits() | FocusNavAction::DOWN.bits() | FocusNavAction::LEFT.bits();
446 }
447}
448
449bitflags! {
450 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
451 pub(super) struct FocusMode: u8 {
452 const DISABLED = 1;
454 const HIDDEN = 2;
456 }
457}
458impl FocusMode {
459 pub fn new(focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
460 let mut mode = FocusMode::empty();
461 mode.set(FocusMode::DISABLED, focus_disabled_widgets);
462 mode.set(FocusMode::HIDDEN, focus_hidden_widgets);
463 mode
464 }
465}
466
467#[derive(Clone, Debug)]
471pub struct FocusInfoTree {
472 tree: WidgetInfoTree,
473 mode: FocusMode,
474}
475impl FocusInfoTree {
476 pub fn new(tree: WidgetInfoTree, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
483 FocusInfoTree {
484 tree,
485 mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
486 }
487 }
488
489 pub fn tree(&self) -> &WidgetInfoTree {
491 &self.tree
492 }
493
494 pub fn focus_disabled_widgets(&self) -> bool {
501 self.mode.contains(FocusMode::DISABLED)
502 }
503
504 pub fn focus_hidden_widgets(&self) -> bool {
511 self.mode.contains(FocusMode::HIDDEN)
512 }
513
514 pub fn root(&self) -> WidgetFocusInfo {
519 WidgetFocusInfo {
520 info: self.tree.root(),
521 mode: self.mode,
522 }
523 }
524
525 pub fn focusable_root(&self) -> Option<WidgetFocusInfo> {
530 let root = self.root();
531 if root.is_focusable() {
532 return Some(root);
533 }
534
535 let mut candidate = None;
536 let mut candidate_weight = usize::MAX;
537
538 for w in root.descendants().tree_filter(|_| TreeFilter::SkipDescendants) {
539 let weight = w.info.prev_siblings().count() + w.info.ancestors().count();
540 if weight < candidate_weight {
541 candidate = Some(w);
542 candidate_weight = weight;
543 }
544 }
545
546 candidate
547 }
548
549 pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetFocusInfo> {
551 self.tree
552 .get(widget_id)
553 .and_then(|i| i.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
554 }
555
556 pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetFocusInfo> {
558 self.get(path.widget_id())
559 .or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
560 }
561
562 pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
564 self.get(widget_id).is_some()
565 }
566}
567
568pub trait WidgetInfoFocusExt {
572 fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo;
580 fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo>;
588}
589impl WidgetInfoFocusExt for WidgetInfo {
590 fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo {
591 WidgetFocusInfo::new(self, focus_disabled_widgets, focus_hidden_widgets)
592 }
593 fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo> {
594 let r = self.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
595 if r.is_focusable() { Some(r) } else { None }
596 }
597}
598
599#[derive(Clone, Eq, PartialEq, Hash, Debug)]
603pub struct WidgetFocusInfo {
604 info: WidgetInfo,
605 mode: FocusMode,
606}
607impl WidgetFocusInfo {
608 pub fn new(widget_info: WidgetInfo, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
615 WidgetFocusInfo {
616 info: widget_info,
617 mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
618 }
619 }
620
621 pub fn info(&self) -> &WidgetInfo {
623 &self.info
624 }
625
626 pub fn focus_disabled_widgets(&self) -> bool {
633 self.mode.contains(FocusMode::DISABLED)
634 }
635
636 pub fn focus_hidden_widgets(&self) -> bool {
643 self.mode.contains(FocusMode::HIDDEN)
644 }
645
646 pub fn root(&self) -> Self {
648 self.ancestors().last().unwrap_or_else(|| self.clone())
649 }
650
651 pub fn focus_tree(&self) -> FocusInfoTree {
653 FocusInfoTree {
654 tree: self.info.tree().clone(),
655 mode: self.mode,
656 }
657 }
658
659 pub fn is_focusable(&self) -> bool {
668 self.focus_info().is_focusable()
669 }
670
671 pub fn is_scope(&self) -> bool {
673 self.focus_info().is_scope()
674 }
675
676 pub fn is_alt_scope(&self) -> bool {
678 self.focus_info().is_alt_scope()
679 }
680
681 pub fn nested_window(&self) -> Option<WindowId> {
685 self.info.nested_window()
686 }
687
688 pub fn nested_window_tree(&self) -> Option<FocusInfoTree> {
690 self.info
691 .nested_window_tree()
692 .map(|t| FocusInfoTree::new(t, self.focus_disabled_widgets(), self.focus_hidden_widgets()))
693 }
694
695 fn mode_allows_focus(&self) -> bool {
696 let int = self.info.interactivity();
697 if self.mode.contains(FocusMode::DISABLED) {
698 if int.is_blocked() {
699 return false;
700 }
701 } else if !int.is_enabled() {
702 return false;
703 }
704
705 let vis = self.info.visibility();
706 if self.mode.contains(FocusMode::HIDDEN) {
707 if vis == Visibility::Collapsed {
708 return false;
709 }
710 } else if vis != Visibility::Visible {
711 return false;
712 }
713
714 true
715 }
716
717 fn mode_allows_focus_ignore_blocked(&self) -> bool {
718 let int = self.info.interactivity();
719 if !self.mode.contains(FocusMode::DISABLED) && int.is_vis_disabled() {
720 return false;
721 }
722
723 let vis = self.info.visibility();
724 if self.mode.contains(FocusMode::HIDDEN) {
725 if vis == Visibility::Collapsed {
726 return false;
727 }
728 } else if vis != Visibility::Visible {
729 return false;
730 }
731
732 true
733 }
734
735 pub fn focus_info(&self) -> FocusInfo {
737 if self.mode_allows_focus() {
738 if let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID) {
739 return builder.build();
740 } else if self.info.nested_window().is_some() {
741 return FocusInfo::FocusScope {
743 tab_index: TabIndex::AUTO,
744 skip_directional: false,
745 tab_nav: TabNav::Contained,
746 directional_nav: DirectionalNav::Contained,
747 on_focus: FocusScopeOnFocus::FirstDescendant,
748 alt: false,
749 };
750 }
751 }
752 FocusInfo::NotFocusable
753 }
754
755 pub fn focus_info_ignore_blocked(&self) -> FocusInfo {
757 if self.mode_allows_focus_ignore_blocked() {
758 if let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID) {
759 return builder.build();
760 }
761 }
762 FocusInfo::NotFocusable
763 }
764
765 pub fn ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
767 let focus_disabled_widgets = self.focus_disabled_widgets();
768 let focus_hidden_widgets = self.focus_hidden_widgets();
769 self.info.ancestors().focusable(focus_disabled_widgets, focus_hidden_widgets)
770 }
771
772 pub fn self_and_ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
774 [self.clone()].into_iter().chain(self.ancestors())
775 }
776
777 pub fn scopes(&self) -> impl Iterator<Item = WidgetFocusInfo> {
779 let focus_disabled_widgets = self.focus_disabled_widgets();
780 let focus_hidden_widgets = self.focus_hidden_widgets();
781 self.info.ancestors().filter_map(move |i| {
782 let i = i.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
783 if i.is_scope() { Some(i) } else { None }
784 })
785 }
786
787 pub fn parent(&self) -> Option<WidgetFocusInfo> {
789 self.ancestors().next()
790 }
791
792 pub fn scope(&self) -> Option<WidgetFocusInfo> {
794 self.scopes().next()
795 }
796
797 pub fn alt_scope(&self) -> Option<WidgetFocusInfo> {
805 if self.in_alt_scope() {
806 let mut alt_scope = self.clone();
808 for scope in self.scopes() {
809 if scope.is_alt_scope() {
810 alt_scope = scope;
811 } else {
812 return scope.inner_alt_scope_skip(&alt_scope);
813 }
814 }
815 None
816 } else if self.is_scope() {
817 let r = self.inner_alt_scope();
819 if r.is_some() {
820 return r;
821 }
822 if let Some(scope) = self.scope() {
823 return scope.inner_alt_scope_skip(self);
825 }
826 None
827 } else if let Some(scope) = self.scope() {
828 if self.is_focusable() {
830 scope.inner_alt_scope_skip(self)
831 } else {
832 scope.inner_alt_scope()
833 }
834 } else {
835 None
837 }
838 }
839 fn inner_alt_scope(&self) -> Option<WidgetFocusInfo> {
840 let inner_alt = self.info.meta().get(*FOCUS_INFO_ID)?.inner_alt.load(Relaxed);
841 if let Some(id) = inner_alt {
842 if let Some(wgt) = self.info.tree().get(id) {
843 let wgt = wgt.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
844 if wgt.is_alt_scope() && wgt.info.is_descendant(&self.info) {
845 return Some(wgt);
846 }
847 }
848 }
849 None
850 }
851 fn inner_alt_scope_skip(self, skip: &WidgetFocusInfo) -> Option<WidgetFocusInfo> {
852 if let Some(alt) = self.inner_alt_scope() {
853 if !alt.info.is_descendant(&skip.info) && alt.info != skip.info {
854 return Some(alt);
855 }
856 }
857 None
858 }
859
860 pub fn in_alt_scope(&self) -> bool {
862 self.is_alt_scope() || self.scopes().any(|s| s.is_alt_scope())
863 }
864
865 pub fn on_focus_scope_move<'f>(
879 &self,
880 last_focused: impl FnOnce(WidgetId) -> Option<&'f WidgetPath>,
881 is_tab_cycle_reentry: bool,
882 reverse: bool,
883 ) -> Option<WidgetFocusInfo> {
884 match self.focus_info() {
885 FocusInfo::FocusScope { on_focus, .. } => {
886 let candidate = match on_focus {
887 FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::FirstDescendantIgnoreBounds => {
888 if reverse {
889 self.last_tab_descendant()
890 } else {
891 self.first_tab_descendant()
892 }
893 }
894 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds => {
895 if is_tab_cycle_reentry { None } else { last_focused(self.info.id()) }
896 .and_then(|path| self.info.tree().get(path.widget_id()))
897 .and_then(|w| w.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
898 .and_then(|f| {
899 if f.info.is_descendant(&self.info) {
900 Some(f) } else {
902 None
903 }
904 })
905 .or_else(|| {
906 if reverse {
907 self.last_tab_descendant()
908 } else {
909 self.first_tab_descendant()
910 }
911 })
912 } FocusScopeOnFocus::Widget => None,
914 };
915
916 if let (FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::LastFocused, Some(candidate)) = (on_focus, &candidate) {
918 if !self.info.inner_bounds().contains_rect(&candidate.info().inner_bounds()) {
919 return None;
921 }
922 }
923
924 candidate
925 }
926 FocusInfo::NotFocusable | FocusInfo::Focusable { .. } => None,
927 }
928 }
929
930 pub fn descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
932 super::iter::FocusTreeIter::new(self.info.descendants(), self.mode)
933 }
934
935 pub fn self_and_descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
937 super::iter::FocusTreeIter::new(self.info.self_and_descendants(), self.mode)
938 }
939
940 pub fn has_tab_descendant(&self) -> bool {
942 self.descendants().tree_find(Self::filter_tab_skip).is_some()
943 }
944
945 pub fn first_tab_descendant(&self) -> Option<WidgetFocusInfo> {
947 let mut best = (TabIndex::SKIP, self.clone());
948
949 for d in self.descendants().tree_filter(Self::filter_tab_skip) {
950 let idx = d.focus_info().tab_index();
951
952 if idx < best.0 {
953 best = (idx, d);
954 }
955 }
956
957 if best.0.is_skip() { None } else { Some(best.1) }
958 }
959
960 pub fn last_tab_descendant(&self) -> Option<WidgetFocusInfo> {
962 let mut best = (-1i64, self.clone());
963
964 for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
965 let idx = d.focus_info().tab_index().0 as i64;
966
967 if idx > best.0 {
968 best = (idx, d);
969 }
970 }
971
972 if best.0 < 0 { None } else { Some(best.1) }
973 }
974
975 pub fn next_focusables(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
977 if let Some(scope) = self.scope() {
978 super::iter::FocusTreeIter::new(self.info.next_siblings_in(&scope.info), self.mode)
979 } else {
980 super::iter::FocusTreeIter::new(self.info.next_siblings_in(&self.info), self.mode)
982 }
983 }
984
985 pub fn next_focusable(&self) -> Option<WidgetFocusInfo> {
987 self.next_focusables().next()
988 }
989
990 fn filter_tab_skip(w: &WidgetFocusInfo) -> TreeFilter {
991 if w.focus_info().tab_index().is_skip() {
992 TreeFilter::SkipAll
993 } else {
994 TreeFilter::Include
995 }
996 }
997
998 pub fn next_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1004 self.next_tab_focusable_impl(skip_self, false)
1005 }
1006 fn next_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
1007 let self_index = self.focus_info().tab_index();
1008
1009 if self_index == TabIndex::SKIP {
1010 return self.next_focusables().tree_find(Self::filter_tab_skip);
1012 }
1013
1014 let mut best = (TabIndex::SKIP, self.clone());
1015
1016 if !skip_self {
1017 for d in self.descendants().tree_filter(Self::filter_tab_skip) {
1018 let idx = d.focus_info().tab_index();
1019
1020 if idx == self_index {
1021 return Some(d);
1022 } else if idx < best.0 && idx > self_index {
1023 if any {
1024 return Some(d);
1025 }
1026 best = (idx, d);
1027 }
1028 }
1029 }
1030
1031 for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
1032 let idx = s.focus_info().tab_index();
1033
1034 if idx == self_index {
1035 return Some(s);
1036 } else if idx < best.0 && idx > self_index {
1037 if any {
1038 return Some(s);
1039 }
1040 best = (idx, s);
1041 }
1042 }
1043
1044 for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
1045 let idx = s.focus_info().tab_index();
1046
1047 if idx <= best.0 && idx > self_index {
1048 if any {
1049 return Some(s);
1050 }
1051 best = (idx, s);
1052 }
1053 }
1054
1055 if best.0.is_skip() { None } else { Some(best.1) }
1056 }
1057
1058 pub fn prev_focusables(&self) -> super::iter::FocusTreeIter<w_iter::RevTreeIter> {
1060 if let Some(scope) = self.scope() {
1061 super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&scope.info), self.mode)
1062 } else {
1063 super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&self.info), self.mode)
1065 }
1066 }
1067
1068 pub fn prev_focusable(&self) -> Option<WidgetFocusInfo> {
1070 self.prev_focusables().next()
1071 }
1072
1073 pub fn prev_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1079 self.prev_tab_focusable_impl(skip_self, false)
1080 }
1081 fn prev_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
1082 let self_index = self.focus_info().tab_index();
1083
1084 if self_index == TabIndex::SKIP {
1085 return self.prev_focusables().tree_find(Self::filter_tab_skip);
1087 }
1088
1089 let self_index = self_index.0 as i64;
1090 let mut best = (-1i64, self.clone());
1091
1092 if !skip_self {
1093 for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
1094 let idx = d.focus_info().tab_index().0 as i64;
1095
1096 if idx == self_index {
1097 return Some(d);
1098 } else if idx > best.0 && idx < self_index {
1099 if any {
1100 return Some(d);
1101 }
1102 best = (idx, d);
1103 }
1104 }
1105 }
1106
1107 for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
1108 let idx = s.focus_info().tab_index().0 as i64;
1109
1110 if idx == self_index {
1111 return Some(s);
1112 } else if idx > best.0 && idx < self_index {
1113 if any {
1114 return Some(s);
1115 }
1116 best = (idx, s);
1117 }
1118 }
1119
1120 for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
1121 let idx = s.focus_info().tab_index().0 as i64;
1122
1123 if idx >= best.0 && idx < self_index {
1124 if any {
1125 return Some(s);
1126 }
1127 best = (idx, s);
1128 }
1129 }
1130
1131 if best.0 < 0 { None } else { Some(best.1) }
1132 }
1133
1134 pub fn next_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1140 let _span = tracing::trace_span!("next_tab").entered();
1141
1142 if let Some(scope) = self.scope() {
1143 let scope_info = scope.focus_info();
1144 match scope_info.tab_nav() {
1145 TabNav::None => None,
1146 TabNav::Continue => self.next_tab_focusable(skip_self).or_else(|| scope.next_tab(true)),
1147 TabNav::Contained => self.next_tab_focusable(skip_self),
1148 TabNav::Cycle => self.next_tab_focusable(skip_self).or_else(|| scope.first_tab_descendant()),
1149 TabNav::Once => scope.next_tab(true),
1150 }
1151 } else {
1152 None
1153 }
1154 }
1155
1156 pub fn prev_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1162 let _span = tracing::trace_span!("prev_tab").entered();
1163 if let Some(scope) = self.scope() {
1164 let scope_info = scope.focus_info();
1165 match scope_info.tab_nav() {
1166 TabNav::None => None,
1167 TabNav::Continue => self.prev_tab_focusable(skip_self).or_else(|| scope.prev_tab(true)),
1168 TabNav::Contained => self.prev_tab_focusable(skip_self),
1169 TabNav::Cycle => self.prev_tab_focusable(skip_self).or_else(|| scope.last_tab_descendant()),
1170 TabNav::Once => scope.prev_tab(true),
1171 }
1172 } else {
1173 None
1174 }
1175 }
1176
1177 pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetFocusInfo> {
1179 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1180 self.info
1181 .nearest_filtered(origin, max_radius, |w| cast(w.clone()).is_focusable())
1182 .map(cast)
1183 }
1184
1185 pub fn nearest_filtered(
1187 &self,
1188 origin: PxPoint,
1189 max_radius: Px,
1190 mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1191 ) -> Option<WidgetFocusInfo> {
1192 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1193 self.info
1194 .nearest_filtered(origin, max_radius, |w| {
1195 let w = cast(w.clone());
1196 w.is_focusable() && filter(w)
1197 })
1198 .map(cast)
1199 }
1200
1201 pub fn nearest_bounded_filtered(
1203 &self,
1204 origin: PxPoint,
1205 max_radius: Px,
1206 bounds: PxRect,
1207 mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1208 ) -> Option<WidgetFocusInfo> {
1209 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1210 self.info
1211 .nearest_bounded_filtered(origin, max_radius, bounds, move |w| {
1212 let w = cast(w.clone());
1213 w.is_focusable() && filter(w)
1214 })
1215 .map(cast)
1216 }
1217
1218 pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
1220 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1221 self.info
1222 .nearest_oriented_filtered(origin, max_distance, orientation, |w| cast(w.clone()).is_focusable())
1223 .map(cast)
1224 }
1225
1226 pub fn nearest_oriented_filtered(
1229 &self,
1230 origin: PxPoint,
1231 max_distance: Px,
1232 orientation: Orientation2D,
1233 mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1234 ) -> Option<WidgetFocusInfo> {
1235 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1236 self.info
1237 .nearest_oriented_filtered(origin, max_distance, orientation, |w| {
1238 let w = cast(w.clone());
1239 w.is_focusable() && filter(w)
1240 })
1241 .map(cast)
1242 }
1243
1244 fn directional_from(
1245 &self,
1246 scope: &WidgetFocusInfo,
1247 origin: PxBox,
1248 orientation: Orientation2D,
1249 skip_self: bool,
1250 any: bool,
1251 ) -> Option<WidgetFocusInfo> {
1252 let self_id = self.info.id();
1253 let scope_id = scope.info.id();
1254
1255 let skip_parent = if self.is_focusable() {
1257 None
1258 } else {
1259 self.ancestors().next().map(|w| w.info.id())
1260 };
1261
1262 let filter = |w: &WidgetFocusInfo| {
1263 let mut up_to_scope = w.self_and_ancestors().take_while(|w| w.info.id() != scope_id);
1264
1265 if skip_self {
1266 up_to_scope.all(|w| w.info.id() != self_id && !w.focus_info().skip_directional())
1267 } else {
1268 up_to_scope.all(|w| !w.focus_info().skip_directional())
1269 }
1270 };
1271
1272 let origin_center = origin.center();
1273
1274 let mut oriented = scope
1275 .info
1276 .oriented(origin_center, Px::MAX, orientation)
1277 .chain(
1278 scope
1280 .info
1281 .oriented_box(origin, origin.width().max(origin.height()) * Px(2), orientation)
1282 .filter(|w| !w.inner_bounds().to_box2d().intersects(&origin)),
1283 )
1284 .focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets())
1285 .filter(|w| w.info.id() != scope_id && Some(w.info.id()) != skip_parent);
1286
1287 if any {
1288 return oriented.find(filter);
1289 }
1290
1291 let parent_range = self.parent().map(|w| w.info.descendants_range()).unwrap_or_default();
1292
1293 let mut ancestor_dist = DistanceKey::NONE_MAX;
1294 let mut ancestor = None;
1295 let mut sibling_dist = DistanceKey::NONE_MAX;
1296 let mut sibling = None;
1297 let mut other_dist = DistanceKey::NONE_MAX;
1298 let mut other = None;
1299
1300 for w in oriented {
1301 if filter(&w) {
1302 let dist = w.info.distance_key(origin_center);
1303
1304 let mut is_ancestor = None;
1305 let mut is_ancestor = || *is_ancestor.get_or_insert_with(|| w.info.is_ancestor(&self.info));
1306
1307 let mut is_sibling = None;
1308 let mut is_sibling = || *is_sibling.get_or_insert_with(|| parent_range.contains(&w.info));
1309
1310 if dist <= ancestor_dist && is_ancestor() {
1311 ancestor_dist = dist;
1312 ancestor = Some(w);
1313 } else if dist <= sibling_dist && is_sibling() {
1314 sibling_dist = dist;
1315 sibling = Some(w);
1316 } else if dist <= other_dist && !is_ancestor() && !is_sibling() {
1317 other_dist = dist;
1318 other = Some(w);
1319 }
1320 }
1321 }
1322
1323 if other_dist <= ancestor_dist && other_dist <= sibling_dist {
1324 other
1325 } else {
1326 sibling.or(ancestor)
1327 }
1328 }
1329
1330 fn directional_next(&self, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
1331 self.directional_next_from(orientation, self.info.inner_bounds().to_box2d())
1332 }
1333
1334 fn directional_next_from(&self, orientation: Orientation2D, from: PxBox) -> Option<WidgetFocusInfo> {
1335 self.scope()
1336 .and_then(|s| self.directional_from(&s, from, orientation, false, false))
1337 }
1338
1339 pub fn focusable_up(&self) -> Option<WidgetFocusInfo> {
1341 self.directional_next(Orientation2D::Above)
1342 }
1343
1344 pub fn focusable_down(&self) -> Option<WidgetFocusInfo> {
1346 self.directional_next(Orientation2D::Below)
1347 }
1348
1349 pub fn focusable_left(&self) -> Option<WidgetFocusInfo> {
1351 self.directional_next(Orientation2D::Left)
1352 }
1353
1354 pub fn focusable_right(&self) -> Option<WidgetFocusInfo> {
1356 self.directional_next(Orientation2D::Right)
1357 }
1358
1359 pub fn next_up(&self) -> Option<WidgetFocusInfo> {
1361 let _span = tracing::trace_span!("next_up").entered();
1362 self.next_up_from(self.info.inner_bounds().to_box2d())
1363 }
1364 fn next_up_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1365 if let Some(scope) = self.scope() {
1366 let scope_info = scope.focus_info();
1367 match scope_info.directional_nav() {
1368 DirectionalNav::None => None,
1369 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
1370 let mut from = scope.info.inner_bounds();
1371 from.origin.y -= Px(1);
1372 from.size.height = Px(1);
1373 scope.next_up_from(from.to_box2d())
1374 }),
1375 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Above, origin),
1376 DirectionalNav::Cycle => {
1377 self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
1378 let mut from_pt = origin.center();
1380 from_pt.y = scope.info.spatial_bounds().max.y;
1381 self.directional_from(
1382 &scope,
1383 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1384 Orientation2D::Above,
1385 false,
1386 false,
1387 )
1388 })
1389 }
1390 }
1391 } else {
1392 None
1393 }
1394 }
1395
1396 pub fn next_right(&self) -> Option<WidgetFocusInfo> {
1398 let _span = tracing::trace_span!("next_right").entered();
1399 self.next_right_from(self.info.inner_bounds().to_box2d())
1400 }
1401 fn next_right_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1402 if let Some(scope) = self.scope() {
1403 let scope_info = scope.focus_info();
1404 match scope_info.directional_nav() {
1405 DirectionalNav::None => None,
1406 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
1407 let mut from = scope.info.inner_bounds();
1408 from.origin.x += from.size.width + Px(1);
1409 from.size.width = Px(1);
1410 scope.next_right_from(from.to_box2d())
1411 }),
1412 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Right, origin),
1413 DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
1414 let mut from_pt = origin.center();
1416 from_pt.x = scope.info.spatial_bounds().min.x;
1417 self.directional_from(
1418 &scope,
1419 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1420 Orientation2D::Right,
1421 false,
1422 false,
1423 )
1424 }),
1425 }
1426 } else {
1427 None
1428 }
1429 }
1430
1431 pub fn next_down(&self) -> Option<WidgetFocusInfo> {
1433 let _span = tracing::trace_span!("next_down").entered();
1434 self.next_down_from(self.info.inner_bounds().to_box2d())
1435 }
1436 fn next_down_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1437 if let Some(scope) = self.scope() {
1438 let scope_info = scope.focus_info();
1439 match scope_info.directional_nav() {
1440 DirectionalNav::None => None,
1441 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
1442 let mut from = scope.info.inner_bounds();
1443 from.origin.y += from.size.height + Px(1);
1444 from.size.height = Px(1);
1445 scope.next_down_from(from.to_box2d())
1446 }),
1447 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Below, origin),
1448 DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
1449 let mut from_pt = origin.center();
1451 from_pt.y = scope.info.spatial_bounds().min.y;
1452 self.directional_from(
1453 &scope,
1454 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1455 Orientation2D::Below,
1456 false,
1457 false,
1458 )
1459 }),
1460 }
1461 } else {
1462 None
1463 }
1464 }
1465
1466 pub fn next_left(&self) -> Option<WidgetFocusInfo> {
1468 let _span = tracing::trace_span!("next_left").entered();
1469 self.next_left_from(self.info.inner_bounds().to_box2d())
1470 }
1471 fn next_left_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1472 if let Some(scope) = self.scope() {
1473 let scope_info = scope.focus_info();
1474 match scope_info.directional_nav() {
1475 DirectionalNav::None => None,
1476 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
1477 let mut from = scope.info.inner_bounds();
1478 from.origin.x -= Px(1);
1479 from.size.width = Px(1);
1480 scope.next_left_from(from.to_box2d())
1481 }),
1482 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Left, origin),
1483 DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
1484 let mut from_pt = origin.center();
1486 from_pt.x = scope.info.spatial_bounds().max.x;
1487 self.directional_from(
1488 &scope,
1489 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1490 Orientation2D::Left,
1491 false,
1492 false,
1493 )
1494 }),
1495 }
1496 } else {
1497 None
1498 }
1499 }
1500
1501 fn enabled_tab_nav(
1502 &self,
1503 scope: &WidgetFocusInfo,
1504 scope_info: FocusInfo,
1505 skip_self: bool,
1506 already_found: FocusNavAction,
1507 ) -> FocusNavAction {
1508 match scope_info.tab_nav() {
1509 TabNav::None => FocusNavAction::empty(),
1510 tab_nav @ (TabNav::Continue | TabNav::Contained) => {
1511 let mut nav = already_found;
1512
1513 if !nav.contains(FocusNavAction::PREV) && self.prev_tab_focusable_impl(skip_self, true).is_some() {
1514 nav |= FocusNavAction::PREV;
1515 }
1516 if !nav.contains(FocusNavAction::NEXT) && self.next_tab_focusable_impl(skip_self, true).is_some() {
1517 nav |= FocusNavAction::NEXT;
1518 }
1519
1520 if !nav.contains(FocusNavAction::PREV | FocusNavAction::NEXT) && tab_nav == TabNav::Continue {
1521 if let Some(p_scope) = scope.scope() {
1522 nav |= scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, nav)
1523 }
1524 }
1525 nav
1526 }
1527 TabNav::Cycle => {
1528 if scope.descendants().tree_filter(Self::filter_tab_skip).any(|w| &w != self) {
1529 FocusNavAction::PREV | FocusNavAction::NEXT
1530 } else {
1531 FocusNavAction::empty()
1532 }
1533 }
1534 TabNav::Once => {
1535 if let Some(p_scope) = scope.scope() {
1536 scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, already_found)
1537 } else {
1538 FocusNavAction::empty()
1539 }
1540 }
1541 }
1542 }
1543
1544 fn enabled_directional_nav(
1545 &self,
1546 scope: &WidgetFocusInfo,
1547 scope_info: FocusInfo,
1548 skip_self: bool,
1549 already_found: FocusNavAction,
1550 ) -> FocusNavAction {
1551 let directional_nav = scope_info.directional_nav();
1552
1553 if directional_nav == DirectionalNav::None {
1554 return FocusNavAction::empty();
1555 }
1556
1557 let mut nav = already_found;
1558 let from_pt = self.info.inner_bounds().to_box2d();
1559
1560 if !nav.contains(FocusNavAction::UP)
1561 && self
1562 .directional_from(scope, from_pt, Orientation2D::Above, skip_self, true)
1563 .is_some()
1564 {
1565 nav |= FocusNavAction::UP;
1566 }
1567 if !nav.contains(FocusNavAction::RIGHT)
1568 && self
1569 .directional_from(scope, from_pt, Orientation2D::Right, skip_self, true)
1570 .is_some()
1571 {
1572 nav |= FocusNavAction::RIGHT;
1573 }
1574 if !nav.contains(FocusNavAction::DOWN)
1575 && self
1576 .directional_from(scope, from_pt, Orientation2D::Below, skip_self, true)
1577 .is_some()
1578 {
1579 nav |= FocusNavAction::DOWN;
1580 }
1581 if !nav.contains(FocusNavAction::LEFT)
1582 && self
1583 .directional_from(scope, from_pt, Orientation2D::Left, skip_self, true)
1584 .is_some()
1585 {
1586 nav |= FocusNavAction::LEFT;
1587 }
1588
1589 if !nav.contains(FocusNavAction::DIRECTIONAL) {
1590 match directional_nav {
1591 DirectionalNav::Continue => {
1592 if let Some(p_scope) = scope.scope() {
1593 nav |= scope.enabled_directional_nav(&p_scope, p_scope.focus_info(), true, nav);
1594 }
1595 }
1596 DirectionalNav::Cycle => {
1597 let scope_bounds = scope.info.inner_bounds();
1598 if !nav.contains(FocusNavAction::UP) {
1599 let mut from_pt = from_pt.center();
1600 from_pt.y = scope_bounds.max().y;
1601 if self
1602 .directional_from(
1603 scope,
1604 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1605 Orientation2D::Above,
1606 true,
1607 true,
1608 )
1609 .is_some()
1610 {
1611 nav |= FocusNavAction::UP;
1612 }
1613 }
1614 if !nav.contains(FocusNavAction::RIGHT) {
1615 let mut from_pt = from_pt.center();
1616 from_pt.x = scope_bounds.min().x;
1617 if self
1618 .directional_from(
1619 scope,
1620 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1621 Orientation2D::Right,
1622 true,
1623 true,
1624 )
1625 .is_some()
1626 {
1627 nav |= FocusNavAction::RIGHT;
1628 }
1629 }
1630 if !nav.contains(FocusNavAction::DOWN) {
1631 let mut from_pt = from_pt.center();
1632 from_pt.y = scope_bounds.min().y;
1633 if self
1634 .directional_from(
1635 scope,
1636 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1637 Orientation2D::Below,
1638 true,
1639 true,
1640 )
1641 .is_some()
1642 {
1643 nav |= FocusNavAction::DOWN;
1644 }
1645 }
1646 if !nav.contains(FocusNavAction::LEFT) {
1647 let mut from_pt = from_pt.center();
1648 from_pt.x = scope_bounds.max().x;
1649 if self
1650 .directional_from(
1651 scope,
1652 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1653 Orientation2D::Left,
1654 true,
1655 true,
1656 )
1657 .is_some()
1658 {
1659 nav |= FocusNavAction::LEFT;
1660 }
1661 }
1662
1663 if !nav.contains(FocusNavAction::DIRECTIONAL) {
1664 let info = self.focus_info();
1665
1666 if info.is_scope() && matches!(info.directional_nav(), DirectionalNav::Continue) {
1667 if nav.contains(FocusNavAction::UP) || nav.contains(FocusNavAction::DOWN) {
1669 nav |= FocusNavAction::UP | FocusNavAction::DOWN;
1670 }
1671 if nav.contains(FocusNavAction::LEFT) || nav.contains(FocusNavAction::RIGHT) {
1672 nav |= FocusNavAction::LEFT | FocusNavAction::RIGHT;
1673 }
1674 }
1675 }
1676 }
1677 _ => {}
1678 }
1679 }
1680
1681 nav
1682 }
1683
1684 pub fn enabled_nav(&self) -> FocusNavAction {
1686 let _span = tracing::trace_span!("enabled_nav").entered();
1687
1688 let mut nav = FocusNavAction::empty();
1689
1690 if let Some(scope) = self.scope() {
1691 nav |= FocusNavAction::EXIT;
1692 nav.set(FocusNavAction::ENTER, self.descendants().next().is_some());
1693
1694 let scope_info = scope.focus_info();
1695
1696 nav |= self.enabled_tab_nav(&scope, scope_info, false, FocusNavAction::empty());
1697 nav |= self.enabled_directional_nav(&scope, scope_info, false, FocusNavAction::empty());
1698 }
1699
1700 nav.set(FocusNavAction::ALT, self.in_alt_scope() || self.alt_scope().is_some());
1701
1702 nav
1703 }
1704}
1705
1706#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
1708pub enum FocusInfo {
1709 NotFocusable,
1711 Focusable {
1713 tab_index: TabIndex,
1715 skip_directional: bool,
1717 },
1718 FocusScope {
1720 tab_index: TabIndex,
1722 skip_directional: bool,
1724 tab_nav: TabNav,
1726 directional_nav: DirectionalNav,
1728 on_focus: FocusScopeOnFocus,
1730 alt: bool,
1732 },
1733}
1734
1735#[derive(Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
1737pub enum FocusScopeOnFocus {
1738 Widget,
1740 FirstDescendant,
1750 LastFocused,
1762
1763 FirstDescendantIgnoreBounds,
1771
1772 LastFocusedIgnoreBounds,
1780}
1781impl fmt::Debug for FocusScopeOnFocus {
1782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1783 if f.alternate() {
1784 write!(f, "FocusScopeOnFocus::")?;
1785 }
1786 match self {
1787 FocusScopeOnFocus::Widget => write!(f, "Widget"),
1788 FocusScopeOnFocus::FirstDescendant => write!(f, "FirstDescendant"),
1789 FocusScopeOnFocus::LastFocused => write!(f, "LastFocused"),
1790 FocusScopeOnFocus::FirstDescendantIgnoreBounds => write!(f, "FirstDescendantIgnoreBounds"),
1791 FocusScopeOnFocus::LastFocusedIgnoreBounds => write!(f, "LastFocusedIgnoreBounds"),
1792 }
1793 }
1794}
1795impl Default for FocusScopeOnFocus {
1796 fn default() -> Self {
1798 FocusScopeOnFocus::FirstDescendant
1799 }
1800}
1801
1802impl FocusInfo {
1803 pub fn is_focusable(self) -> bool {
1805 !matches!(self, FocusInfo::NotFocusable)
1806 }
1807
1808 pub fn is_scope(self) -> bool {
1810 matches!(self, FocusInfo::FocusScope { .. })
1811 }
1812
1813 pub fn is_alt_scope(self) -> bool {
1815 match self {
1816 FocusInfo::FocusScope { alt, .. } => alt,
1817 _ => false,
1818 }
1819 }
1820
1821 pub fn tab_nav(self) -> TabNav {
1829 match self {
1830 FocusInfo::FocusScope { tab_nav, .. } => tab_nav,
1831 FocusInfo::Focusable { .. } => TabNav::Continue,
1832 FocusInfo::NotFocusable => TabNav::None,
1833 }
1834 }
1835
1836 pub fn directional_nav(self) -> DirectionalNav {
1844 match self {
1845 FocusInfo::FocusScope { directional_nav, .. } => directional_nav,
1846 FocusInfo::Focusable { .. } => DirectionalNav::Continue,
1847 FocusInfo::NotFocusable => DirectionalNav::None,
1848 }
1849 }
1850
1851 pub fn tab_index(self) -> TabIndex {
1858 match self {
1859 FocusInfo::Focusable { tab_index, .. } => tab_index,
1860 FocusInfo::FocusScope { tab_index, .. } => tab_index,
1861 FocusInfo::NotFocusable => TabIndex::SKIP,
1862 }
1863 }
1864
1865 pub fn skip_directional(self) -> bool {
1872 match self {
1873 FocusInfo::Focusable { skip_directional, .. } => skip_directional,
1874 FocusInfo::FocusScope { skip_directional, .. } => skip_directional,
1875 FocusInfo::NotFocusable => true,
1876 }
1877 }
1878
1879 pub fn scope_on_focus(self) -> FocusScopeOnFocus {
1886 match self {
1887 FocusInfo::FocusScope { on_focus, .. } => on_focus,
1888 _ => FocusScopeOnFocus::Widget,
1889 }
1890 }
1891}
1892
1893static_id! {
1894 static ref FOCUS_INFO_ID: StateId<FocusInfoData>;
1895 static ref FOCUS_TREE_ID: StateId<FocusTreeData>;
1896}
1897
1898#[derive(Default)]
1899pub(super) struct FocusTreeData {
1900 alt_scopes: Mutex<IdSet<WidgetId>>,
1901}
1902impl FocusTreeData {
1903 pub(super) fn consolidate_alt_scopes(prev_tree: &WidgetInfoTree, new_tree: &WidgetInfoTree) {
1904 let prev = prev_tree
1907 .build_meta()
1908 .get(*FOCUS_TREE_ID)
1909 .map(|d| d.alt_scopes.lock().clone())
1910 .unwrap_or_default();
1911
1912 let mut alt_scopes = prev;
1913 if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
1914 alt_scopes.extend(data.alt_scopes.lock().iter());
1915 }
1916
1917 alt_scopes.retain(|id| {
1918 if let Some(wgt) = new_tree.get(*id) {
1919 if let Some(info) = wgt.meta().get(*FOCUS_INFO_ID) {
1920 if info.build().is_alt_scope() {
1921 for parent in wgt.ancestors() {
1922 if let Some(info) = parent.meta().get(*FOCUS_INFO_ID) {
1923 if info.build().is_scope() {
1924 info.inner_alt.store(Some(*id), Relaxed);
1925 break;
1926 }
1927 }
1928 }
1929
1930 return true;
1931 }
1932 }
1933 }
1934 false
1935 });
1936
1937 if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
1938 *data.alt_scopes.lock() = alt_scopes;
1939 }
1940 }
1941}
1942
1943#[derive(Default, Debug)]
1944struct FocusInfoData {
1945 focusable: Option<bool>,
1946 scope: Option<bool>,
1947 alt_scope: bool,
1948 on_focus: FocusScopeOnFocus,
1949 tab_index: Option<TabIndex>,
1950 tab_nav: Option<TabNav>,
1951 directional_nav: Option<DirectionalNav>,
1952 skip_directional: Option<bool>,
1953
1954 inner_alt: Atomic<Option<WidgetId>>,
1955
1956 access_handler_registered: bool,
1957}
1958impl FocusInfoData {
1959 pub fn build(&self) -> FocusInfo {
1963 match (self.focusable, self.scope, self.tab_index, self.tab_nav, self.directional_nav) {
1964 (Some(false), _, _, _, _) => FocusInfo::NotFocusable,
1966
1967 (_, Some(true), idx, tab, dir) | (_, None, idx, tab @ Some(_), dir) | (_, None, idx, tab, dir @ Some(_)) => {
1971 FocusInfo::FocusScope {
1972 tab_index: idx.unwrap_or(TabIndex::AUTO),
1973 skip_directional: self.skip_directional.unwrap_or_default(),
1974 tab_nav: tab.unwrap_or(TabNav::Continue),
1975 directional_nav: dir.unwrap_or(DirectionalNav::Continue),
1976 alt: self.alt_scope,
1977 on_focus: self.on_focus,
1978 }
1979 }
1980
1981 (Some(true), _, idx, _, _) | (_, _, idx @ Some(_), _, _) => FocusInfo::Focusable {
1984 tab_index: idx.unwrap_or(TabIndex::AUTO),
1985 skip_directional: self.skip_directional.unwrap_or_default(),
1986 },
1987
1988 _ => FocusInfo::NotFocusable,
1989 }
1990 }
1991}
1992
1993pub struct FocusInfoBuilder<'a>(&'a mut WidgetInfoBuilder);
2038impl<'a> FocusInfoBuilder<'a> {
2039 pub fn new(builder: &'a mut WidgetInfoBuilder) -> Self {
2041 let mut r = Self(builder);
2042 r.with_tree_data(|_| {}); r
2044 }
2045
2046 fn with_data<R>(&mut self, visitor: impl FnOnce(&mut FocusInfoData) -> R) -> R {
2047 let mut access = self.0.access().is_some();
2048
2049 let r = self.0.with_meta(|m| {
2050 let data = m.into_entry(*FOCUS_INFO_ID).or_default();
2051
2052 if access {
2053 access = !std::mem::replace(&mut data.access_handler_registered, true);
2054 }
2055
2056 visitor(data)
2057 });
2058
2059 if access {
2060 self.0.access().unwrap().on_access_build(|args| {
2062 if args.widget.info().clone().into_focusable(true, false).is_some() {
2063 args.node.commands.push(zng_view_api::access::AccessCmdName::Focus);
2064 }
2065 });
2066 }
2067
2068 r
2069 }
2070
2071 fn with_tree_data<R>(&mut self, visitor: impl FnOnce(&mut FocusTreeData) -> R) -> R {
2072 self.0.with_build_meta(|m| visitor(m.into_entry(*FOCUS_TREE_ID).or_default()))
2073 }
2074
2075 pub fn focusable(&mut self, is_focusable: bool) -> &mut Self {
2077 self.with_data(|data| {
2078 data.focusable = Some(is_focusable);
2079 });
2080 self
2081 }
2082
2083 pub fn focusable_passive(&mut self, is_focusable: bool) -> &mut Self {
2087 self.with_data(|data| {
2088 if data.focusable.is_none() {
2089 data.focusable = Some(is_focusable);
2090 }
2091 });
2092 self
2093 }
2094
2095 pub fn scope(&mut self, is_focus_scope: bool) -> &mut Self {
2097 self.with_data(|data| {
2098 data.scope = Some(is_focus_scope);
2099 });
2100 self
2101 }
2102
2103 pub fn alt_scope(&mut self, is_alt_focus_scope: bool) -> &mut Self {
2107 self.with_data(|data| {
2108 data.alt_scope = is_alt_focus_scope;
2109 if is_alt_focus_scope {
2110 data.scope = Some(true);
2111
2112 if data.tab_index.is_none() {
2113 data.tab_index = Some(TabIndex::SKIP);
2114 }
2115 if data.tab_nav.is_none() {
2116 data.tab_nav = Some(TabNav::Cycle);
2117 }
2118 if data.directional_nav.is_none() {
2119 data.directional_nav = Some(DirectionalNav::Cycle);
2120 }
2121 if data.skip_directional.is_none() {
2122 data.skip_directional = Some(true);
2123 }
2124 }
2125 });
2126 if is_alt_focus_scope {
2127 let wgt_id = self.0.widget_id();
2128 self.with_tree_data(|d| d.alt_scopes.lock().insert(wgt_id));
2129 }
2130 self
2131 }
2132
2133 pub fn on_focus(&mut self, as_focus_scope_on_focus: FocusScopeOnFocus) -> &mut Self {
2135 self.with_data(|data| {
2136 data.on_focus = as_focus_scope_on_focus;
2137 });
2138 self
2139 }
2140
2141 pub fn tab_index(&mut self, tab_index: TabIndex) -> &mut Self {
2143 self.with_data(|data| {
2144 data.tab_index = Some(tab_index);
2145 });
2146 self
2147 }
2148
2149 pub fn tab_nav(&mut self, scope_tab_nav: TabNav) -> &mut Self {
2151 self.with_data(|data| {
2152 data.tab_nav = Some(scope_tab_nav);
2153 });
2154 self
2155 }
2156
2157 pub fn directional_nav(&mut self, scope_directional_nav: DirectionalNav) -> &mut Self {
2159 self.with_data(|data| {
2160 data.directional_nav = Some(scope_directional_nav);
2161 });
2162 self
2163 }
2164 pub fn skip_directional(&mut self, skip: bool) -> &mut Self {
2166 self.with_data(|data| {
2167 data.skip_directional = Some(skip);
2168 });
2169 self
2170 }
2171}