1pub mod cmd;
17pub mod iter;
18
19mod focus_info;
20pub use focus_info::*;
21use zng_unique_id::{IdEntry, IdMap};
22
23use std::{mem, time::Duration};
24use zng_app::{
25 APP, DInstant,
26 access::{ACCESS_CLICK_EVENT, ACCESS_FOCUS_EVENT, ACCESS_FOCUS_NAV_ORIGIN_EVENT},
27 event::event,
28 event_args, hn,
29 update::UPDATES,
30 view_process::raw_events::RAW_KEY_INPUT_EVENT,
31 widget::{
32 WidgetId,
33 info::{InteractionPath, WIDGET_TREE_CHANGED_EVENT, WidgetInfoTree},
34 },
35 window::WindowId,
36};
37use zng_app_context::app_local;
38use zng_ext_window::{FocusIndicator, WINDOW_FOCUS_CHANGED_EVENT, WINDOWS, WINDOWS_FOCUS, WindowInstanceState};
39use zng_layout::unit::TimeUnits as _;
40use zng_var::{Var, WeakVar, const_var, var};
41
42use crate::{mouse::MOUSE_INPUT_EVENT, touch::TOUCH_INPUT_EVENT};
43
44event_args! {
45 pub struct FocusChangedArgs {
47 pub prev_focus: Option<InteractionPath>,
49
50 pub new_focus: Option<InteractionPath>,
52
53 pub highlight: bool,
59
60 pub cause: FocusChangedCause,
62
63 pub enabled_nav: FocusNavAction,
67
68 ..
69
70 fn is_in_target(&self, id: WidgetId) -> bool {
72 if let Some(prev) = &self.prev_focus
73 && prev.contains(id)
74 {
75 return true;
76 }
77 if let Some(new) = &self.new_focus
78 && new.contains(id)
79 {
80 return true;
81 }
82 false
83 }
84 }
85
86 pub struct ReturnFocusChangedArgs {
88 pub scope: Option<InteractionPath>,
92
93 pub prev_return: Option<InteractionPath>,
95
96 pub new_return: Option<InteractionPath>,
98
99 ..
100
101 fn is_in_target(&self, id: WidgetId) -> bool {
104 if let Some(scope) = &self.scope
105 && scope.contains(id)
106 {
107 return true;
108 }
109 if let Some(prev_return) = &self.prev_return
110 && prev_return.contains(id)
111 {
112 return true;
113 }
114 if let Some(new_return) = &self.new_return
115 && new_return.contains(id)
116 {
117 return true;
118 }
119 false
120 }
121 }
122}
123
124impl FocusChangedArgs {
125 pub fn is_widget_move(&self) -> bool {
127 match (&self.prev_focus, &self.new_focus) {
128 (Some(prev), Some(new)) => prev.widget_id() == new.widget_id() && prev.as_path() != new.as_path(),
129 _ => false,
130 }
131 }
132
133 pub fn is_enabled_change(&self) -> bool {
135 match (&self.prev_focus, &self.new_focus) {
136 (Some(prev), Some(new)) => prev.as_path() == new.as_path() && prev.disabled_index() != new.disabled_index(),
137 _ => false,
138 }
139 }
140
141 pub fn is_highlight_changed(&self) -> bool {
143 self.prev_focus == self.new_focus
144 }
145
146 pub fn is_focus(&self, widget_id: WidgetId) -> bool {
148 match (&self.prev_focus, &self.new_focus) {
149 (Some(prev), Some(new)) => prev.widget_id() != widget_id && new.widget_id() == widget_id,
150 (None, Some(new)) => new.widget_id() == widget_id,
151 (_, None) => false,
152 }
153 }
154
155 pub fn is_blur(&self, widget_id: WidgetId) -> bool {
157 match (&self.prev_focus, &self.new_focus) {
158 (Some(prev), Some(new)) => prev.widget_id() == widget_id && new.widget_id() != widget_id,
159 (Some(prev), None) => prev.widget_id() == widget_id,
160 (None, _) => false,
161 }
162 }
163
164 pub fn is_focus_enter(&self, widget_id: WidgetId) -> bool {
166 match (&self.prev_focus, &self.new_focus) {
167 (Some(prev), Some(new)) => !prev.contains(widget_id) && new.contains(widget_id),
168 (None, Some(new)) => new.contains(widget_id),
169 (_, None) => false,
170 }
171 }
172
173 pub fn is_focus_enter_enabled(&self, widget_id: WidgetId) -> bool {
176 match (&self.prev_focus, &self.new_focus) {
177 (Some(prev), Some(new)) => !prev.contains_enabled(widget_id) && new.contains_enabled(widget_id),
178 (None, Some(new)) => new.contains_enabled(widget_id),
179 (_, None) => false,
180 }
181 }
182
183 pub fn is_focus_leave(&self, widget_id: WidgetId) -> bool {
185 match (&self.prev_focus, &self.new_focus) {
186 (Some(prev), Some(new)) => prev.contains(widget_id) && !new.contains(widget_id),
187 (Some(prev), None) => prev.contains(widget_id),
188 (None, _) => false,
189 }
190 }
191
192 pub fn is_focus_leave_enabled(&self, widget_id: WidgetId) -> bool {
195 match (&self.prev_focus, &self.new_focus) {
196 (Some(prev), Some(new)) => prev.contains_enabled(widget_id) && !new.contains_enabled(widget_id),
197 (Some(prev), None) => prev.contains_enabled(widget_id),
198 (None, _) => false,
199 }
200 }
201
202 pub fn is_focused(&self, widget_id: WidgetId) -> bool {
204 self.new_focus.as_ref().map(|p| p.widget_id() == widget_id).unwrap_or(false)
205 }
206
207 pub fn is_focus_within(&self, widget_id: WidgetId) -> bool {
209 self.new_focus.as_ref().map(|p| p.contains(widget_id)).unwrap_or(false)
210 }
211}
212
213impl ReturnFocusChangedArgs {
214 pub fn is_widget_move(&self) -> bool {
216 match (&self.prev_return, &self.new_return) {
217 (Some(prev), Some(new)) => prev.widget_id() == new.widget_id() && prev != new,
218 _ => false,
219 }
220 }
221
222 pub fn is_alt_return(&self) -> bool {
225 if let Some(scope) = &self.scope {
226 match (&self.prev_return, &self.new_return) {
227 (Some(prev), None) => !prev.contains(scope.widget_id()),
228 (None, Some(new)) => !new.contains(scope.widget_id()),
229 _ => false,
230 }
231 } else {
232 false
233 }
234 }
235
236 pub fn lost_return_focus(&self, widget_id: WidgetId) -> bool {
241 self.prev_return.as_ref().map(|p| p.contains(widget_id)).unwrap_or(false)
242 && self.new_return.as_ref().map(|p| !p.contains(widget_id)).unwrap_or(true)
243 }
244
245 pub fn got_return_focus(&self, widget_id: WidgetId) -> bool {
250 self.prev_return.as_ref().map(|p| !p.contains(widget_id)).unwrap_or(true)
251 && self.new_return.as_ref().map(|p| p.contains(widget_id)).unwrap_or(false)
252 }
253
254 pub fn was_return_focus(&self, widget_id: WidgetId) -> bool {
259 self.prev_return.as_ref().map(|p| p.widget_id() == widget_id).unwrap_or(false)
260 && self.new_return.as_ref().map(|p| p.widget_id() != widget_id).unwrap_or(true)
261 }
262
263 pub fn is_return_focus(&self, widget_id: WidgetId) -> bool {
268 self.prev_return.as_ref().map(|p| p.widget_id() != widget_id).unwrap_or(true)
269 && self.new_return.as_ref().map(|p| p.widget_id() == widget_id).unwrap_or(false)
270 }
271
272 pub fn is_return_focus_enter(&self, widget_id: WidgetId) -> bool {
274 match (&self.prev_return, &self.new_return) {
275 (Some(prev), Some(new)) => !prev.contains(widget_id) && new.contains(widget_id),
276 (None, Some(new)) => new.contains(widget_id),
277 (_, None) => false,
278 }
279 }
280
281 pub fn is_return_focus_leave(&self, widget_id: WidgetId) -> bool {
283 match (&self.prev_return, &self.new_return) {
284 (Some(prev), Some(new)) => prev.contains(widget_id) && !new.contains(widget_id),
285 (Some(prev), None) => prev.contains(widget_id),
286 (None, _) => false,
287 }
288 }
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
293pub enum FocusChangedCause {
294 Request(FocusRequest),
296
297 ScopeGotFocus(bool),
301
302 Recovery,
304}
305impl FocusChangedCause {
306 pub fn request_target(self) -> Option<FocusTarget> {
308 match self {
309 Self::Request(r) => Some(r.target),
310 _ => None,
311 }
312 }
313}
314
315event! {
316 pub static FOCUS_CHANGED_EVENT: FocusChangedArgs {
318 let _ = FOCUS_SV.read();
319 };
320
321 pub static RETURN_FOCUS_CHANGED_EVENT: ReturnFocusChangedArgs {
323 let _ = FOCUS_SV.read();
324 };
325}
326
327pub struct FOCUS;
329impl FOCUS {
330 #[must_use]
335 pub fn auto_highlight(&self) -> Var<Option<Duration>> {
336 FOCUS_SV.read().auto_highlight.clone()
337 }
338
339 #[must_use]
350 pub fn focus_disabled_widgets(&self) -> Var<bool> {
351 FOCUS_SV.read().focus_disabled_widgets.clone()
352 }
353
354 #[must_use]
365 pub fn focus_hidden_widgets(&self) -> Var<bool> {
366 FOCUS_SV.read().focus_hidden_widgets.clone()
367 }
368
369 #[must_use]
381 pub fn navigation_origin(&self) -> Var<Option<WidgetId>> {
382 FOCUS_SV.read().navigation_origin.clone()
383 }
384
385 #[must_use]
387 pub fn focused(&self) -> Var<Option<InteractionPath>> {
388 FOCUS_SV.read().focused.read_only()
389 }
390
391 #[must_use]
393 pub fn return_focused(&self, scope_id: WidgetId) -> Var<Option<InteractionPath>> {
394 FOCUS_SV
395 .write()
396 .return_focused
397 .entry(scope_id)
398 .or_insert_with(|| var(None))
399 .read_only()
400 }
401
402 pub fn is_window_focused(&self, window_id: WindowId) -> Var<bool> {
406 self.focused().map(move |p| matches!(p, Some(p) if p.window_id() == window_id))
407 }
408
409 pub fn is_focus_within(&self, widget_id: WidgetId) -> Var<bool> {
413 self.focused().map(move |p| matches!(p, Some(p) if p.contains(widget_id)))
414 }
415
416 pub fn is_focused(&self, widget_id: WidgetId) -> Var<bool> {
420 self.focused().map(move |p| matches!(p, Some(p) if p.widget_id() == widget_id))
421 }
422
423 #[must_use]
425 pub fn is_highlighting(&self) -> Var<bool> {
426 FOCUS_SV.read().is_highlighting.read_only()
427 }
428
429 #[must_use]
433 pub fn alt_return(&self) -> Var<Option<InteractionPath>> {
434 {
435 let s = FOCUS_SV.read();
436 if let Some(r) = s.alt_return.upgrade() {
437 return r;
438 }
439 }
440 let focused = FOCUS_SV.read().focused.clone();
441 let r = focused.flat_map(|p| {
442 let s = FOCUS_SV.read();
443 if let Some(p) = p
444 && let Some(tree) = WINDOWS.widget_tree(p.window_id())
445 && let Some(wgt) = tree.get(p.widget_id())
446 && let Some(wgt) = wgt.into_focusable(s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get())
447 && let Some(scope) = wgt.self_and_ancestors().find(|w| w.is_alt_scope())
448 {
449 drop(s);
450 return FOCUS.return_focused(scope.info().id());
451 }
452 const_var(None)
453 });
454 FOCUS_SV.write().alt_return = r.downgrade();
455 r
456 }
457
458 pub fn focus(&self, request: FocusRequest) {
462 let mut f = FOCUS_SV.write();
463 if f.request.is_none() && f.fallback_request.is_none() {
464 UPDATES.once_update("FOCUS.focus", || {
465 FOCUS_SV.write().fulfill_request(None, true);
466 });
467 }
468 if request.fallback_only {
469 f.fallback_request = Some(request);
470 } else {
471 f.request = Some(request);
472 }
473 }
474
475 pub fn highlight(&self) {
479 let mut f = FOCUS_SV.write();
480 if f.request_highlight {
481 return;
482 }
483
484 f.request_highlight = true;
485 if f.request.is_none() && f.fallback_request.is_none() {
486 UPDATES.once_update("FOCUS.highlight", || {
488 FOCUS_SV.write().fulfill_highlight_request();
489 });
490 }
491 }
492
493 pub fn highlight_within_auto(&self) {
498 let dur = FOCUS_SV.read().auto_highlight.get();
499 if let Some(dur) = dur
500 && last_keyboard_event().elapsed() <= dur
501 {
502 self.highlight();
503 }
504 }
505
506 pub fn focus_widget(&self, widget_id: impl Into<WidgetId>, highlight: bool) {
517 self.focus(FocusRequest::direct(widget_id.into(), highlight));
518 }
519
520 pub fn focus_window(&self, window_id: impl Into<WindowId>, highlight: bool) {
522 self.focus_window_impl(window_id.into(), highlight);
523 }
524 fn focus_window_impl(&self, window_id: WindowId, highlight: bool) {
525 UPDATES.once_update("FOCUS.focus_window", move || {
526 if let Some(tree) = WINDOWS.widget_tree(window_id) {
527 FOCUS.focus_widget_or_enter(tree.root().id(), false, highlight);
528 }
529 });
530 }
531
532 pub fn focus_widget_or_exit(&self, widget_id: impl Into<WidgetId>, navigation_origin: bool, highlight: bool) {
543 self.focus(FocusRequest::direct_or_exit(widget_id.into(), navigation_origin, highlight));
544 }
545
546 pub fn focus_widget_or_enter(&self, widget_id: impl Into<WidgetId>, navigation_origin: bool, highlight: bool) {
558 self.focus(FocusRequest::direct_or_enter(widget_id.into(), navigation_origin, highlight));
559 }
560
561 pub fn focus_widget_or_related(&self, widget_id: impl Into<WidgetId>, navigation_origin: bool, highlight: bool) {
574 self.focus(FocusRequest::direct_or_related(widget_id.into(), navigation_origin, highlight));
575 }
576
577 pub fn focus_enter(&self) {
583 let req = FocusRequest::enter(FOCUS_SV.read().is_highlighting.get());
584 self.focus(req);
585 }
586
587 pub fn focus_exit(&self, recursive_alt: bool) {
596 let req = FocusRequest::exit(recursive_alt, FOCUS_SV.read().is_highlighting.get());
597 self.focus(req)
598 }
599
600 pub fn focus_next(&self) {
606 let req = FocusRequest::next(FOCUS_SV.read().is_highlighting.get());
607 self.focus(req);
608 }
609
610 pub fn focus_prev(&self) {
616 let req = FocusRequest::prev(FOCUS_SV.read().is_highlighting.get());
617 self.focus(req);
618 }
619
620 pub fn focus_up(&self) {
626 let req = FocusRequest::up(FOCUS_SV.read().is_highlighting.get());
627 self.focus(req);
628 }
629
630 pub fn focus_right(&self) {
636 let req = FocusRequest::right(FOCUS_SV.read().is_highlighting.get());
637 self.focus(req);
638 }
639
640 pub fn focus_down(&self) {
646 let req = FocusRequest::down(FOCUS_SV.read().is_highlighting.get());
647 self.focus(req);
648 }
649
650 pub fn focus_left(&self) {
656 let req = FocusRequest::left(FOCUS_SV.read().is_highlighting.get());
657 self.focus(req);
658 }
659
660 pub fn focus_alt(&self) {
666 let req = FocusRequest::alt(FOCUS_SV.read().is_highlighting.get());
667 self.focus(req);
668 }
669}
670
671zng_env::on_process_start!(|_| {
672 APP.on_init(hn!(|args| {
673 WINDOWS_FOCUS.hook_focus_service(FOCUS.focused());
674 }));
675});
676
677app_local! {
678 static FOCUS_SV: FocusService = FocusService::new();
679}
680struct FocusService {
681 auto_highlight: Var<Option<Duration>>,
682 focus_disabled_widgets: Var<bool>,
683 focus_hidden_widgets: Var<bool>,
684
685 navigation_origin: Var<Option<WidgetId>>,
686 focused: Var<Option<InteractionPath>>,
687 return_focused: IdMap<WidgetId, Var<Option<InteractionPath>>>,
688 is_highlighting: Var<bool>,
689 alt_return: WeakVar<Option<InteractionPath>>,
690
691 commands: cmd::FocusCommands,
692
693 request: Option<FocusRequest>,
694 fallback_request: Option<FocusRequest>,
695 request_highlight: bool,
696
697 enabled_nav: FocusNavAction,
698}
699fn last_keyboard_event() -> DInstant {
700 RAW_KEY_INPUT_EVENT
701 .with(|v| v.latest().map(|a| a.timestamp))
702 .unwrap_or(DInstant::EPOCH)
703}
704impl FocusService {
705 fn new() -> Self {
706 hooks();
707 let s = Self {
708 auto_highlight: var(Some(300.ms())),
709 focus_disabled_widgets: var(true),
710 focus_hidden_widgets: var(true),
711
712 navigation_origin: var(None),
713 focused: var(None),
714 return_focused: IdMap::default(),
715 is_highlighting: var(false),
716 alt_return: WeakVar::new(),
717
718 commands: cmd::FocusCommands::new(),
719
720 request: None,
721 fallback_request: None,
722 request_highlight: false,
723
724 enabled_nav: FocusNavAction::empty(),
725 };
726 fn refresh(_: &zng_var::VarHookArgs<bool>) -> bool {
727 let mut s = FOCUS_SV.write();
728 if let Some(id) = s.focused.with(|p| p.as_ref().map(|p| p.widget_id())) {
729 tracing::trace!("focus_disabled_widgets or focus_hidden_widgets changed recovery");
730 s.focus_direct_recovery(id, None);
731 }
732 true
733 }
734 s.focus_disabled_widgets.hook(refresh).perm();
735 s.focus_hidden_widgets.hook(refresh).perm();
736 s
737 }
738
739 fn fulfill_request(&mut self, tree_hint: Option<&WidgetInfoTree>, is_service_request: bool) {
740 let mut request = self.request.take().or(self.fallback_request.take()).unwrap();
742
743 if mem::take(&mut self.request_highlight) {
744 request.highlight = true;
746 } else if !request.highlight
747 && let Some(dur) = self.auto_highlight.get()
748 && last_keyboard_event().elapsed() <= dur
749 {
750 tracing::trace!("last keyboard event within {dur:?}, highlight");
752 request.highlight = true;
753 }
754
755 let focus_disabled = self.focus_disabled_widgets.get();
756 let focus_hidden = self.focus_hidden_widgets.get();
757
758 let current_info = self
760 .focused
761 .with(|p| match p {
762 Some(p) => {
763 if let Some(t) = &tree_hint
764 && t.window_id() == p.window_id()
765 {
766 t.get(p.widget_id())
767 } else {
768 WINDOWS.widget_tree(p.window_id()).and_then(|t| t.get(p.widget_id()))
769 }
770 }
771 None => None,
772 })
773 .map(|i| i.into_focus_info(focus_disabled, focus_hidden));
774
775 let find_wgt = |id| {
777 if let Some(c) = ¤t_info
778 && let Some(r) = c.info().tree().get(id)
779 {
780 return Some(r.into_focus_info(focus_disabled, focus_hidden));
781 }
782 if let Some(t) = &tree_hint
783 && let Some(r) = t.get(id)
784 {
785 return Some(r.into_focus_info(focus_disabled, focus_hidden));
786 }
787 WINDOWS.widget_info(id).map(|r| r.into_focus_info(focus_disabled, focus_hidden))
788 };
789
790 let origin_info = self
792 .navigation_origin
793 .get()
794 .and_then(|id| current_info.as_ref().and_then(|i| i.info().tree().get(id)))
795 .map(|i| i.into_focus_info(focus_disabled, focus_hidden))
796 .or_else(|| current_info.clone());
797
798 let mut new_info = None;
800 let mut new_origin = None;
801 match request.target {
802 FocusTarget::Direct { target } => match find_wgt(target) {
803 Some(w) => {
804 if w.is_focusable() {
805 tracing::trace!("focus {:?}", w.info().id());
806 new_info = Some(w);
807 } else {
808 tracing::debug!("cannot focus {target}, not focusable")
809 }
810 }
811 None => tracing::debug!("cannot focus {target}, not found"),
812 },
813 FocusTarget::DirectOrExit { target, navigation_origin } => match find_wgt(target) {
814 Some(w) => {
815 if w.is_focusable() {
816 tracing::trace!("focus {:?}", w.info().id());
817 new_info = Some(w);
818 } else {
819 tracing::debug!("cannot focus {target}, not focusable, will try ancestors");
820 match w.ancestors().next() {
821 Some(actual) => {
822 tracing::trace!("focusing ancestor {:?}", actual.info().id());
823 new_info = Some(actual);
824 }
825 None => {
826 tracing::debug!("cannot focus {target} or ancestor, none focusable in path");
827 }
828 }
829 if navigation_origin {
830 new_origin = Some(w.info().id());
831 }
832 }
833 }
834 None => tracing::debug!("cannot focus {target} or ancestor, not found"),
835 },
836 FocusTarget::DirectOrEnter { target, navigation_origin } => match find_wgt(target) {
837 Some(w) => {
838 if w.is_focusable() {
839 tracing::trace!("focus {:?}", w.info().id());
840 new_info = Some(w);
841 } else {
842 tracing::debug!("cannot focus {target}, not focusable, will try descendants");
843 match w.first_tab_descendant() {
844 Some(actual) => {
845 tracing::trace!("focusing descendant {:?}", actual.info().id());
846 new_info = Some(actual);
847 }
848 None => {
849 if navigation_origin {
850 new_origin = Some(w.info().id());
851 }
852 tracing::debug!("cannot focus {target} or descendants, none tab focusable in subtree");
853 }
854 }
855 }
856 }
857 None => tracing::debug!("cannot focus {target} or descendants, not found"),
858 },
859 FocusTarget::DirectOrRelated { target, navigation_origin } => match find_wgt(target) {
860 Some(w) => {
861 if w.is_focusable() {
862 tracing::trace!("focus {:?}", w.info().id());
863 new_info = Some(w);
864 } else {
865 tracing::debug!("cannot focus {target}, not focusable, will try descendants and ancestors");
866 match w
867 .first_tab_descendant()
868 .map(|w| (w, false))
869 .or_else(|| w.descendants().next().map(|w| (w, false)))
870 .or_else(|| w.ancestors().next().map(|w| (w, true)))
871 {
872 Some((actual, is_ancestor)) => {
873 if navigation_origin && is_ancestor {
874 new_origin = Some(w.info().id());
875 }
876 tracing::trace!(
877 "focusing {} {:?}",
878 if is_ancestor { "ancestor" } else { "descendant" },
879 actual.info().id()
880 );
881 new_info = Some(actual);
882 }
883 None => {
884 if navigation_origin {
885 new_origin = Some(w.info().id());
886 }
887 tracing::debug!("cannot focus {target} or descendants or ancestors, none focusable")
888 }
889 }
890 }
891 }
892 None => {
893 tracing::debug!("cannot focus {target} or descendants or ancestors, not found");
894 if !is_service_request {
895 let focused = self.focused.get();
896 if let Some(focused) = focused
897 && focused.widget_id() == target
898 {
899 UPDATES.once_next_update("delayed-focus-recovery", move || {
902 let mut s = FOCUS_SV.write();
903 let focus_did_not_change = s.focused.with(|p| p.as_ref() == Some(&focused));
904 if focus_did_not_change && s.request.is_none() {
905 for wgt in focused.widgets_path().iter().rev() {
906 if let Some(wgt) = WINDOWS.widget_info(*wgt) {
907 s.focus_direct_recovery(wgt.id(), Some(wgt.tree()));
908 break;
909 }
910 }
911 }
912 })
913 }
914 }
915 }
916 },
917 FocusTarget::Enter => match &origin_info {
918 Some(i) => {
919 new_info = i.first_tab_descendant();
920 tracing::trace!("enter {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
921 }
922 None => tracing::debug!("cannot enter focused, no current focus"),
923 },
924 FocusTarget::Exit { recursive_alt } => match &origin_info {
925 Some(i) => {
926 let mut alt = i.self_and_ancestors().find(|s| s.is_alt_scope());
927 let mut recursive_alt_scopes = vec![];
928 while let Some(a) = alt.take() {
929 if recursive_alt_scopes.contains(&a.info().id()) {
930 tracing::error!("circular alt return focus, {recursive_alt_scopes:?}");
931 break;
932 }
933
934 if let Some(r) = self.return_focused.get(&a.info().id())
935 && let Some(r) = r.with(|p| p.as_ref().map(|p| p.widget_id()))
936 && let Some(r) = find_wgt(r)
937 && r.is_focusable()
938 {
939 if recursive_alt {
941 recursive_alt_scopes.push(a.info().id());
942 alt = r.self_and_ancestors().find(|s| s.is_alt_scope());
943 if alt.is_some() {
944 continue;
945 }
946 tracing::trace!(
947 "exit {:?}, alt {:?}, return {:?}",
948 i.info().id(),
949 recursive_alt_scopes,
950 r.info().id()
951 );
952 } else {
953 tracing::trace!("exit {:?}, alt {:?}, return {:?}", i.info().id(), a.info().id(), r.info().id());
954 }
955 new_info = Some(r);
956 } else {
957 if recursive_alt {
959 new_info = a.ancestors().find(|w| !w.in_alt_scope());
960 } else {
961 new_info = a.ancestors().next();
962 }
963 tracing::debug!(
964 "exit {:?}, alt {:?}, no return, focus {:?}",
965 i.info().id(),
966 a.info().id(),
967 new_info.as_ref().map(|w| w.info().id())
968 );
969 }
970 }
971 if new_info.is_none() {
972 new_info = i.ancestors().next();
973 tracing::trace!("exit {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
974 }
975 }
976 None => tracing::debug!("cannot exit focused, no current focus"),
977 },
978 FocusTarget::Next => match &origin_info {
979 Some(i) => {
980 new_info = i.next_tab(false);
981 tracing::trace!(
982 "next from {:?}, focus {:?}",
983 i.info().id(),
984 new_info.as_ref().map(|w| w.info().id())
985 );
986 }
987 None => tracing::debug!("cannot focus next, no current focus"),
988 },
989 FocusTarget::Prev => match &origin_info {
990 Some(i) => {
991 new_info = i.prev_tab(false);
992 tracing::trace!(
993 "prev from {:?}, focus {:?}",
994 i.info().id(),
995 new_info.as_ref().map(|w| w.info().id())
996 );
997 }
998 None => tracing::debug!("cannot focus prev, no current focus"),
999 },
1000 FocusTarget::Up => match &origin_info {
1001 Some(i) => {
1002 new_info = i.next_up();
1003 tracing::trace!("up from {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
1004 }
1005 None => tracing::debug!("cannot focus up, no current focus"),
1006 },
1007 FocusTarget::Right => match &origin_info {
1008 Some(i) => {
1009 new_info = i.next_right();
1010 tracing::trace!(
1011 "right from {:?}, focus {:?}",
1012 i.info().id(),
1013 new_info.as_ref().map(|w| w.info().id())
1014 );
1015 }
1016 None => tracing::debug!("cannot focus right, no current focus"),
1017 },
1018 FocusTarget::Down => match &origin_info {
1019 Some(i) => {
1020 new_info = i.next_down();
1021 tracing::trace!(
1022 "down from {:?}, focus {:?}",
1023 i.info().id(),
1024 new_info.as_ref().map(|w| w.info().id())
1025 );
1026 }
1027 None => tracing::debug!("cannot focus down, no current focus"),
1028 },
1029 FocusTarget::Left => match &origin_info {
1030 Some(i) => {
1031 new_info = i.next_left();
1032 tracing::trace!(
1033 "left from {:?}, focus {:?}",
1034 i.info().id(),
1035 new_info.as_ref().map(|w| w.info().id())
1036 );
1037 }
1038 None => tracing::debug!("cannot focus left, no current focus"),
1039 },
1040 FocusTarget::Alt => match &origin_info {
1041 Some(i) => {
1042 if let Some(alt) = i.self_and_ancestors().find(|w| w.is_alt_scope()) {
1043 if let Some(r) = self.return_focused.get(&alt.info().id())
1045 && let Some(r) = r.with(|p| p.as_ref().map(|p| p.widget_id()))
1046 && let Some(r) = find_wgt(r)
1047 && r.is_focusable()
1048 {
1049 tracing::trace!("toggle alt from alt scope, exit to return {:?}", r.info().id());
1050 new_info = Some(r);
1051 } else {
1052 tracing::trace!("is in alt scope without return focus, exiting to window root focusable");
1053 new_info = i.focus_tree().focusable_root();
1054 tracing::trace!(
1055 "toggle alt from alt scope, to window root {:?}",
1056 new_info.as_ref().map(|w| w.info().id())
1057 );
1058 }
1059 } else {
1060 new_info = i.alt_scope();
1061 tracing::trace!("alt into alt scope {:?}", new_info.as_ref().map(|w| w.info().id()));
1062 }
1063 }
1064 None => tracing::debug!("cannot focus alt, no current focus"),
1065 },
1066 }
1067
1068 let new_info = match new_info {
1069 Some(i) => i, None => match current_info.clone() {
1071 Some(i) => i,
1073 None => return,
1075 },
1076 };
1077
1078 let current_highlight = self.is_highlighting.get();
1079 let new_highlight = request.highlight;
1080
1081 let mut new_enabled_nav = new_info.enabled_nav();
1082
1083 let prev_focus = self.focused.get();
1084 let mut new_focus = Some(new_info.info().interaction_path());
1085
1086 if prev_focus == new_focus && current_highlight == new_highlight && self.enabled_nav == new_enabled_nav {
1087 tracing::trace!("no focus change");
1089 return;
1090 }
1091
1092 if let Some(prev_info) = ¤t_info {
1093 let mut update_return = |scope_path: InteractionPath| -> bool {
1095 match self.return_focused.entry(scope_path.widget_id()) {
1096 IdEntry::Occupied(e) => {
1097 let e = e.get();
1098 if e.with(|p| *p != prev_focus) {
1099 e.set(prev_focus.clone());
1100 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope_path, e.get(), prev_focus.clone()));
1101 return true;
1102 }
1103 }
1104 IdEntry::Vacant(e) => {
1105 e.insert(var(prev_focus.clone()));
1106 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope_path, None, prev_focus.clone()));
1107 return true;
1108 }
1109 }
1110 false
1111 };
1112
1113 let prev_scope = prev_info.self_and_ancestors().find(|w| w.is_scope());
1114 let new_scope = new_info.self_and_ancestors().find(|w| w.is_scope());
1115
1116 if prev_scope != new_scope {
1117 debug_assert_eq!(prev_focus.as_ref().unwrap().widget_id(), prev_info.info().id());
1118
1119 if let Some(new_scope) = new_scope
1120 && new_scope.is_alt_scope()
1121 && !matches!(request.target, FocusTarget::Exit { .. })
1122 && prev_scope.as_ref().map(|p| !p.is_alt_scope()).unwrap_or(true)
1123 && prev_focus.as_ref().map(|f| !f.contains(new_scope.info().id())).unwrap_or(true)
1124 {
1125 let set = update_return(new_scope.info().interaction_path());
1127 if set {
1128 tracing::trace!(
1129 "set alt scope {:?} return focus to {:?}",
1130 new_scope.info().id(),
1131 prev_focus.as_ref().map(|f| f.widget_id())
1132 );
1133 }
1134 }
1135
1136 if let Some(scope) = prev_scope
1137 && !scope.is_alt_scope()
1138 && matches!(
1139 scope.focus_info().scope_on_focus(),
1140 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds
1141 )
1142 {
1143 let set = update_return(scope.info().interaction_path());
1145 if set {
1146 tracing::trace!(
1147 "set scope {:?} return focus to {:?}",
1148 scope.info().id(),
1149 prev_focus.as_ref().map(|f| f.widget_id())
1150 );
1151 }
1152 }
1153 }
1154 }
1155
1156 let cause = if is_service_request {
1157 FocusChangedCause::Request(request)
1158 } else {
1159 FocusChangedCause::Recovery
1160 };
1161
1162 if current_highlight != new_highlight {
1163 self.is_highlighting.set(new_highlight);
1164 }
1165
1166 let mut focused_changed = prev_focus != new_focus;
1167
1168 let prev_focus_window = prev_focus.as_ref().map(|p| p.window_id());
1169 let new_focus_window = new_focus.as_ref().map(|p| p.window_id());
1170
1171 let args = FocusChangedArgs::now(prev_focus, new_focus.clone(), new_highlight, cause, new_enabled_nav);
1172
1173 if new_info.is_scope() {
1174 let last_focused = |id| {
1176 self.return_focused
1177 .get(&id)
1178 .and_then(|p| p.with(|p| p.as_ref().map(|p| p.widget_id())))
1179 };
1180
1181 let is_tab_cycle_reentry = matches!(args.cause.request_target(), Some(FocusTarget::Prev | FocusTarget::Next))
1183 && match (&args.prev_focus, &args.new_focus) {
1184 (Some(p), Some(n)) => p.contains(n.widget_id()),
1185 _ => false,
1186 };
1187
1188 let reverse = matches!(args.cause.request_target(), Some(FocusTarget::Prev));
1190
1191 let mut pending_args = Some(args.clone());
1192 let mut scope_info = new_info;
1193 while let Some(w) = scope_info.on_focus_scope_move(last_focused, is_tab_cycle_reentry, reverse) {
1194 let prev_focus = args.new_focus.clone();
1197 new_focus = Some(w.info().interaction_path());
1198
1199 focused_changed = args.prev_focus != new_focus;
1200 if prev_focus == new_focus {
1201 break;
1202 }
1203
1204 new_enabled_nav = w.enabled_nav();
1205
1206 tracing::trace!("on focus scope move to {:?}", new_focus.as_ref().map(|w| w.widget_id()));
1207
1208 if let Some(args) = pending_args.take() {
1209 FOCUS_CHANGED_EVENT.notify(args);
1210 }
1211 pending_args = Some(FocusChangedArgs::now(
1212 prev_focus,
1213 new_focus.clone(),
1214 new_highlight,
1215 FocusChangedCause::ScopeGotFocus(reverse),
1216 new_enabled_nav,
1217 ));
1218
1219 if w.is_scope() {
1220 scope_info = w;
1222 } else {
1223 break;
1224 }
1225 }
1226 if let Some(args) = pending_args.take() {
1227 FOCUS_CHANGED_EVENT.notify(args);
1228 }
1229 } else {
1230 FOCUS_CHANGED_EVENT.notify(args);
1231 }
1232
1233 if focused_changed {
1234 self.focused.set(new_focus);
1235
1236 if prev_focus_window != new_focus_window
1237 && let Some(w) = new_focus_window
1238 && let Some(mode) = WINDOWS.mode(w)
1239 && mode.is_headed()
1240 && let Some(vars) = WINDOWS.vars(w)
1241 && matches!(vars.instance_state().get(), WindowInstanceState::Loaded { has_view: true })
1242 {
1243 tracing::trace!("focus changed to another window, from {prev_focus_window:?} to {new_focus_window:?}");
1244
1245 if prev_focus_window.is_some() {
1246 WINDOWS_FOCUS.focus(w);
1247 } else if request.force_window_focus {
1248 tracing::trace!("attempting to steal focus from other app");
1249 vars.focus_indicator().set(Some(FocusIndicator::Critical));
1251 WINDOWS_FOCUS.focus(w);
1252 } else if let Some(i) = request.window_indicator {
1253 tracing::trace!("set focus indicator {i:?}");
1254 vars.focus_indicator().set(i);
1255 } else {
1256 tracing::debug!("app does not have focus and force or indicator request, set info indicator");
1257 vars.focus_indicator().set(FocusIndicator::Info);
1258 }
1259 }
1260 }
1261
1262 if self.enabled_nav != new_enabled_nav {
1263 self.enabled_nav = new_enabled_nav;
1264 tracing::trace!("update cmds {:?}", new_enabled_nav);
1265 self.commands.update_enabled(new_enabled_nav);
1266 }
1267
1268 self.navigation_origin.set(new_origin);
1269 }
1270
1271 fn fulfill_highlight_request(&mut self) {
1272 if self.request.is_some() || self.fallback_request.is_some() {
1273 debug_assert!(self.request_highlight);
1275 return;
1276 }
1277
1278 if !self.is_highlighting.get() {
1279 self.focused.with(|f| {
1280 if let Some(p) = f {
1281 tracing::trace!("highlight request to {:?}", p.widget_id());
1283 self.request = Some(FocusRequest::direct(p.widget_id(), true));
1284 }
1285 });
1286 if self.request.is_some() {
1287 self.fulfill_request(None, true);
1288 }
1289 }
1290 }
1291
1292 fn focus_direct_recovery(&mut self, wgt_id: WidgetId, tree_hint: Option<&WidgetInfoTree>) {
1293 let pending_request = self.request.take();
1294 let pending_fallback_request = self.fallback_request.take();
1295 self.request = Some(FocusRequest::direct_or_related(wgt_id, false, self.is_highlighting.get()));
1296 self.fulfill_request(tree_hint, false);
1297 self.request = pending_request;
1298 self.fallback_request = pending_fallback_request;
1299 }
1300}
1301
1302fn hooks() {
1303 ACCESS_FOCUS_EVENT
1304 .hook(|args| {
1305 let is_focused = FOCUS_SV.read().focused.with(|p| p.as_ref().map(|p| p.widget_id())) == Some(args.target.widget_id());
1306 if args.focus {
1307 if !is_focused {
1308 tracing::trace!("access focus request {}", args.target.widget_id());
1309 FOCUS.focus_widget(args.target.widget_id(), false);
1310 } else {
1311 tracing::debug!("access focus request {} ignored, already focused", args.target.widget_id());
1312 }
1313 } else if is_focused {
1314 tracing::trace!("access focus exit request {}", args.target.widget_id());
1315 FOCUS.focus_exit(false);
1316 } else {
1317 tracing::debug!("access focus exit request {} ignored, not focused", args.target.widget_id());
1318 }
1319 true
1320 })
1321 .perm();
1322
1323 ACCESS_FOCUS_NAV_ORIGIN_EVENT
1324 .hook(|args| {
1325 let is_window_focused = FOCUS_SV.read().focused.with(|p| p.as_ref().map(|p| p.window_id())) == Some(args.target.window_id());
1326 if is_window_focused {
1327 tracing::trace!("access focus nav origin request {}", args.target.widget_id());
1328 FOCUS.navigation_origin().set(Some(args.target.widget_id()));
1329 } else {
1330 tracing::debug!(
1331 "access focus nav origin request {} ignored, not in focused window",
1332 args.target.widget_id()
1333 );
1334 }
1335 true
1336 })
1337 .perm();
1338
1339 MOUSE_INPUT_EVENT
1340 .hook(|args| {
1341 if args.is_mouse_down() {
1342 tracing::trace!("mouse press focus request {}", args.target.widget_id());
1343 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1344 }
1345 true
1346 })
1347 .perm();
1348
1349 TOUCH_INPUT_EVENT
1350 .hook(|args| {
1351 if args.is_touch_start() {
1352 tracing::trace!("touch start focus request {}", args.target.widget_id());
1353 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1354 }
1355 true
1356 })
1357 .perm();
1358
1359 ACCESS_CLICK_EVENT
1360 .hook(|args| {
1361 tracing::trace!("access click focus request {}", args.target.widget_id());
1362 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1363 true
1364 })
1365 .perm();
1366
1367 WIDGET_TREE_CHANGED_EVENT
1368 .hook(|args| {
1369 let mut s = FOCUS_SV.write();
1370 if let Some((win_id, wgt_id)) = s.focused.with(|f| f.as_ref().map(|f| (f.window_id(), f.widget_id())))
1371 && args.tree.window_id() == win_id
1372 {
1373 tracing::trace!("tree changed recovery");
1381 s.focus_direct_recovery(wgt_id, Some(&args.tree));
1382
1383 s.return_focused.retain(|scope_id, ret| {
1385 if let Some((win_id, wgt_id)) = ret.with(|f| f.as_ref().map(|f| (f.window_id(), f.widget_id())))
1386 && win_id == args.tree.window_id()
1387 {
1388 if win_id != args.tree.window_id() {
1389 return true;
1391 }
1392
1393 if let Some(scope) = args.tree.get(*scope_id) {
1394 if let Some(wgt) = args.tree.get(wgt_id) {
1397 let wgt_path = wgt.interaction_path();
1400 if ret.with(|p| p.as_ref() != Some(&wgt_path)) {
1401 tracing::trace!("return_focus of {scope_id} ({wgt_id}) changed");
1403
1404 let was_inside_scope = ret.with(|p| p.as_ref().unwrap().contains(*scope_id));
1405 let is_inside_scope = scope.is_ancestor(&wgt);
1406
1407 if was_inside_scope == is_inside_scope {
1408 ret.set(Some(wgt_path.clone()));
1409 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1410 scope.interaction_path(),
1411 ret.get(),
1412 Some(wgt_path),
1413 ));
1414
1415 return true;
1417 } else {
1418 tracing::trace!("return_focus of {scope_id} ({wgt_id}) cannot be return anymore");
1419
1420 ret.set(None);
1421 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1422 scope.interaction_path(),
1423 ret.get(),
1424 None,
1425 ));
1426 }
1427 } else {
1428 return true;
1430 }
1431 } else {
1432 tracing::trace!("return_focus of {scope_id} ({wgt_id}) no longer in focus tree");
1433 ret.set(None);
1434 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope.interaction_path(), ret.get(), None));
1435 }
1436 }
1437 }
1438
1439 ret.strong_count() > 1
1441 });
1442 }
1443 if !args.is_update {
1444 focus_info::FocusTreeData::consolidate_alt_scopes(&args.prev_tree, &args.tree);
1445 }
1446 true
1447 })
1448 .perm();
1449
1450 WINDOW_FOCUS_CHANGED_EVENT
1451 .hook(|args| {
1452 let mut s = FOCUS_SV.write();
1453 let current_focus = s.focused.with(|p| p.as_ref().map(|p| p.window_id()));
1454 if current_focus != args.new_focus {
1455 if let Some(id) = args.new_focus
1456 && let Some(tree) = WINDOWS.widget_tree(id)
1457 {
1458 tracing::trace!("window focus changed to {id:?}");
1459 let tree = FocusInfoTree::new(tree, s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get());
1460 if let Some(root) = tree.focusable_root() {
1461 let pending_request = s.request.take();
1462 let pending_fallback_request = s.fallback_request.take();
1463 tracing::trace!("window focus changed focus request {:?}", root.info().id());
1464 s.request = Some(FocusRequest::direct_or_related(root.info().id(), false, s.is_highlighting.get()));
1465 s.fulfill_request(Some(tree.tree()), false);
1466 s.request = pending_request;
1467 s.fallback_request = pending_fallback_request;
1468 return true;
1469 } else {
1470 tracing::debug!("focused window does not have any focusable widget");
1471 }
1472 } else {
1473 tracing::debug!("all windows lost focus");
1474 }
1475
1476 if let Some(win_id) = current_focus {
1477 let wgt_id = s.focused.with(|p| p.as_ref().map(|p| p.widget_id())).unwrap();
1478
1479 s.focused.set(None);
1481 s.is_highlighting.set(false);
1482 if !s.enabled_nav.is_empty() {
1483 s.enabled_nav = FocusNavAction::empty();
1484 s.commands.update_enabled(FocusNavAction::empty());
1485 }
1486
1487 FOCUS_CHANGED_EVENT.notify(FocusChangedArgs::now(
1488 s.focused.get(),
1489 None,
1490 false,
1491 FocusChangedCause::Recovery,
1492 s.enabled_nav,
1493 ));
1494
1495 let prev_tree = WINDOWS.widget_tree(win_id);
1496 if let Some(prev_tree) = &prev_tree
1497 && let Some(wgt) = prev_tree.get(wgt_id)
1498 && let Some(wgt) = wgt.into_focusable(s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get())
1499 && let Some(root_scope) = wgt.self_and_ancestors().filter(|w| w.is_scope()).last()
1500 && !root_scope.is_alt_scope()
1501 && matches!(
1502 root_scope.focus_info().scope_on_focus(),
1503 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds
1504 )
1505 {
1506 let mut return_change = None;
1509 if let Some(alt_scope) = wgt.self_and_ancestors().find(|w| w.is_alt_scope()) {
1510 if let Some(ret) = s.return_focused.get(&alt_scope.info().id())
1512 && let Some(path) = ret.get()
1513 {
1514 return_change = Some(path);
1515 }
1516 } else {
1517 return_change = s.focused.get();
1519 }
1520
1521 if return_change.is_some() {
1522 let mut prev = None;
1523 match s.return_focused.entry(root_scope.info().id()) {
1524 IdEntry::Occupied(e) => {
1525 prev = e.get().get();
1526 e.get().set(return_change.clone());
1527 }
1528 IdEntry::Vacant(e) => {
1529 e.insert(var(return_change.clone()));
1530 }
1531 }
1532 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1533 Some(root_scope.info().interaction_path()),
1534 prev,
1535 return_change,
1536 ));
1537 }
1538 } else if prev_tree.is_none() {
1539 s.return_focused.retain(|scope_id, v| {
1541 if let Some(p_win_id) = v.with(|p| p.as_ref().map(|p| p.window_id()))
1542 && p_win_id == win_id
1543 {
1544 #[cfg(debug_assertions)]
1548 if WINDOWS.widget_info(*scope_id).is_some() {
1549 tracing::error!("expected focus scope {scope_id} to not exist after window close");
1550 }
1551 #[cfg(not(debug_assertions))]
1552 let _ = scope_id;
1553
1554 return false;
1555 }
1556 true
1557 });
1558 }
1559 }
1560 }
1561 true
1562 })
1563 .perm();
1564}