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 direct_or_direct_or_exit = matches!(&request.target, FocusTarget::Direct { .. } | FocusTarget::DirectOrExit { .. });
799
800 let mut new_info = None;
802 let mut new_origin = None;
803 match request.target {
804 FocusTarget::Direct { target } => match find_wgt(target) {
805 Some(w) => {
806 if w.is_focusable() {
807 tracing::trace!("focus {:?}", w.info().id());
808 new_info = Some(w);
809 } else {
810 tracing::debug!("cannot focus {target}, not focusable")
811 }
812 }
813 None => tracing::debug!("cannot focus {target}, not found"),
814 },
815 FocusTarget::DirectOrExit { target, navigation_origin } => match find_wgt(target) {
816 Some(w) => {
817 if w.is_focusable() {
818 tracing::trace!("focus {:?}", w.info().id());
819 new_info = Some(w);
820 } else {
821 tracing::debug!("cannot focus {target}, not focusable, will try ancestors");
822 match w.ancestors().next() {
823 Some(actual) => {
824 tracing::trace!("focusing ancestor {:?}", actual.info().id());
825 new_info = Some(actual);
826 }
827 None => {
828 tracing::debug!("cannot focus {target} or ancestor, none focusable in path");
829 }
830 }
831 if navigation_origin {
832 new_origin = Some(w.info().id());
833 }
834 }
835 }
836 None => tracing::debug!("cannot focus {target} or ancestor, not found"),
837 },
838 FocusTarget::DirectOrEnter { target, navigation_origin } => match find_wgt(target) {
839 Some(w) => {
840 if w.is_focusable() {
841 tracing::trace!("focus {:?}", w.info().id());
842 new_info = Some(w);
843 } else {
844 tracing::debug!("cannot focus {target}, not focusable, will try descendants");
845 match w.first_tab_descendant() {
846 Some(actual) => {
847 tracing::trace!("focusing descendant {:?}", actual.info().id());
848 new_info = Some(actual);
849 }
850 None => {
851 if navigation_origin {
852 new_origin = Some(w.info().id());
853 }
854 tracing::debug!("cannot focus {target} or descendants, none tab focusable in subtree");
855 }
856 }
857 }
858 }
859 None => tracing::debug!("cannot focus {target} or descendants, not found"),
860 },
861 FocusTarget::DirectOrRelated { target, navigation_origin } => match find_wgt(target) {
862 Some(w) => {
863 if w.is_focusable() {
864 tracing::trace!("focus {:?}", w.info().id());
865 new_info = Some(w);
866 } else {
867 tracing::debug!("cannot focus {target}, not focusable, will try descendants and ancestors");
868 match w
869 .first_tab_descendant()
870 .map(|w| (w, false))
871 .or_else(|| w.descendants().next().map(|w| (w, false)))
872 .or_else(|| w.ancestors().next().map(|w| (w, true)))
873 {
874 Some((actual, is_ancestor)) => {
875 if navigation_origin && is_ancestor {
876 new_origin = Some(w.info().id());
877 }
878 tracing::trace!(
879 "focusing {} {:?}",
880 if is_ancestor { "ancestor" } else { "descendant" },
881 actual.info().id()
882 );
883 new_info = Some(actual);
884 }
885 None => {
886 if navigation_origin {
887 new_origin = Some(w.info().id());
888 }
889 tracing::debug!("cannot focus {target} or descendants or ancestors, none focusable")
890 }
891 }
892 }
893 }
894 None => {
895 tracing::debug!("cannot focus {target} or descendants or ancestors, not found");
896 if !is_service_request {
897 let focused = self.focused.get();
898 if let Some(focused) = focused
899 && focused.widget_id() == target
900 {
901 UPDATES.once_next_update("delayed-focus-recovery", move || {
904 let mut s = FOCUS_SV.write();
905 let focus_did_not_change = s.focused.with(|p| p.as_ref() == Some(&focused));
906 if focus_did_not_change && s.request.is_none() {
907 for wgt in focused.widgets_path().iter().rev() {
908 if let Some(wgt) = WINDOWS.widget_info(*wgt) {
909 s.focus_direct_recovery(wgt.id(), Some(wgt.tree()));
910 break;
911 }
912 }
913 }
914 })
915 }
916 }
917 }
918 },
919 FocusTarget::Enter => match &origin_info {
920 Some(i) => {
921 new_info = i.first_tab_descendant();
922 tracing::trace!("enter {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
923 }
924 None => tracing::debug!("cannot enter focused, no current focus"),
925 },
926 FocusTarget::Exit { recursive_alt } => match &origin_info {
927 Some(i) => {
928 let mut alt = i.self_and_ancestors().find(|s| s.is_alt_scope());
929 let mut recursive_alt_scopes = vec![];
930 while let Some(a) = alt.take() {
931 if recursive_alt_scopes.contains(&a.info().id()) {
932 tracing::error!("circular alt return focus, {recursive_alt_scopes:?}");
933 break;
934 }
935
936 if let Some(r) = self.return_focused.get(&a.info().id())
937 && let Some(r) = r.with(|p| p.as_ref().map(|p| p.widget_id()))
938 && let Some(r) = find_wgt(r)
939 && r.is_focusable()
940 {
941 if recursive_alt {
943 recursive_alt_scopes.push(a.info().id());
944 alt = r.self_and_ancestors().find(|s| s.is_alt_scope());
945 if alt.is_some() {
946 continue;
947 }
948 tracing::trace!(
949 "exit {:?}, alt {:?}, return {:?}",
950 i.info().id(),
951 recursive_alt_scopes,
952 r.info().id()
953 );
954 } else {
955 tracing::trace!("exit {:?}, alt {:?}, return {:?}", i.info().id(), a.info().id(), r.info().id());
956 }
957 new_info = Some(r);
958 } else {
959 if recursive_alt {
961 new_info = a.ancestors().find(|w| !w.in_alt_scope());
962 } else {
963 new_info = a.ancestors().next();
964 }
965 tracing::debug!(
966 "exit {:?}, alt {:?}, no return, focus {:?}",
967 i.info().id(),
968 a.info().id(),
969 new_info.as_ref().map(|w| w.info().id())
970 );
971 }
972 }
973 if new_info.is_none() {
974 new_info = i.ancestors().next();
975 tracing::trace!("exit {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
976 }
977 }
978 None => tracing::debug!("cannot exit focused, no current focus"),
979 },
980 FocusTarget::Next => match &origin_info {
981 Some(i) => {
982 new_info = i.next_tab(false);
983 tracing::trace!(
984 "next from {:?}, focus {:?}",
985 i.info().id(),
986 new_info.as_ref().map(|w| w.info().id())
987 );
988 }
989 None => tracing::debug!("cannot focus next, no current focus"),
990 },
991 FocusTarget::Prev => match &origin_info {
992 Some(i) => {
993 new_info = i.prev_tab(false);
994 tracing::trace!(
995 "prev from {:?}, focus {:?}",
996 i.info().id(),
997 new_info.as_ref().map(|w| w.info().id())
998 );
999 }
1000 None => tracing::debug!("cannot focus prev, no current focus"),
1001 },
1002 FocusTarget::Up => match &origin_info {
1003 Some(i) => {
1004 new_info = i.next_up();
1005 tracing::trace!("up from {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
1006 }
1007 None => tracing::debug!("cannot focus up, no current focus"),
1008 },
1009 FocusTarget::Right => match &origin_info {
1010 Some(i) => {
1011 new_info = i.next_right();
1012 tracing::trace!(
1013 "right from {:?}, focus {:?}",
1014 i.info().id(),
1015 new_info.as_ref().map(|w| w.info().id())
1016 );
1017 }
1018 None => tracing::debug!("cannot focus right, no current focus"),
1019 },
1020 FocusTarget::Down => match &origin_info {
1021 Some(i) => {
1022 new_info = i.next_down();
1023 tracing::trace!(
1024 "down from {:?}, focus {:?}",
1025 i.info().id(),
1026 new_info.as_ref().map(|w| w.info().id())
1027 );
1028 }
1029 None => tracing::debug!("cannot focus down, no current focus"),
1030 },
1031 FocusTarget::Left => match &origin_info {
1032 Some(i) => {
1033 new_info = i.next_left();
1034 tracing::trace!(
1035 "left from {:?}, focus {:?}",
1036 i.info().id(),
1037 new_info.as_ref().map(|w| w.info().id())
1038 );
1039 }
1040 None => tracing::debug!("cannot focus left, no current focus"),
1041 },
1042 FocusTarget::Alt => match &origin_info {
1043 Some(i) => {
1044 if let Some(alt) = i.self_and_ancestors().find(|w| w.is_alt_scope()) {
1045 if let Some(r) = self.return_focused.get(&alt.info().id())
1047 && let Some(r) = r.with(|p| p.as_ref().map(|p| p.widget_id()))
1048 && let Some(r) = find_wgt(r)
1049 && r.is_focusable()
1050 {
1051 tracing::trace!("toggle alt from alt scope, exit to return {:?}", r.info().id());
1052 new_info = Some(r);
1053 } else {
1054 tracing::trace!("is in alt scope without return focus, exiting to window root focusable");
1055 new_info = i.focus_tree().focusable_root();
1056 tracing::trace!(
1057 "toggle alt from alt scope, to window root {:?}",
1058 new_info.as_ref().map(|w| w.info().id())
1059 );
1060 }
1061 } else {
1062 new_info = i.alt_scope();
1063 tracing::trace!("alt into alt scope {:?}", new_info.as_ref().map(|w| w.info().id()));
1064 }
1065 }
1066 None => tracing::debug!("cannot focus alt, no current focus"),
1067 },
1068 }
1069
1070 if direct_or_direct_or_exit
1071 && let Some(prev_info) = ¤t_info
1072 && let Some(new_info) = &mut new_info
1073 && new_info.info().is_ancestor(prev_info.info())
1074 && let FocusInfo::FocusScope { on_focus, alt, .. } = new_info.focus_info()
1075 && !matches!(on_focus, FocusScopeOnFocus::Widget)
1076 && !alt
1077 && prev_info.is_focusable()
1078 {
1079 *new_info = prev_info.clone();
1082 }
1083
1084 let new_info = match new_info {
1085 Some(i) => i, None => match current_info.clone() {
1087 Some(i) => i,
1089 None => return,
1091 },
1092 };
1093
1094 let current_highlight = self.is_highlighting.get();
1095 let new_highlight = request.highlight;
1096
1097 let mut new_enabled_nav = new_info.enabled_nav();
1098
1099 let prev_focus = self.focused.get();
1100 let mut new_focus = Some(new_info.info().interaction_path());
1101
1102 if prev_focus == new_focus && current_highlight == new_highlight && self.enabled_nav == new_enabled_nav {
1103 tracing::trace!("no focus change");
1105 return;
1106 }
1107
1108 if let Some(prev_info) = ¤t_info {
1109 let prev_scope = prev_info.self_and_ancestors().find(|w| w.is_scope());
1110 let new_scope = new_info.self_and_ancestors().find(|w| w.is_scope());
1111
1112 if prev_scope != new_scope {
1113 debug_assert_eq!(prev_focus.as_ref().unwrap().widget_id(), prev_info.info().id());
1114
1115 let mut update_return = |scope_path: InteractionPath| -> bool {
1117 match self.return_focused.entry(scope_path.widget_id()) {
1118 IdEntry::Occupied(e) => {
1119 let e = e.get();
1120 if e.with(|p| *p != prev_focus) {
1121 e.set(prev_focus.clone());
1122 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope_path, e.get(), prev_focus.clone()));
1123 return true;
1124 }
1125 }
1126 IdEntry::Vacant(e) => {
1127 e.insert(var(prev_focus.clone()));
1128 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope_path, None, prev_focus.clone()));
1129 return true;
1130 }
1131 }
1132 false
1133 };
1134
1135 if let Some(new_scope) = new_scope
1136 && new_scope.is_alt_scope()
1137 && !matches!(request.target, FocusTarget::Exit { .. })
1138 && prev_scope.as_ref().map(|p| !p.is_alt_scope()).unwrap_or(true)
1139 && prev_focus.as_ref().map(|f| !f.contains(new_scope.info().id())).unwrap_or(true)
1140 {
1141 let set = update_return(new_scope.info().interaction_path());
1143 if set {
1144 tracing::trace!(
1145 "set alt scope {:?} return focus to {:?}",
1146 new_scope.info().id(),
1147 prev_focus.as_ref().map(|f| f.widget_id())
1148 );
1149 }
1150 }
1151
1152 if let Some(scope) = prev_scope
1153 && !scope.is_alt_scope()
1154 && matches!(
1155 scope.focus_info().scope_on_focus(),
1156 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds
1157 )
1158 {
1159 let set = update_return(scope.info().interaction_path());
1161 if set {
1162 tracing::trace!(
1163 "set scope {:?} return focus to {:?}",
1164 scope.info().id(),
1165 prev_focus.as_ref().map(|f| f.widget_id())
1166 );
1167 }
1168 }
1169 }
1170 }
1171
1172 let cause = if is_service_request {
1173 FocusChangedCause::Request(request)
1174 } else {
1175 FocusChangedCause::Recovery
1176 };
1177
1178 if current_highlight != new_highlight {
1179 self.is_highlighting.set(new_highlight);
1180 }
1181
1182 let mut focused_changed = prev_focus != new_focus;
1183
1184 let prev_focus_window = prev_focus.as_ref().map(|p| p.window_id());
1185 let new_focus_window = new_focus.as_ref().map(|p| p.window_id());
1186
1187 let args = FocusChangedArgs::now(prev_focus, new_focus.clone(), new_highlight, cause, new_enabled_nav);
1188
1189 if new_info.is_scope() {
1190 let last_focused = |id| {
1192 self.return_focused
1193 .get(&id)
1194 .and_then(|p| p.with(|p| p.as_ref().map(|p| p.widget_id())))
1195 };
1196
1197 let is_tab_cycle_reentry = matches!(args.cause.request_target(), Some(FocusTarget::Prev | FocusTarget::Next))
1199 && match (&args.prev_focus, &args.new_focus) {
1200 (Some(p), Some(n)) => p.contains(n.widget_id()),
1201 _ => false,
1202 };
1203
1204 let reverse = matches!(args.cause.request_target(), Some(FocusTarget::Prev));
1206
1207 let mut pending_args = Some(args.clone());
1208 let mut scope_info = new_info;
1209 while let Some(w) = scope_info.on_focus_scope_move(last_focused, is_tab_cycle_reentry, reverse) {
1210 let prev_focus = args.new_focus.clone();
1213 new_focus = Some(w.info().interaction_path());
1214
1215 focused_changed = args.prev_focus != new_focus;
1216 if prev_focus == new_focus {
1217 break;
1218 }
1219
1220 new_enabled_nav = w.enabled_nav();
1221
1222 tracing::trace!("on focus scope move to {:?}", new_focus.as_ref().map(|w| w.widget_id()));
1223
1224 if let Some(args) = pending_args.take() {
1225 FOCUS_CHANGED_EVENT.notify(args);
1226 }
1227 pending_args = Some(FocusChangedArgs::now(
1228 prev_focus,
1229 new_focus.clone(),
1230 new_highlight,
1231 FocusChangedCause::ScopeGotFocus(reverse),
1232 new_enabled_nav,
1233 ));
1234
1235 if w.is_scope() {
1236 scope_info = w;
1238 } else {
1239 break;
1240 }
1241 }
1242 if let Some(args) = pending_args.take() {
1243 FOCUS_CHANGED_EVENT.notify(args);
1244 }
1245 } else {
1246 FOCUS_CHANGED_EVENT.notify(args);
1247 }
1248
1249 if focused_changed {
1250 self.focused.set(new_focus);
1251
1252 if prev_focus_window != new_focus_window
1253 && let Some(w) = new_focus_window
1254 && let Some(mode) = WINDOWS.mode(w)
1255 && mode.is_headed()
1256 && let Some(vars) = WINDOWS.vars(w)
1257 && matches!(vars.instance_state().get(), WindowInstanceState::Loaded { has_view: true })
1258 {
1259 tracing::trace!("focus changed to another window, from {prev_focus_window:?} to {new_focus_window:?}");
1260
1261 if prev_focus_window.is_some() {
1262 WINDOWS_FOCUS.focus(w);
1263 } else if request.force_window_focus {
1264 tracing::trace!("attempting to steal focus from other app");
1265 vars.focus_indicator().set(Some(FocusIndicator::Critical));
1267 WINDOWS_FOCUS.focus(w);
1268 } else if let Some(i) = request.window_indicator {
1269 tracing::trace!("set focus indicator {i:?}");
1270 vars.focus_indicator().set(i);
1271 } else {
1272 tracing::debug!("app does not have focus and force or indicator request, set info indicator");
1273 vars.focus_indicator().set(FocusIndicator::Info);
1274 }
1275 }
1276 }
1277
1278 if self.enabled_nav != new_enabled_nav {
1279 self.enabled_nav = new_enabled_nav;
1280 tracing::trace!("update cmds {:?}", new_enabled_nav);
1281 self.commands.update_enabled(new_enabled_nav);
1282 }
1283
1284 self.navigation_origin.set(new_origin);
1285 }
1286
1287 fn fulfill_highlight_request(&mut self) {
1288 if self.request.is_some() || self.fallback_request.is_some() {
1289 debug_assert!(self.request_highlight);
1291 return;
1292 }
1293
1294 if !self.is_highlighting.get() {
1295 self.focused.with(|f| {
1296 if let Some(p) = f {
1297 tracing::trace!("highlight request to {:?}", p.widget_id());
1299 self.request = Some(FocusRequest::direct(p.widget_id(), true));
1300 }
1301 });
1302 if self.request.is_some() {
1303 self.fulfill_request(None, true);
1304 }
1305 }
1306 }
1307
1308 fn focus_direct_recovery(&mut self, wgt_id: WidgetId, tree_hint: Option<&WidgetInfoTree>) {
1309 let pending_request = self.request.take();
1310 let pending_fallback_request = self.fallback_request.take();
1311 self.request = Some(FocusRequest::direct_or_related(wgt_id, false, self.is_highlighting.get()));
1312 self.fulfill_request(tree_hint, false);
1313 self.request = pending_request;
1314 self.fallback_request = pending_fallback_request;
1315 }
1316}
1317
1318fn hooks() {
1319 ACCESS_FOCUS_EVENT
1320 .hook(|args| {
1321 let is_focused = FOCUS_SV.read().focused.with(|p| p.as_ref().map(|p| p.widget_id())) == Some(args.target.widget_id());
1322 if args.focus {
1323 if !is_focused {
1324 tracing::trace!("access focus request {}", args.target.widget_id());
1325 FOCUS.focus_widget(args.target.widget_id(), false);
1326 } else {
1327 tracing::debug!("access focus request {} ignored, already focused", args.target.widget_id());
1328 }
1329 } else if is_focused {
1330 tracing::trace!("access focus exit request {}", args.target.widget_id());
1331 FOCUS.focus_exit(false);
1332 } else {
1333 tracing::debug!("access focus exit request {} ignored, not focused", args.target.widget_id());
1334 }
1335 true
1336 })
1337 .perm();
1338
1339 ACCESS_FOCUS_NAV_ORIGIN_EVENT
1340 .hook(|args| {
1341 let is_window_focused = FOCUS_SV.read().focused.with(|p| p.as_ref().map(|p| p.window_id())) == Some(args.target.window_id());
1342 if is_window_focused {
1343 tracing::trace!("access focus nav origin request {}", args.target.widget_id());
1344 FOCUS.navigation_origin().set(Some(args.target.widget_id()));
1345 } else {
1346 tracing::debug!(
1347 "access focus nav origin request {} ignored, not in focused window",
1348 args.target.widget_id()
1349 );
1350 }
1351 true
1352 })
1353 .perm();
1354
1355 MOUSE_INPUT_EVENT
1356 .hook(|args| {
1357 if args.is_mouse_down() {
1358 tracing::trace!("mouse press focus request {}", args.target.widget_id());
1360 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1361 }
1362 true
1363 })
1364 .perm();
1365
1366 TOUCH_INPUT_EVENT
1367 .hook(|args| {
1368 if args.is_touch_start() {
1369 tracing::trace!("touch start focus request {}", args.target.widget_id());
1370 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1371 }
1372 true
1373 })
1374 .perm();
1375
1376 ACCESS_CLICK_EVENT
1377 .hook(|args| {
1378 tracing::trace!("access click focus request {}", args.target.widget_id());
1379 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1380 true
1381 })
1382 .perm();
1383
1384 WIDGET_TREE_CHANGED_EVENT
1385 .hook(|args| {
1386 let mut s = FOCUS_SV.write();
1387 if let Some((win_id, wgt_id)) = s.focused.with(|f| f.as_ref().map(|f| (f.window_id(), f.widget_id())))
1388 && args.tree.window_id() == win_id
1389 {
1390 tracing::trace!("tree changed recovery");
1398 s.focus_direct_recovery(wgt_id, Some(&args.tree));
1399
1400 s.return_focused.retain(|scope_id, ret| {
1402 if let Some((win_id, wgt_id)) = ret.with(|f| f.as_ref().map(|f| (f.window_id(), f.widget_id())))
1403 && win_id == args.tree.window_id()
1404 {
1405 if win_id != args.tree.window_id() {
1406 return true;
1408 }
1409
1410 if let Some(scope) = args.tree.get(*scope_id) {
1411 if let Some(wgt) = args.tree.get(wgt_id) {
1414 let wgt_path = wgt.interaction_path();
1417 if ret.with(|p| p.as_ref() != Some(&wgt_path)) {
1418 tracing::trace!("return_focus of {scope_id} ({wgt_id}) changed");
1420
1421 let was_inside_scope = ret.with(|p| p.as_ref().unwrap().contains(*scope_id));
1422 let is_inside_scope = scope.is_ancestor(&wgt);
1423
1424 if was_inside_scope == is_inside_scope {
1425 ret.set(Some(wgt_path.clone()));
1426 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1427 scope.interaction_path(),
1428 ret.get(),
1429 Some(wgt_path),
1430 ));
1431
1432 return true;
1434 } else {
1435 tracing::trace!("return_focus of {scope_id} ({wgt_id}) cannot be return anymore");
1436
1437 ret.set(None);
1438 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1439 scope.interaction_path(),
1440 ret.get(),
1441 None,
1442 ));
1443 }
1444 } else {
1445 return true;
1447 }
1448 } else {
1449 tracing::trace!("return_focus of {scope_id} ({wgt_id}) no longer in focus tree");
1450 ret.set(None);
1451 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope.interaction_path(), ret.get(), None));
1452 }
1453 }
1454 }
1455
1456 ret.strong_count() > 1
1458 });
1459 }
1460 if !args.is_update {
1461 focus_info::FocusTreeData::consolidate_alt_scopes(&args.prev_tree, &args.tree);
1462 }
1463 true
1464 })
1465 .perm();
1466
1467 WINDOW_FOCUS_CHANGED_EVENT
1468 .hook(|args| {
1469 let mut s = FOCUS_SV.write();
1470 let current_focus = s.focused.with(|p| p.as_ref().map(|p| p.window_id()));
1471 if current_focus != args.new_focus {
1472 if let Some(id) = args.new_focus
1473 && let Some(tree) = WINDOWS.widget_tree(id)
1474 {
1475 tracing::trace!("window focus changed to {id:?}");
1476 let tree = FocusInfoTree::new(tree, s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get());
1477 if let Some(root) = tree.focusable_root() {
1478 let pending_request = s.request.take();
1479 let pending_fallback_request = s.fallback_request.take();
1480 tracing::trace!("window focus changed focus request {:?}", root.info().id());
1481 s.request = Some(FocusRequest::direct_or_related(root.info().id(), false, s.is_highlighting.get()));
1482 s.fulfill_request(Some(tree.tree()), false);
1483 s.request = pending_request;
1484 s.fallback_request = pending_fallback_request;
1485 return true;
1486 } else {
1487 tracing::debug!("focused window does not have any focusable widget");
1488 }
1489 } else {
1490 tracing::debug!("all windows lost focus");
1491 }
1492
1493 if let Some(win_id) = current_focus {
1494 let wgt_id = s.focused.with(|p| p.as_ref().map(|p| p.widget_id())).unwrap();
1495
1496 s.focused.set(None);
1498 s.is_highlighting.set(false);
1499 if !s.enabled_nav.is_empty() {
1500 s.enabled_nav = FocusNavAction::empty();
1501 s.commands.update_enabled(FocusNavAction::empty());
1502 }
1503
1504 FOCUS_CHANGED_EVENT.notify(FocusChangedArgs::now(
1505 s.focused.get(),
1506 None,
1507 false,
1508 FocusChangedCause::Recovery,
1509 s.enabled_nav,
1510 ));
1511
1512 let prev_tree = WINDOWS.widget_tree(win_id);
1513 if let Some(prev_tree) = &prev_tree
1514 && let Some(wgt) = prev_tree.get(wgt_id)
1515 && let Some(wgt) = wgt.into_focusable(s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get())
1516 && let Some(root_scope) = wgt.self_and_ancestors().filter(|w| w.is_scope()).last()
1517 && !root_scope.is_alt_scope()
1518 && matches!(
1519 root_scope.focus_info().scope_on_focus(),
1520 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds
1521 )
1522 {
1523 let mut return_change = None;
1526 if let Some(alt_scope) = wgt.self_and_ancestors().find(|w| w.is_alt_scope()) {
1527 if let Some(ret) = s.return_focused.get(&alt_scope.info().id())
1529 && let Some(path) = ret.get()
1530 {
1531 return_change = Some(path);
1532 }
1533 } else {
1534 return_change = s.focused.get();
1536 }
1537
1538 if return_change.is_some() {
1539 let mut prev = None;
1540 match s.return_focused.entry(root_scope.info().id()) {
1541 IdEntry::Occupied(e) => {
1542 prev = e.get().get();
1543 e.get().set(return_change.clone());
1544 }
1545 IdEntry::Vacant(e) => {
1546 e.insert(var(return_change.clone()));
1547 }
1548 }
1549 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1550 Some(root_scope.info().interaction_path()),
1551 prev,
1552 return_change,
1553 ));
1554 }
1555 } else if prev_tree.is_none() {
1556 s.return_focused.retain(|scope_id, v| {
1558 if let Some(p_win_id) = v.with(|p| p.as_ref().map(|p| p.window_id()))
1559 && p_win_id == win_id
1560 {
1561 #[cfg(debug_assertions)]
1565 if WINDOWS.widget_info(*scope_id).is_some() {
1566 tracing::error!("expected focus scope {scope_id} to not exist after window close");
1567 }
1568 #[cfg(not(debug_assertions))]
1569 let _ = scope_id;
1570
1571 return false;
1572 }
1573 true
1574 });
1575 }
1576 }
1577 }
1578 true
1579 })
1580 .perm();
1581}