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 =
446 FocusNavAction::UP.bits() | FocusNavAction::RIGHT.bits() | FocusNavAction::DOWN.bits() | FocusNavAction::LEFT.bits();
447 }
448}
449
450bitflags! {
451 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
452 pub(super) struct FocusMode: u8 {
453 const DISABLED = 1;
455 const HIDDEN = 2;
457 }
458}
459impl FocusMode {
460 pub fn new(focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
461 let mut mode = FocusMode::empty();
462 mode.set(FocusMode::DISABLED, focus_disabled_widgets);
463 mode.set(FocusMode::HIDDEN, focus_hidden_widgets);
464 mode
465 }
466}
467
468#[derive(Clone, Debug)]
472pub struct FocusInfoTree {
473 tree: WidgetInfoTree,
474 mode: FocusMode,
475}
476impl FocusInfoTree {
477 pub fn new(tree: WidgetInfoTree, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
484 FocusInfoTree {
485 tree,
486 mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
487 }
488 }
489
490 pub fn tree(&self) -> &WidgetInfoTree {
492 &self.tree
493 }
494
495 pub fn focus_disabled_widgets(&self) -> bool {
502 self.mode.contains(FocusMode::DISABLED)
503 }
504
505 pub fn focus_hidden_widgets(&self) -> bool {
512 self.mode.contains(FocusMode::HIDDEN)
513 }
514
515 pub fn root(&self) -> WidgetFocusInfo {
520 WidgetFocusInfo {
521 info: self.tree.root(),
522 mode: self.mode,
523 }
524 }
525
526 pub fn focusable_root(&self) -> Option<WidgetFocusInfo> {
531 let root = self.root();
532 if root.is_focusable() {
533 return Some(root);
534 }
535
536 let mut candidate = None;
537 let mut candidate_weight = usize::MAX;
538
539 for w in root.descendants().tree_filter(|_| TreeFilter::SkipDescendants) {
540 let weight = w.info.prev_siblings().count() + w.info.ancestors().count();
541 if weight < candidate_weight {
542 candidate = Some(w);
543 candidate_weight = weight;
544 }
545 }
546
547 candidate
548 }
549
550 pub fn get(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetFocusInfo> {
552 self.tree
553 .get(widget_id)
554 .and_then(|i| i.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
555 }
556
557 pub fn get_or_parent(&self, path: &WidgetPath) -> Option<WidgetFocusInfo> {
559 self.get(path.widget_id())
560 .or_else(|| path.ancestors().iter().rev().find_map(|&id| self.get(id)))
561 }
562
563 pub fn contains(&self, widget_id: impl Into<WidgetId>) -> bool {
565 self.get(widget_id).is_some()
566 }
567}
568
569pub trait WidgetInfoFocusExt {
573 fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo;
581 fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo>;
589}
590impl WidgetInfoFocusExt for WidgetInfo {
591 fn into_focus_info(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> WidgetFocusInfo {
592 WidgetFocusInfo::new(self, focus_disabled_widgets, focus_hidden_widgets)
593 }
594 fn into_focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Option<WidgetFocusInfo> {
595 let r = self.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
596 if r.is_focusable() { Some(r) } else { None }
597 }
598}
599
600#[derive(Clone, Eq, PartialEq, Hash, Debug)]
604pub struct WidgetFocusInfo {
605 info: WidgetInfo,
606 mode: FocusMode,
607}
608impl WidgetFocusInfo {
609 pub fn new(widget_info: WidgetInfo, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> Self {
616 WidgetFocusInfo {
617 info: widget_info,
618 mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
619 }
620 }
621
622 pub fn info(&self) -> &WidgetInfo {
624 &self.info
625 }
626
627 pub fn focus_disabled_widgets(&self) -> bool {
634 self.mode.contains(FocusMode::DISABLED)
635 }
636
637 pub fn focus_hidden_widgets(&self) -> bool {
644 self.mode.contains(FocusMode::HIDDEN)
645 }
646
647 pub fn root(&self) -> Self {
649 self.ancestors().last().unwrap_or_else(|| self.clone())
650 }
651
652 pub fn focus_tree(&self) -> FocusInfoTree {
654 FocusInfoTree {
655 tree: self.info.tree().clone(),
656 mode: self.mode,
657 }
658 }
659
660 pub fn is_focusable(&self) -> bool {
669 self.focus_info().is_focusable()
670 }
671
672 pub fn is_scope(&self) -> bool {
674 self.focus_info().is_scope()
675 }
676
677 pub fn is_alt_scope(&self) -> bool {
679 self.focus_info().is_alt_scope()
680 }
681
682 pub fn nested_window(&self) -> Option<WindowId> {
686 self.info.nested_window()
687 }
688
689 pub fn nested_window_tree(&self) -> Option<FocusInfoTree> {
691 self.info
692 .nested_window_tree()
693 .map(|t| FocusInfoTree::new(t, self.focus_disabled_widgets(), self.focus_hidden_widgets()))
694 }
695
696 fn mode_allows_focus(&self) -> bool {
697 let int = self.info.interactivity();
698 if self.mode.contains(FocusMode::DISABLED) {
699 if int.is_blocked() {
700 return false;
701 }
702 } else if !int.is_enabled() {
703 return false;
704 }
705
706 let vis = self.info.visibility();
707 if self.mode.contains(FocusMode::HIDDEN) {
708 if vis == Visibility::Collapsed {
709 return false;
710 }
711 } else if vis != Visibility::Visible {
712 return false;
713 }
714
715 true
716 }
717
718 fn mode_allows_focus_ignore_blocked(&self) -> bool {
719 let int = self.info.interactivity();
720 if !self.mode.contains(FocusMode::DISABLED) && int.is_vis_disabled() {
721 return false;
722 }
723
724 let vis = self.info.visibility();
725 if self.mode.contains(FocusMode::HIDDEN) {
726 if vis == Visibility::Collapsed {
727 return false;
728 }
729 } else if vis != Visibility::Visible {
730 return false;
731 }
732
733 true
734 }
735
736 pub fn focus_info(&self) -> FocusInfo {
738 if self.mode_allows_focus() {
739 if let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID) {
740 return builder.build();
741 } else if self.info.nested_window().is_some() {
742 return FocusInfo::FocusScope {
744 tab_index: TabIndex::AUTO,
745 skip_directional: false,
746 tab_nav: TabNav::Contained,
747 directional_nav: DirectionalNav::Contained,
748 on_focus: FocusScopeOnFocus::FirstDescendant,
749 alt: false,
750 };
751 }
752 }
753 FocusInfo::NotFocusable
754 }
755
756 pub fn focus_info_ignore_blocked(&self) -> FocusInfo {
758 if self.mode_allows_focus_ignore_blocked()
759 && let Some(builder) = self.info.meta().get(*FOCUS_INFO_ID)
760 {
761 return builder.build();
762 }
763 FocusInfo::NotFocusable
764 }
765
766 pub fn ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
768 let focus_disabled_widgets = self.focus_disabled_widgets();
769 let focus_hidden_widgets = self.focus_hidden_widgets();
770 self.info.ancestors().focusable(focus_disabled_widgets, focus_hidden_widgets)
771 }
772
773 pub fn self_and_ancestors(&self) -> impl Iterator<Item = WidgetFocusInfo> {
775 [self.clone()].into_iter().chain(self.ancestors())
776 }
777
778 pub fn scopes(&self) -> impl Iterator<Item = WidgetFocusInfo> {
780 let focus_disabled_widgets = self.focus_disabled_widgets();
781 let focus_hidden_widgets = self.focus_hidden_widgets();
782 self.info.ancestors().filter_map(move |i| {
783 let i = i.into_focus_info(focus_disabled_widgets, focus_hidden_widgets);
784 if i.is_scope() { Some(i) } else { None }
785 })
786 }
787
788 pub fn parent(&self) -> Option<WidgetFocusInfo> {
790 self.ancestors().next()
791 }
792
793 pub fn scope(&self) -> Option<WidgetFocusInfo> {
795 self.scopes().next()
796 }
797
798 pub fn alt_scope(&self) -> Option<WidgetFocusInfo> {
806 if self.in_alt_scope() {
807 let mut alt_scope = self.clone();
809 for scope in self.scopes() {
810 if scope.is_alt_scope() {
811 alt_scope = scope;
812 } else {
813 return scope.inner_alt_scope_skip(&alt_scope);
814 }
815 }
816 return None;
817 }
818
819 if self.is_scope() {
820 let r = self.inner_alt_scope();
822 if r.is_some() {
823 return r;
824 }
825 }
826
827 let mut skip = self.clone();
829 for scope in self.scopes() {
830 let r = scope.inner_alt_scope_skip(&skip);
831 if r.is_some() {
832 return r;
833 }
834 skip = scope;
835 }
836
837 None
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 && let Some(wgt) = self.info.tree().get(id)
843 {
844 let wgt = wgt.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
845 if wgt.is_alt_scope() && wgt.info.is_descendant(&self.info) {
846 return Some(wgt);
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 && !alt.info.is_descendant(&skip.info)
854 && alt.info != skip.info
855 {
856 return Some(alt);
857 }
858 None
859 }
860
861 pub fn in_alt_scope(&self) -> bool {
863 self.is_alt_scope() || self.scopes().any(|s| s.is_alt_scope())
864 }
865
866 pub fn on_focus_scope_move<'f>(
880 &self,
881 last_focused: impl FnOnce(WidgetId) -> Option<&'f WidgetPath>,
882 is_tab_cycle_reentry: bool,
883 reverse: bool,
884 ) -> Option<WidgetFocusInfo> {
885 match self.focus_info() {
886 FocusInfo::FocusScope { on_focus, .. } => {
887 let candidate = match on_focus {
888 FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::FirstDescendantIgnoreBounds => {
889 if reverse {
890 self.last_tab_descendant()
891 } else {
892 self.first_tab_descendant()
893 }
894 }
895 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds => {
896 if is_tab_cycle_reentry { None } else { last_focused(self.info.id()) }
897 .and_then(|path| self.info.tree().get(path.widget_id()))
898 .and_then(|w| w.into_focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets()))
899 .and_then(|f| {
900 if f.info.is_descendant(&self.info) {
901 Some(f) } else {
903 None
904 }
905 })
906 .or_else(|| {
907 if reverse {
908 self.last_tab_descendant()
909 } else {
910 self.first_tab_descendant()
911 }
912 })
913 } FocusScopeOnFocus::Widget => None,
915 };
916
917 if let FocusScopeOnFocus::FirstDescendant | FocusScopeOnFocus::LastFocused = on_focus
919 && let Some(candidate) = &candidate
920 && !self.info.inner_bounds().contains_rect(&candidate.info().inner_bounds())
921 {
922 return None;
924 }
925
926 candidate
927 }
928 FocusInfo::NotFocusable | FocusInfo::Focusable { .. } => None,
929 }
930 }
931
932 pub fn descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
934 super::iter::FocusTreeIter::new(self.info.descendants(), self.mode)
935 }
936
937 pub fn self_and_descendants(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
939 super::iter::FocusTreeIter::new(self.info.self_and_descendants(), self.mode)
940 }
941
942 pub fn has_tab_descendant(&self) -> bool {
944 self.descendants().tree_find(Self::filter_tab_skip).is_some()
945 }
946
947 pub fn first_tab_descendant(&self) -> Option<WidgetFocusInfo> {
949 let mut best = (TabIndex::SKIP, self.clone());
950
951 for d in self.descendants().tree_filter(Self::filter_tab_skip) {
952 let idx = d.focus_info().tab_index();
953
954 if idx < best.0 {
955 best = (idx, d);
956 }
957 }
958
959 if best.0.is_skip() { None } else { Some(best.1) }
960 }
961
962 pub fn last_tab_descendant(&self) -> Option<WidgetFocusInfo> {
964 let mut best = (-1i64, self.clone());
965
966 for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
967 let idx = d.focus_info().tab_index().0 as i64;
968
969 if idx > best.0 {
970 best = (idx, d);
971 }
972 }
973
974 if best.0 < 0 { None } else { Some(best.1) }
975 }
976
977 pub fn next_focusables(&self) -> super::iter::FocusTreeIter<w_iter::TreeIter> {
979 if let Some(scope) = self.scope() {
980 super::iter::FocusTreeIter::new(self.info.next_siblings_in(&scope.info), self.mode)
981 } else {
982 super::iter::FocusTreeIter::new(self.info.next_siblings_in(&self.info), self.mode)
984 }
985 }
986
987 pub fn next_focusable(&self) -> Option<WidgetFocusInfo> {
989 self.next_focusables().next()
990 }
991
992 fn filter_tab_skip(w: &WidgetFocusInfo) -> TreeFilter {
993 if w.focus_info().tab_index().is_skip() {
994 TreeFilter::SkipAll
995 } else {
996 TreeFilter::Include
997 }
998 }
999
1000 pub fn next_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1006 self.next_tab_focusable_impl(skip_self, false)
1007 }
1008 fn next_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
1009 let self_index = self.focus_info().tab_index();
1010
1011 if self_index == TabIndex::SKIP {
1012 return self.next_focusables().tree_find(Self::filter_tab_skip);
1014 }
1015
1016 let mut best = (TabIndex::SKIP, self.clone());
1017
1018 if !skip_self {
1019 for d in self.descendants().tree_filter(Self::filter_tab_skip) {
1020 let idx = d.focus_info().tab_index();
1021
1022 if idx == self_index {
1023 return Some(d);
1024 } else if idx < best.0 && idx > self_index {
1025 if any {
1026 return Some(d);
1027 }
1028 best = (idx, d);
1029 }
1030 }
1031 }
1032
1033 for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
1034 let idx = s.focus_info().tab_index();
1035
1036 if idx == self_index {
1037 return Some(s);
1038 } else if idx < best.0 && idx > self_index {
1039 if any {
1040 return Some(s);
1041 }
1042 best = (idx, s);
1043 }
1044 }
1045
1046 for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
1047 let idx = s.focus_info().tab_index();
1048
1049 if idx <= best.0 && idx > self_index {
1050 if any {
1051 return Some(s);
1052 }
1053 best = (idx, s);
1054 }
1055 }
1056
1057 if best.0.is_skip() { None } else { Some(best.1) }
1058 }
1059
1060 pub fn prev_focusables(&self) -> super::iter::FocusTreeIter<w_iter::RevTreeIter> {
1062 if let Some(scope) = self.scope() {
1063 super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&scope.info), self.mode)
1064 } else {
1065 super::iter::FocusTreeIter::new(self.info.prev_siblings_in(&self.info), self.mode)
1067 }
1068 }
1069
1070 pub fn prev_focusable(&self) -> Option<WidgetFocusInfo> {
1072 self.prev_focusables().next()
1073 }
1074
1075 pub fn prev_tab_focusable(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1081 self.prev_tab_focusable_impl(skip_self, false)
1082 }
1083 fn prev_tab_focusable_impl(&self, skip_self: bool, any: bool) -> Option<WidgetFocusInfo> {
1084 let self_index = self.focus_info().tab_index();
1085
1086 if self_index == TabIndex::SKIP {
1087 return self.prev_focusables().tree_find(Self::filter_tab_skip);
1089 }
1090
1091 let self_index = self_index.0 as i64;
1092 let mut best = (-1i64, self.clone());
1093
1094 if !skip_self {
1095 for d in self.descendants().tree_rev().tree_filter(Self::filter_tab_skip) {
1096 let idx = d.focus_info().tab_index().0 as i64;
1097
1098 if idx == self_index {
1099 return Some(d);
1100 } else if idx > best.0 && idx < self_index {
1101 if any {
1102 return Some(d);
1103 }
1104 best = (idx, d);
1105 }
1106 }
1107 }
1108
1109 for s in self.prev_focusables().tree_filter(Self::filter_tab_skip) {
1110 let idx = s.focus_info().tab_index().0 as i64;
1111
1112 if idx == self_index {
1113 return Some(s);
1114 } else if idx > best.0 && idx < self_index {
1115 if any {
1116 return Some(s);
1117 }
1118 best = (idx, s);
1119 }
1120 }
1121
1122 for s in self.next_focusables().tree_filter(Self::filter_tab_skip) {
1123 let idx = s.focus_info().tab_index().0 as i64;
1124
1125 if idx >= best.0 && idx < self_index {
1126 if any {
1127 return Some(s);
1128 }
1129 best = (idx, s);
1130 }
1131 }
1132
1133 if best.0 < 0 { None } else { Some(best.1) }
1134 }
1135
1136 pub fn next_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1142 let _span = tracing::trace_span!("next_tab").entered();
1143
1144 if let Some(scope) = self.scope() {
1145 let scope_info = scope.focus_info();
1146 match scope_info.tab_nav() {
1147 TabNav::None => None,
1148 TabNav::Continue => self.next_tab_focusable(skip_self).or_else(|| scope.next_tab(true)),
1149 TabNav::Contained => self.next_tab_focusable(skip_self),
1150 TabNav::Cycle => self.next_tab_focusable(skip_self).or_else(|| scope.first_tab_descendant()),
1151 TabNav::Once => scope.next_tab(true),
1152 }
1153 } else {
1154 None
1155 }
1156 }
1157
1158 pub fn prev_tab(&self, skip_self: bool) -> Option<WidgetFocusInfo> {
1164 let _span = tracing::trace_span!("prev_tab").entered();
1165 if let Some(scope) = self.scope() {
1166 let scope_info = scope.focus_info();
1167 match scope_info.tab_nav() {
1168 TabNav::None => None,
1169 TabNav::Continue => self.prev_tab_focusable(skip_self).or_else(|| scope.prev_tab(true)),
1170 TabNav::Contained => self.prev_tab_focusable(skip_self),
1171 TabNav::Cycle => self.prev_tab_focusable(skip_self).or_else(|| scope.last_tab_descendant()),
1172 TabNav::Once => scope.prev_tab(true),
1173 }
1174 } else {
1175 None
1176 }
1177 }
1178
1179 pub fn nearest(&self, origin: PxPoint, max_radius: Px) -> Option<WidgetFocusInfo> {
1181 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1182 self.info
1183 .nearest_filtered(origin, max_radius, |w| cast(w.clone()).is_focusable())
1184 .map(cast)
1185 }
1186
1187 pub fn nearest_filtered(
1189 &self,
1190 origin: PxPoint,
1191 max_radius: Px,
1192 mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1193 ) -> Option<WidgetFocusInfo> {
1194 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1195 self.info
1196 .nearest_filtered(origin, max_radius, |w| {
1197 let w = cast(w.clone());
1198 w.is_focusable() && filter(w)
1199 })
1200 .map(cast)
1201 }
1202
1203 pub fn nearest_bounded_filtered(
1205 &self,
1206 origin: PxPoint,
1207 max_radius: Px,
1208 bounds: PxRect,
1209 mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1210 ) -> Option<WidgetFocusInfo> {
1211 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1212 self.info
1213 .nearest_bounded_filtered(origin, max_radius, bounds, move |w| {
1214 let w = cast(w.clone());
1215 w.is_focusable() && filter(w)
1216 })
1217 .map(cast)
1218 }
1219
1220 pub fn nearest_oriented(&self, origin: PxPoint, max_distance: Px, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
1222 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1223 self.info
1224 .nearest_oriented_filtered(origin, max_distance, orientation, |w| cast(w.clone()).is_focusable())
1225 .map(cast)
1226 }
1227
1228 pub fn nearest_oriented_filtered(
1231 &self,
1232 origin: PxPoint,
1233 max_distance: Px,
1234 orientation: Orientation2D,
1235 mut filter: impl FnMut(WidgetFocusInfo) -> bool,
1236 ) -> Option<WidgetFocusInfo> {
1237 let cast = |w: WidgetInfo| w.into_focus_info(self.focus_disabled_widgets(), self.focus_hidden_widgets());
1238 self.info
1239 .nearest_oriented_filtered(origin, max_distance, orientation, |w| {
1240 let w = cast(w.clone());
1241 w.is_focusable() && filter(w)
1242 })
1243 .map(cast)
1244 }
1245
1246 fn directional_from(
1247 &self,
1248 scope: &WidgetFocusInfo,
1249 origin: PxBox,
1250 orientation: Orientation2D,
1251 skip_self: bool,
1252 any: bool,
1253 ) -> Option<WidgetFocusInfo> {
1254 let self_id = self.info.id();
1255 let scope_id = scope.info.id();
1256
1257 let skip_parent = if self.is_focusable() {
1259 None
1260 } else {
1261 self.ancestors().next().map(|w| w.info.id())
1262 };
1263
1264 let filter = |w: &WidgetFocusInfo| {
1265 let mut up_to_scope = w.self_and_ancestors().take_while(|w| w.info.id() != scope_id);
1266
1267 if skip_self {
1268 up_to_scope.all(|w| w.info.id() != self_id && !w.focus_info().skip_directional())
1269 } else {
1270 up_to_scope.all(|w| !w.focus_info().skip_directional())
1271 }
1272 };
1273
1274 let origin_center = origin.center();
1275
1276 let mut oriented = scope
1277 .info
1278 .oriented(origin_center, Px::MAX, orientation)
1279 .chain(
1280 scope
1282 .info
1283 .oriented_box(origin, origin.width().max(origin.height()) * Px(2), orientation)
1284 .filter(|w| !w.inner_bounds().to_box2d().intersects(&origin)),
1285 )
1286 .focusable(self.focus_disabled_widgets(), self.focus_hidden_widgets())
1287 .filter(|w| w.info.id() != scope_id && Some(w.info.id()) != skip_parent);
1288
1289 if any {
1290 return oriented.find(filter);
1291 }
1292
1293 let parent_range = self.parent().map(|w| w.info.descendants_range()).unwrap_or_default();
1294
1295 let mut ancestor_dist = DistanceKey::NONE_MAX;
1296 let mut ancestor = None;
1297 let mut sibling_dist = DistanceKey::NONE_MAX;
1298 let mut sibling = None;
1299 let mut other_dist = DistanceKey::NONE_MAX;
1300 let mut other = None;
1301
1302 for w in oriented {
1303 if filter(&w) {
1304 let dist = w.info.distance_key(origin_center);
1305
1306 let mut is_ancestor = None;
1307 let mut is_ancestor = || *is_ancestor.get_or_insert_with(|| w.info.is_ancestor(&self.info));
1308
1309 let mut is_sibling = None;
1310 let mut is_sibling = || *is_sibling.get_or_insert_with(|| parent_range.contains(&w.info));
1311
1312 if dist <= ancestor_dist && is_ancestor() {
1313 ancestor_dist = dist;
1314 ancestor = Some(w);
1315 } else if dist <= sibling_dist && is_sibling() {
1316 sibling_dist = dist;
1317 sibling = Some(w);
1318 } else if dist <= other_dist && !is_ancestor() && !is_sibling() {
1319 other_dist = dist;
1320 other = Some(w);
1321 }
1322 }
1323 }
1324
1325 if other_dist <= ancestor_dist && other_dist <= sibling_dist {
1326 other
1327 } else {
1328 sibling.or(ancestor)
1329 }
1330 }
1331
1332 fn directional_next(&self, orientation: Orientation2D) -> Option<WidgetFocusInfo> {
1333 self.directional_next_from(orientation, self.info.inner_bounds().to_box2d())
1334 }
1335
1336 fn directional_next_from(&self, orientation: Orientation2D, from: PxBox) -> Option<WidgetFocusInfo> {
1337 self.scope()
1338 .and_then(|s| self.directional_from(&s, from, orientation, false, false))
1339 }
1340
1341 pub fn focusable_up(&self) -> Option<WidgetFocusInfo> {
1343 self.directional_next(Orientation2D::Above)
1344 }
1345
1346 pub fn focusable_down(&self) -> Option<WidgetFocusInfo> {
1348 self.directional_next(Orientation2D::Below)
1349 }
1350
1351 pub fn focusable_left(&self) -> Option<WidgetFocusInfo> {
1353 self.directional_next(Orientation2D::Left)
1354 }
1355
1356 pub fn focusable_right(&self) -> Option<WidgetFocusInfo> {
1358 self.directional_next(Orientation2D::Right)
1359 }
1360
1361 pub fn next_up(&self) -> Option<WidgetFocusInfo> {
1363 let _span = tracing::trace_span!("next_up").entered();
1364 self.next_up_from(self.info.inner_bounds().to_box2d())
1365 }
1366 fn next_up_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1367 if let Some(scope) = self.scope() {
1368 let scope_info = scope.focus_info();
1369 match scope_info.directional_nav() {
1370 DirectionalNav::None => None,
1371 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
1372 let mut from = scope.info.inner_bounds();
1373 from.origin.y -= Px(1);
1374 from.size.height = Px(1);
1375 scope.next_up_from(from.to_box2d())
1376 }),
1377 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Above, origin),
1378 DirectionalNav::Cycle => {
1379 self.directional_next_from(Orientation2D::Above, origin).or_else(|| {
1380 let mut from_pt = origin.center();
1382 from_pt.y = scope.info.spatial_bounds().max.y;
1383 self.directional_from(
1384 &scope,
1385 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1386 Orientation2D::Above,
1387 false,
1388 false,
1389 )
1390 })
1391 }
1392 }
1393 } else {
1394 None
1395 }
1396 }
1397
1398 pub fn next_right(&self) -> Option<WidgetFocusInfo> {
1400 let _span = tracing::trace_span!("next_right").entered();
1401 self.next_right_from(self.info.inner_bounds().to_box2d())
1402 }
1403 fn next_right_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1404 if let Some(scope) = self.scope() {
1405 let scope_info = scope.focus_info();
1406 match scope_info.directional_nav() {
1407 DirectionalNav::None => None,
1408 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
1409 let mut from = scope.info.inner_bounds();
1410 from.origin.x += from.size.width + Px(1);
1411 from.size.width = Px(1);
1412 scope.next_right_from(from.to_box2d())
1413 }),
1414 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Right, origin),
1415 DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Right, origin).or_else(|| {
1416 let mut from_pt = origin.center();
1418 from_pt.x = scope.info.spatial_bounds().min.x;
1419 self.directional_from(
1420 &scope,
1421 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1422 Orientation2D::Right,
1423 false,
1424 false,
1425 )
1426 }),
1427 }
1428 } else {
1429 None
1430 }
1431 }
1432
1433 pub fn next_down(&self) -> Option<WidgetFocusInfo> {
1435 let _span = tracing::trace_span!("next_down").entered();
1436 self.next_down_from(self.info.inner_bounds().to_box2d())
1437 }
1438 fn next_down_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1439 if let Some(scope) = self.scope() {
1440 let scope_info = scope.focus_info();
1441 match scope_info.directional_nav() {
1442 DirectionalNav::None => None,
1443 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
1444 let mut from = scope.info.inner_bounds();
1445 from.origin.y += from.size.height + Px(1);
1446 from.size.height = Px(1);
1447 scope.next_down_from(from.to_box2d())
1448 }),
1449 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Below, origin),
1450 DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Below, origin).or_else(|| {
1451 let mut from_pt = origin.center();
1453 from_pt.y = scope.info.spatial_bounds().min.y;
1454 self.directional_from(
1455 &scope,
1456 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1457 Orientation2D::Below,
1458 false,
1459 false,
1460 )
1461 }),
1462 }
1463 } else {
1464 None
1465 }
1466 }
1467
1468 pub fn next_left(&self) -> Option<WidgetFocusInfo> {
1470 let _span = tracing::trace_span!("next_left").entered();
1471 self.next_left_from(self.info.inner_bounds().to_box2d())
1472 }
1473 fn next_left_from(&self, origin: PxBox) -> Option<WidgetFocusInfo> {
1474 if let Some(scope) = self.scope() {
1475 let scope_info = scope.focus_info();
1476 match scope_info.directional_nav() {
1477 DirectionalNav::None => None,
1478 DirectionalNav::Continue => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
1479 let mut from = scope.info.inner_bounds();
1480 from.origin.x -= Px(1);
1481 from.size.width = Px(1);
1482 scope.next_left_from(from.to_box2d())
1483 }),
1484 DirectionalNav::Contained => self.directional_next_from(Orientation2D::Left, origin),
1485 DirectionalNav::Cycle => self.directional_next_from(Orientation2D::Left, origin).or_else(|| {
1486 let mut from_pt = origin.center();
1488 from_pt.x = scope.info.spatial_bounds().max.x;
1489 self.directional_from(
1490 &scope,
1491 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1492 Orientation2D::Left,
1493 false,
1494 false,
1495 )
1496 }),
1497 }
1498 } else {
1499 None
1500 }
1501 }
1502
1503 fn enabled_tab_nav(
1504 &self,
1505 scope: &WidgetFocusInfo,
1506 scope_info: FocusInfo,
1507 skip_self: bool,
1508 already_found: FocusNavAction,
1509 ) -> FocusNavAction {
1510 match scope_info.tab_nav() {
1511 TabNav::None => FocusNavAction::empty(),
1512 tab_nav @ (TabNav::Continue | TabNav::Contained) => {
1513 let mut nav = already_found;
1514
1515 if !nav.contains(FocusNavAction::PREV) && self.prev_tab_focusable_impl(skip_self, true).is_some() {
1516 nav |= FocusNavAction::PREV;
1517 }
1518 if !nav.contains(FocusNavAction::NEXT) && self.next_tab_focusable_impl(skip_self, true).is_some() {
1519 nav |= FocusNavAction::NEXT;
1520 }
1521
1522 if !nav.contains(FocusNavAction::PREV | FocusNavAction::NEXT)
1523 && tab_nav == TabNav::Continue
1524 && let Some(p_scope) = scope.scope()
1525 {
1526 nav |= scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, nav)
1527 }
1528 nav
1529 }
1530 TabNav::Cycle => {
1531 if scope.descendants().tree_filter(Self::filter_tab_skip).any(|w| &w != self) {
1532 FocusNavAction::PREV | FocusNavAction::NEXT
1533 } else {
1534 FocusNavAction::empty()
1535 }
1536 }
1537 TabNav::Once => {
1538 if let Some(p_scope) = scope.scope() {
1539 scope.enabled_tab_nav(&p_scope, p_scope.focus_info(), true, already_found)
1540 } else {
1541 FocusNavAction::empty()
1542 }
1543 }
1544 }
1545 }
1546
1547 fn enabled_directional_nav(
1548 &self,
1549 scope: &WidgetFocusInfo,
1550 scope_info: FocusInfo,
1551 skip_self: bool,
1552 already_found: FocusNavAction,
1553 ) -> FocusNavAction {
1554 let directional_nav = scope_info.directional_nav();
1555
1556 if directional_nav == DirectionalNav::None {
1557 return FocusNavAction::empty();
1558 }
1559
1560 let mut nav = already_found;
1561 let from_pt = self.info.inner_bounds().to_box2d();
1562
1563 if !nav.contains(FocusNavAction::UP)
1564 && self
1565 .directional_from(scope, from_pt, Orientation2D::Above, skip_self, true)
1566 .is_some()
1567 {
1568 nav |= FocusNavAction::UP;
1569 }
1570 if !nav.contains(FocusNavAction::RIGHT)
1571 && self
1572 .directional_from(scope, from_pt, Orientation2D::Right, skip_self, true)
1573 .is_some()
1574 {
1575 nav |= FocusNavAction::RIGHT;
1576 }
1577 if !nav.contains(FocusNavAction::DOWN)
1578 && self
1579 .directional_from(scope, from_pt, Orientation2D::Below, skip_self, true)
1580 .is_some()
1581 {
1582 nav |= FocusNavAction::DOWN;
1583 }
1584 if !nav.contains(FocusNavAction::LEFT)
1585 && self
1586 .directional_from(scope, from_pt, Orientation2D::Left, skip_self, true)
1587 .is_some()
1588 {
1589 nav |= FocusNavAction::LEFT;
1590 }
1591
1592 if !nav.contains(FocusNavAction::DIRECTIONAL) {
1593 match directional_nav {
1594 DirectionalNav::Continue => {
1595 if let Some(p_scope) = scope.scope() {
1596 nav |= scope.enabled_directional_nav(&p_scope, p_scope.focus_info(), true, nav);
1597 }
1598 }
1599 DirectionalNav::Cycle => {
1600 let scope_bounds = scope.info.inner_bounds();
1601 if !nav.contains(FocusNavAction::UP) {
1602 let mut from_pt = from_pt.center();
1603 from_pt.y = scope_bounds.max().y;
1604 if self
1605 .directional_from(
1606 scope,
1607 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1608 Orientation2D::Above,
1609 true,
1610 true,
1611 )
1612 .is_some()
1613 {
1614 nav |= FocusNavAction::UP;
1615 }
1616 }
1617 if !nav.contains(FocusNavAction::RIGHT) {
1618 let mut from_pt = from_pt.center();
1619 from_pt.x = scope_bounds.min().x;
1620 if self
1621 .directional_from(
1622 scope,
1623 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1624 Orientation2D::Right,
1625 true,
1626 true,
1627 )
1628 .is_some()
1629 {
1630 nav |= FocusNavAction::RIGHT;
1631 }
1632 }
1633 if !nav.contains(FocusNavAction::DOWN) {
1634 let mut from_pt = from_pt.center();
1635 from_pt.y = scope_bounds.min().y;
1636 if self
1637 .directional_from(
1638 scope,
1639 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1640 Orientation2D::Below,
1641 true,
1642 true,
1643 )
1644 .is_some()
1645 {
1646 nav |= FocusNavAction::DOWN;
1647 }
1648 }
1649 if !nav.contains(FocusNavAction::LEFT) {
1650 let mut from_pt = from_pt.center();
1651 from_pt.x = scope_bounds.max().x;
1652 if self
1653 .directional_from(
1654 scope,
1655 PxRect::new(from_pt, PxSize::splat(Px(1))).to_box2d(),
1656 Orientation2D::Left,
1657 true,
1658 true,
1659 )
1660 .is_some()
1661 {
1662 nav |= FocusNavAction::LEFT;
1663 }
1664 }
1665
1666 if !nav.contains(FocusNavAction::DIRECTIONAL) {
1667 let info = self.focus_info();
1668
1669 if info.is_scope() && matches!(info.directional_nav(), DirectionalNav::Continue) {
1670 if nav.contains(FocusNavAction::UP) || nav.contains(FocusNavAction::DOWN) {
1672 nav |= FocusNavAction::UP | FocusNavAction::DOWN;
1673 }
1674 if nav.contains(FocusNavAction::LEFT) || nav.contains(FocusNavAction::RIGHT) {
1675 nav |= FocusNavAction::LEFT | FocusNavAction::RIGHT;
1676 }
1677 }
1678 }
1679 }
1680 _ => {}
1681 }
1682 }
1683
1684 nav
1685 }
1686
1687 pub fn enabled_nav(&self) -> FocusNavAction {
1689 let _span = tracing::trace_span!("enabled_nav").entered();
1690
1691 let mut nav = FocusNavAction::empty();
1692
1693 if let Some(scope) = self.scope() {
1694 nav |= FocusNavAction::EXIT;
1695 nav.set(FocusNavAction::ENTER, self.descendants().next().is_some());
1696
1697 let scope_info = scope.focus_info();
1698
1699 nav |= self.enabled_tab_nav(&scope, scope_info, false, FocusNavAction::empty());
1700 nav |= self.enabled_directional_nav(&scope, scope_info, false, FocusNavAction::empty());
1701 }
1702
1703 nav.set(FocusNavAction::ALT, self.in_alt_scope() || self.alt_scope().is_some());
1704
1705 nav
1706 }
1707}
1708impl_from_and_into_var! {
1709 fn from(focus_info: WidgetFocusInfo) -> WidgetInfo {
1710 focus_info.info
1711 }
1712}
1713
1714#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
1716pub enum FocusInfo {
1717 NotFocusable,
1719 Focusable {
1721 tab_index: TabIndex,
1723 skip_directional: bool,
1725 },
1726 FocusScope {
1728 tab_index: TabIndex,
1730 skip_directional: bool,
1732 tab_nav: TabNav,
1734 directional_nav: DirectionalNav,
1736 on_focus: FocusScopeOnFocus,
1738 alt: bool,
1740 },
1741}
1742
1743#[derive(Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
1745pub enum FocusScopeOnFocus {
1746 Widget,
1748 FirstDescendant,
1758 LastFocused,
1770
1771 FirstDescendantIgnoreBounds,
1779
1780 LastFocusedIgnoreBounds,
1788}
1789impl fmt::Debug for FocusScopeOnFocus {
1790 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1791 if f.alternate() {
1792 write!(f, "FocusScopeOnFocus::")?;
1793 }
1794 match self {
1795 FocusScopeOnFocus::Widget => write!(f, "Widget"),
1796 FocusScopeOnFocus::FirstDescendant => write!(f, "FirstDescendant"),
1797 FocusScopeOnFocus::LastFocused => write!(f, "LastFocused"),
1798 FocusScopeOnFocus::FirstDescendantIgnoreBounds => write!(f, "FirstDescendantIgnoreBounds"),
1799 FocusScopeOnFocus::LastFocusedIgnoreBounds => write!(f, "LastFocusedIgnoreBounds"),
1800 }
1801 }
1802}
1803impl Default for FocusScopeOnFocus {
1804 fn default() -> Self {
1806 FocusScopeOnFocus::FirstDescendant
1807 }
1808}
1809
1810impl FocusInfo {
1811 pub fn is_focusable(self) -> bool {
1813 !matches!(self, FocusInfo::NotFocusable)
1814 }
1815
1816 pub fn is_scope(self) -> bool {
1818 matches!(self, FocusInfo::FocusScope { .. })
1819 }
1820
1821 pub fn is_alt_scope(self) -> bool {
1823 match self {
1824 FocusInfo::FocusScope { alt, .. } => alt,
1825 _ => false,
1826 }
1827 }
1828
1829 pub fn tab_nav(self) -> TabNav {
1837 match self {
1838 FocusInfo::FocusScope { tab_nav, .. } => tab_nav,
1839 FocusInfo::Focusable { .. } => TabNav::Continue,
1840 FocusInfo::NotFocusable => TabNav::None,
1841 }
1842 }
1843
1844 pub fn directional_nav(self) -> DirectionalNav {
1852 match self {
1853 FocusInfo::FocusScope { directional_nav, .. } => directional_nav,
1854 FocusInfo::Focusable { .. } => DirectionalNav::Continue,
1855 FocusInfo::NotFocusable => DirectionalNav::None,
1856 }
1857 }
1858
1859 pub fn tab_index(self) -> TabIndex {
1866 match self {
1867 FocusInfo::Focusable { tab_index, .. } => tab_index,
1868 FocusInfo::FocusScope { tab_index, .. } => tab_index,
1869 FocusInfo::NotFocusable => TabIndex::SKIP,
1870 }
1871 }
1872
1873 pub fn skip_directional(self) -> bool {
1880 match self {
1881 FocusInfo::Focusable { skip_directional, .. } => skip_directional,
1882 FocusInfo::FocusScope { skip_directional, .. } => skip_directional,
1883 FocusInfo::NotFocusable => true,
1884 }
1885 }
1886
1887 pub fn scope_on_focus(self) -> FocusScopeOnFocus {
1894 match self {
1895 FocusInfo::FocusScope { on_focus, .. } => on_focus,
1896 _ => FocusScopeOnFocus::Widget,
1897 }
1898 }
1899}
1900
1901static_id! {
1902 static ref FOCUS_INFO_ID: StateId<FocusInfoData>;
1903 static ref FOCUS_TREE_ID: StateId<FocusTreeData>;
1904}
1905
1906#[derive(Default)]
1907pub(super) struct FocusTreeData {
1908 alt_scopes: Mutex<IdSet<WidgetId>>,
1909}
1910impl FocusTreeData {
1911 pub(super) fn consolidate_alt_scopes(prev_tree: &WidgetInfoTree, new_tree: &WidgetInfoTree) {
1912 let prev = prev_tree
1915 .build_meta()
1916 .get(*FOCUS_TREE_ID)
1917 .map(|d| d.alt_scopes.lock().clone())
1918 .unwrap_or_default();
1919
1920 let mut alt_scopes = prev;
1921 if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
1922 alt_scopes.extend(data.alt_scopes.lock().iter());
1923 }
1924
1925 alt_scopes.retain(|id| {
1926 if let Some(wgt) = new_tree.get(*id)
1927 && let Some(info) = wgt.meta().get(*FOCUS_INFO_ID)
1928 && info.build().is_alt_scope()
1929 {
1930 for parent in wgt.ancestors() {
1931 if let Some(info) = parent.meta().get(*FOCUS_INFO_ID)
1932 && info.build().is_scope()
1933 {
1934 info.inner_alt.store(Some(*id), Relaxed);
1935 break;
1936 }
1937 }
1938
1939 return true;
1940 }
1941 false
1942 });
1943
1944 if let Some(data) = new_tree.build_meta().get(*FOCUS_TREE_ID) {
1945 *data.alt_scopes.lock() = alt_scopes;
1946 }
1947 }
1948}
1949
1950#[derive(Default, Debug)]
1951struct FocusInfoData {
1952 focusable: Option<bool>,
1953 scope: Option<bool>,
1954 alt_scope: bool,
1955 on_focus: FocusScopeOnFocus,
1956 tab_index: Option<TabIndex>,
1957 tab_nav: Option<TabNav>,
1958 directional_nav: Option<DirectionalNav>,
1959 skip_directional: Option<bool>,
1960
1961 inner_alt: Atomic<Option<WidgetId>>,
1962
1963 access_handler_registered: bool,
1964}
1965impl FocusInfoData {
1966 pub fn build(&self) -> FocusInfo {
1970 match (self.focusable, self.scope, self.tab_index, self.tab_nav, self.directional_nav) {
1971 (Some(false), _, _, _, _) => FocusInfo::NotFocusable,
1973
1974 (_, Some(true), idx, tab, dir) | (_, None, idx, tab @ Some(_), dir) | (_, None, idx, tab, dir @ Some(_)) => {
1978 FocusInfo::FocusScope {
1979 tab_index: idx.unwrap_or(TabIndex::AUTO),
1980 skip_directional: self.skip_directional.unwrap_or_default(),
1981 tab_nav: tab.unwrap_or(TabNav::Continue),
1982 directional_nav: dir.unwrap_or(DirectionalNav::Continue),
1983 alt: self.alt_scope,
1984 on_focus: self.on_focus,
1985 }
1986 }
1987
1988 (Some(true), _, idx, _, _) | (_, _, idx @ Some(_), _, _) => FocusInfo::Focusable {
1991 tab_index: idx.unwrap_or(TabIndex::AUTO),
1992 skip_directional: self.skip_directional.unwrap_or_default(),
1993 },
1994
1995 _ => FocusInfo::NotFocusable,
1996 }
1997 }
1998}
1999
2000pub struct FocusInfoBuilder<'a>(&'a mut WidgetInfoBuilder);
2045impl<'a> FocusInfoBuilder<'a> {
2046 pub fn new(builder: &'a mut WidgetInfoBuilder) -> Self {
2048 let mut r = Self(builder);
2049 r.with_tree_data(|_| {}); r
2051 }
2052
2053 fn with_data<R>(&mut self, visitor: impl FnOnce(&mut FocusInfoData) -> R) -> R {
2054 let mut access = self.0.access().is_some();
2055
2056 let r = self.0.with_meta(|m| {
2057 let data = m.into_entry(*FOCUS_INFO_ID).or_default();
2058
2059 if access {
2060 access = !std::mem::replace(&mut data.access_handler_registered, true);
2061 }
2062
2063 visitor(data)
2064 });
2065
2066 if access {
2067 self.0.access().unwrap().on_access_build(|args| {
2069 if args.widget.info().clone().into_focusable(true, false).is_some() {
2070 args.node.commands.push(zng_view_api::access::AccessCmdName::Focus);
2071 }
2072 });
2073 }
2074
2075 r
2076 }
2077
2078 fn with_tree_data<R>(&mut self, visitor: impl FnOnce(&mut FocusTreeData) -> R) -> R {
2079 self.0.with_build_meta(|m| visitor(m.into_entry(*FOCUS_TREE_ID).or_default()))
2080 }
2081
2082 pub fn focusable(&mut self, is_focusable: bool) -> &mut Self {
2084 self.with_data(|data| {
2085 data.focusable = Some(is_focusable);
2086 });
2087 self
2088 }
2089
2090 pub fn focusable_passive(&mut self, is_focusable: bool) -> &mut Self {
2094 self.with_data(|data| {
2095 if data.focusable.is_none() {
2096 data.focusable = Some(is_focusable);
2097 }
2098 });
2099 self
2100 }
2101
2102 pub fn scope(&mut self, is_focus_scope: bool) -> &mut Self {
2104 self.with_data(|data| {
2105 data.scope = Some(is_focus_scope);
2106 });
2107 self
2108 }
2109
2110 pub fn alt_scope(&mut self, is_alt_focus_scope: bool) -> &mut Self {
2114 self.with_data(|data| {
2115 data.alt_scope = is_alt_focus_scope;
2116 if is_alt_focus_scope {
2117 data.scope = Some(true);
2118
2119 if data.tab_index.is_none() {
2120 data.tab_index = Some(TabIndex::SKIP);
2121 }
2122 if data.tab_nav.is_none() {
2123 data.tab_nav = Some(TabNav::Cycle);
2124 }
2125 if data.directional_nav.is_none() {
2126 data.directional_nav = Some(DirectionalNav::Cycle);
2127 }
2128 if data.skip_directional.is_none() {
2129 data.skip_directional = Some(true);
2130 }
2131 }
2132 });
2133 if is_alt_focus_scope {
2134 let wgt_id = self.0.widget_id();
2135 self.with_tree_data(|d| d.alt_scopes.lock().insert(wgt_id));
2136 }
2137 self
2138 }
2139
2140 pub fn on_focus(&mut self, as_focus_scope_on_focus: FocusScopeOnFocus) -> &mut Self {
2142 self.with_data(|data| {
2143 data.on_focus = as_focus_scope_on_focus;
2144 });
2145 self
2146 }
2147
2148 pub fn tab_index(&mut self, tab_index: TabIndex) -> &mut Self {
2150 self.with_data(|data| {
2151 data.tab_index = Some(tab_index);
2152 });
2153 self
2154 }
2155
2156 pub fn tab_nav(&mut self, scope_tab_nav: TabNav) -> &mut Self {
2158 self.with_data(|data| {
2159 data.tab_nav = Some(scope_tab_nav);
2160 });
2161 self
2162 }
2163
2164 pub fn directional_nav(&mut self, scope_directional_nav: DirectionalNav) -> &mut Self {
2166 self.with_data(|data| {
2167 data.directional_nav = Some(scope_directional_nav);
2168 });
2169 self
2170 }
2171 pub fn skip_directional(&mut self, skip: bool) -> &mut Self {
2173 self.with_data(|data| {
2174 data.skip_directional = Some(skip);
2175 });
2176 self
2177 }
2178}