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!(|args| {
672 if args.yield_until_app() {
673 return;
674 }
675
676 APP.on_init(hn!(|args| {
677 WINDOWS_FOCUS.hook_focus_service(FOCUS.focused());
678 }));
679});
680
681app_local! {
682 static FOCUS_SV: FocusService = FocusService::new();
683}
684struct FocusService {
685 auto_highlight: Var<Option<Duration>>,
686 focus_disabled_widgets: Var<bool>,
687 focus_hidden_widgets: Var<bool>,
688
689 navigation_origin: Var<Option<WidgetId>>,
690 focused: Var<Option<InteractionPath>>,
691 return_focused: IdMap<WidgetId, Var<Option<InteractionPath>>>,
692 is_highlighting: Var<bool>,
693 alt_return: WeakVar<Option<InteractionPath>>,
694
695 commands: cmd::FocusCommands,
696
697 request: Option<FocusRequest>,
698 fallback_request: Option<FocusRequest>,
699 request_highlight: bool,
700
701 enabled_nav: FocusNavAction,
702}
703fn last_keyboard_event() -> DInstant {
704 RAW_KEY_INPUT_EVENT
705 .with(|v| v.latest().map(|a| a.timestamp))
706 .unwrap_or(DInstant::EPOCH)
707}
708impl FocusService {
709 fn new() -> Self {
710 hooks();
711 let s = Self {
712 auto_highlight: var(Some(300.ms())),
713 focus_disabled_widgets: var(true),
714 focus_hidden_widgets: var(true),
715
716 navigation_origin: var(None),
717 focused: var(None),
718 return_focused: IdMap::default(),
719 is_highlighting: var(false),
720 alt_return: WeakVar::new(),
721
722 commands: cmd::FocusCommands::new(),
723
724 request: None,
725 fallback_request: None,
726 request_highlight: false,
727
728 enabled_nav: FocusNavAction::empty(),
729 };
730 fn refresh(_: &zng_var::VarHookArgs<bool>) -> bool {
731 let mut s = FOCUS_SV.write();
732 if let Some(id) = s.focused.with(|p| p.as_ref().map(|p| p.widget_id())) {
733 tracing::trace!("focus_disabled_widgets or focus_hidden_widgets changed recovery");
734 s.focus_direct_recovery(id, None);
735 }
736 true
737 }
738 s.focus_disabled_widgets.hook(refresh).perm();
739 s.focus_hidden_widgets.hook(refresh).perm();
740 s
741 }
742
743 fn fulfill_request(&mut self, tree_hint: Option<&WidgetInfoTree>, is_service_request: bool) {
744 let mut request = self.request.take().or(self.fallback_request.take()).unwrap();
746
747 if mem::take(&mut self.request_highlight) {
748 request.highlight = true;
750 } else if !request.highlight
751 && let Some(dur) = self.auto_highlight.get()
752 && last_keyboard_event().elapsed() <= dur
753 {
754 tracing::trace!("last keyboard event within {dur:?}, highlight");
756 request.highlight = true;
757 }
758
759 let focus_disabled = self.focus_disabled_widgets.get();
760 let focus_hidden = self.focus_hidden_widgets.get();
761
762 let current_info = self
764 .focused
765 .with(|p| match p {
766 Some(p) => {
767 if let Some(t) = &tree_hint
768 && t.window_id() == p.window_id()
769 {
770 t.get(p.widget_id())
771 } else {
772 WINDOWS.widget_tree(p.window_id()).and_then(|t| t.get(p.widget_id()))
773 }
774 }
775 None => None,
776 })
777 .map(|i| i.into_focus_info(focus_disabled, focus_hidden));
778
779 let find_wgt = |id| {
781 if let Some(c) = ¤t_info
782 && let Some(r) = c.info().tree().get(id)
783 {
784 return Some(r.into_focus_info(focus_disabled, focus_hidden));
785 }
786 if let Some(t) = &tree_hint
787 && let Some(r) = t.get(id)
788 {
789 return Some(r.into_focus_info(focus_disabled, focus_hidden));
790 }
791 WINDOWS.widget_info(id).map(|r| r.into_focus_info(focus_disabled, focus_hidden))
792 };
793
794 let origin_info = self
796 .navigation_origin
797 .get()
798 .and_then(|id| current_info.as_ref().and_then(|i| i.info().tree().get(id)))
799 .map(|i| i.into_focus_info(focus_disabled, focus_hidden))
800 .or_else(|| current_info.clone());
801
802 let mut new_info = None;
804 let mut new_origin = None;
805 match request.target {
806 FocusTarget::Direct { target } => match find_wgt(target) {
807 Some(w) => {
808 if w.is_focusable() {
809 tracing::trace!("focus {:?}", w.info().id());
810 new_info = Some(w);
811 } else {
812 tracing::debug!("cannot focus {target}, not focusable")
813 }
814 }
815 None => tracing::debug!("cannot focus {target}, not found"),
816 },
817 FocusTarget::DirectOrExit { target, navigation_origin } => match find_wgt(target) {
818 Some(w) => {
819 if w.is_focusable() {
820 tracing::trace!("focus {:?}", w.info().id());
821 new_info = Some(w);
822 } else {
823 tracing::debug!("cannot focus {target}, not focusable, will try ancestors");
824 match w.ancestors().next() {
825 Some(actual) => {
826 tracing::trace!("focusing ancestor {:?}", actual.info().id());
827 new_info = Some(actual);
828 }
829 None => {
830 tracing::debug!("cannot focus {target} or ancestor, none focusable in path");
831 }
832 }
833 if navigation_origin {
834 new_origin = Some(w.info().id());
835 }
836 }
837 }
838 None => tracing::debug!("cannot focus {target} or ancestor, not found"),
839 },
840 FocusTarget::DirectOrEnter { target, navigation_origin } => match find_wgt(target) {
841 Some(w) => {
842 if w.is_focusable() {
843 tracing::trace!("focus {:?}", w.info().id());
844 new_info = Some(w);
845 } else {
846 tracing::debug!("cannot focus {target}, not focusable, will try descendants");
847 match w.first_tab_descendant() {
848 Some(actual) => {
849 tracing::trace!("focusing descendant {:?}", actual.info().id());
850 new_info = Some(actual);
851 }
852 None => {
853 if navigation_origin {
854 new_origin = Some(w.info().id());
855 }
856 tracing::debug!("cannot focus {target} or descendants, none tab focusable in subtree");
857 }
858 }
859 }
860 }
861 None => tracing::debug!("cannot focus {target} or descendants, not found"),
862 },
863 FocusTarget::DirectOrRelated { target, navigation_origin } => match find_wgt(target) {
864 Some(w) => {
865 if w.is_focusable() {
866 tracing::trace!("focus {:?}", w.info().id());
867 new_info = Some(w);
868 } else {
869 tracing::debug!("cannot focus {target}, not focusable, will try descendants and ancestors");
870 match w
871 .first_tab_descendant()
872 .map(|w| (w, false))
873 .or_else(|| w.descendants().next().map(|w| (w, false)))
874 .or_else(|| w.ancestors().next().map(|w| (w, true)))
875 {
876 Some((actual, is_ancestor)) => {
877 if navigation_origin && is_ancestor {
878 new_origin = Some(w.info().id());
879 }
880 tracing::trace!(
881 "focusing {} {:?}",
882 if is_ancestor { "ancestor" } else { "descendant" },
883 actual.info().id()
884 );
885 new_info = Some(actual);
886 }
887 None => {
888 if navigation_origin {
889 new_origin = Some(w.info().id());
890 }
891 tracing::debug!("cannot focus {target} or descendants or ancestors, none focusable")
892 }
893 }
894 }
895 }
896 None => {
897 tracing::debug!("cannot focus {target} or descendants or ancestors, not found");
898 if !is_service_request {
899 let focused = self.focused.get();
900 if let Some(focused) = focused
901 && focused.widget_id() == target
902 {
903 UPDATES.once_next_update("delayed-focus-recovery", move || {
906 let mut s = FOCUS_SV.write();
907 let focus_did_not_change = s.focused.with(|p| p.as_ref() == Some(&focused));
908 if focus_did_not_change && s.request.is_none() {
909 for wgt in focused.widgets_path().iter().rev() {
910 if let Some(wgt) = WINDOWS.widget_info(*wgt) {
911 s.focus_direct_recovery(wgt.id(), Some(wgt.tree()));
912 break;
913 }
914 }
915 }
916 })
917 }
918 }
919 }
920 },
921 FocusTarget::Enter => match &origin_info {
922 Some(i) => {
923 new_info = i.first_tab_descendant();
924 tracing::trace!("enter {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
925 }
926 None => tracing::debug!("cannot enter focused, no current focus"),
927 },
928 FocusTarget::Exit { recursive_alt } => match &origin_info {
929 Some(i) => {
930 let mut alt = i.self_and_ancestors().find(|s| s.is_alt_scope());
931 let mut recursive_alt_scopes = vec![];
932 while let Some(a) = alt.take() {
933 if recursive_alt_scopes.contains(&a.info().id()) {
934 tracing::error!("circular alt return focus, {recursive_alt_scopes:?}");
935 break;
936 }
937
938 if let Some(r) = self.return_focused.get(&a.info().id())
939 && let Some(r) = r.with(|p| p.as_ref().map(|p| p.widget_id()))
940 && let Some(r) = find_wgt(r)
941 && r.is_focusable()
942 {
943 if recursive_alt {
945 recursive_alt_scopes.push(a.info().id());
946 alt = r.self_and_ancestors().find(|s| s.is_alt_scope());
947 if alt.is_some() {
948 continue;
949 }
950 tracing::trace!(
951 "exit {:?}, alt {:?}, return {:?}",
952 i.info().id(),
953 recursive_alt_scopes,
954 r.info().id()
955 );
956 } else {
957 tracing::trace!("exit {:?}, alt {:?}, return {:?}", i.info().id(), a.info().id(), r.info().id());
958 }
959 new_info = Some(r);
960 } else {
961 if recursive_alt {
963 new_info = a.ancestors().find(|w| !w.in_alt_scope());
964 } else {
965 new_info = a.ancestors().next();
966 }
967 tracing::debug!(
968 "exit {:?}, alt {:?}, no return, focus {:?}",
969 i.info().id(),
970 a.info().id(),
971 new_info.as_ref().map(|w| w.info().id())
972 );
973 }
974 }
975 if new_info.is_none() {
976 new_info = i.ancestors().next();
977 tracing::trace!("exit {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
978 }
979 }
980 None => tracing::debug!("cannot exit focused, no current focus"),
981 },
982 FocusTarget::Next => match &origin_info {
983 Some(i) => {
984 new_info = i.next_tab(false);
985 tracing::trace!(
986 "next from {:?}, focus {:?}",
987 i.info().id(),
988 new_info.as_ref().map(|w| w.info().id())
989 );
990 }
991 None => tracing::debug!("cannot focus next, no current focus"),
992 },
993 FocusTarget::Prev => match &origin_info {
994 Some(i) => {
995 new_info = i.prev_tab(false);
996 tracing::trace!(
997 "prev from {:?}, focus {:?}",
998 i.info().id(),
999 new_info.as_ref().map(|w| w.info().id())
1000 );
1001 }
1002 None => tracing::debug!("cannot focus prev, no current focus"),
1003 },
1004 FocusTarget::Up => match &origin_info {
1005 Some(i) => {
1006 new_info = i.next_up();
1007 tracing::trace!("up from {:?}, focus {:?}", i.info().id(), new_info.as_ref().map(|w| w.info().id()));
1008 }
1009 None => tracing::debug!("cannot focus up, no current focus"),
1010 },
1011 FocusTarget::Right => match &origin_info {
1012 Some(i) => {
1013 new_info = i.next_right();
1014 tracing::trace!(
1015 "right from {:?}, focus {:?}",
1016 i.info().id(),
1017 new_info.as_ref().map(|w| w.info().id())
1018 );
1019 }
1020 None => tracing::debug!("cannot focus right, no current focus"),
1021 },
1022 FocusTarget::Down => match &origin_info {
1023 Some(i) => {
1024 new_info = i.next_down();
1025 tracing::trace!(
1026 "down from {:?}, focus {:?}",
1027 i.info().id(),
1028 new_info.as_ref().map(|w| w.info().id())
1029 );
1030 }
1031 None => tracing::debug!("cannot focus down, no current focus"),
1032 },
1033 FocusTarget::Left => match &origin_info {
1034 Some(i) => {
1035 new_info = i.next_left();
1036 tracing::trace!(
1037 "left from {:?}, focus {:?}",
1038 i.info().id(),
1039 new_info.as_ref().map(|w| w.info().id())
1040 );
1041 }
1042 None => tracing::debug!("cannot focus left, no current focus"),
1043 },
1044 FocusTarget::Alt => match &origin_info {
1045 Some(i) => {
1046 if let Some(alt) = i.self_and_ancestors().find(|w| w.is_alt_scope()) {
1047 if let Some(r) = self.return_focused.get(&alt.info().id())
1049 && let Some(r) = r.with(|p| p.as_ref().map(|p| p.widget_id()))
1050 && let Some(r) = find_wgt(r)
1051 && r.is_focusable()
1052 {
1053 tracing::trace!("toggle alt from alt scope, exit to return {:?}", r.info().id());
1054 new_info = Some(r);
1055 } else {
1056 tracing::trace!("is in alt scope without return focus, exiting to window root focusable");
1057 new_info = i.focus_tree().focusable_root();
1058 tracing::trace!(
1059 "toggle alt from alt scope, to window root {:?}",
1060 new_info.as_ref().map(|w| w.info().id())
1061 );
1062 }
1063 } else {
1064 new_info = i.alt_scope();
1065 tracing::trace!("alt into alt scope {:?}", new_info.as_ref().map(|w| w.info().id()));
1066 }
1067 }
1068 None => tracing::debug!("cannot focus alt, no current focus"),
1069 },
1070 }
1071
1072 let new_info = match new_info {
1073 Some(i) => i, None => match current_info.clone() {
1075 Some(i) => i,
1077 None => return,
1079 },
1080 };
1081
1082 let current_highlight = self.is_highlighting.get();
1083 let new_highlight = request.highlight;
1084
1085 let mut new_enabled_nav = new_info.enabled_nav();
1086
1087 let prev_focus = self.focused.get();
1088 let mut new_focus = Some(new_info.info().interaction_path());
1089
1090 if prev_focus == new_focus && current_highlight == new_highlight && self.enabled_nav == new_enabled_nav {
1091 tracing::trace!("no focus change");
1093 return;
1094 }
1095
1096 if let Some(prev_info) = ¤t_info {
1097 let mut update_return = |scope_path: InteractionPath| -> bool {
1099 match self.return_focused.entry(scope_path.widget_id()) {
1100 IdEntry::Occupied(e) => {
1101 let e = e.get();
1102 if e.with(|p| *p != prev_focus) {
1103 e.set(prev_focus.clone());
1104 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope_path, e.get(), prev_focus.clone()));
1105 return true;
1106 }
1107 }
1108 IdEntry::Vacant(e) => {
1109 e.insert(var(prev_focus.clone()));
1110 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope_path, None, prev_focus.clone()));
1111 return true;
1112 }
1113 }
1114 false
1115 };
1116
1117 let prev_scope = prev_info.self_and_ancestors().find(|w| w.is_scope());
1118 let new_scope = new_info.self_and_ancestors().find(|w| w.is_scope());
1119
1120 if prev_scope != new_scope {
1121 debug_assert_eq!(prev_focus.as_ref().unwrap().widget_id(), prev_info.info().id());
1122
1123 if let Some(scope) = new_scope
1124 && scope.is_alt_scope()
1125 && !matches!(request.target, FocusTarget::Exit { .. })
1126 {
1127 let set = update_return(scope.info().interaction_path());
1129 if set {
1130 tracing::trace!(
1131 "set alt scope {:?} return focus to {:?}",
1132 scope.info().id(),
1133 prev_focus.as_ref().map(|f| f.widget_id())
1134 );
1135 }
1136 }
1137
1138 if let Some(scope) = prev_scope
1139 && !scope.is_alt_scope()
1140 && matches!(
1141 scope.focus_info().scope_on_focus(),
1142 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds
1143 )
1144 {
1145 let set = update_return(scope.info().interaction_path());
1147 if set {
1148 tracing::trace!(
1149 "set scope {:?} return focus to {:?}",
1150 scope.info().id(),
1151 prev_focus.as_ref().map(|f| f.widget_id())
1152 );
1153 }
1154 }
1155 }
1156 }
1157
1158 let cause = if is_service_request {
1159 FocusChangedCause::Request(request)
1160 } else {
1161 FocusChangedCause::Recovery
1162 };
1163
1164 if current_highlight != new_highlight {
1165 self.is_highlighting.set(new_highlight);
1166 }
1167
1168 let mut focused_changed = prev_focus != new_focus;
1169
1170 let prev_focus_window = prev_focus.as_ref().map(|p| p.window_id());
1171 let new_focus_window = new_focus.as_ref().map(|p| p.window_id());
1172
1173 let args = FocusChangedArgs::now(prev_focus, new_focus.clone(), new_highlight, cause, new_enabled_nav);
1174
1175 if new_info.is_scope() {
1176 let last_focused = |id| {
1178 self.return_focused
1179 .get(&id)
1180 .and_then(|p| p.with(|p| p.as_ref().map(|p| p.widget_id())))
1181 };
1182
1183 let is_tab_cycle_reentry = matches!(args.cause.request_target(), Some(FocusTarget::Prev | FocusTarget::Next))
1185 && match (&args.prev_focus, &args.new_focus) {
1186 (Some(p), Some(n)) => p.contains(n.widget_id()),
1187 _ => false,
1188 };
1189
1190 let reverse = matches!(args.cause.request_target(), Some(FocusTarget::Prev));
1192
1193 let mut pending_args = Some(args.clone());
1194 let mut scope_info = new_info;
1195 while let Some(w) = scope_info.on_focus_scope_move(last_focused, is_tab_cycle_reentry, reverse) {
1196 let prev_focus = args.new_focus.clone();
1199 new_focus = Some(w.info().interaction_path());
1200
1201 focused_changed = args.prev_focus != new_focus;
1202 if prev_focus == new_focus {
1203 break;
1204 }
1205
1206 new_enabled_nav = w.enabled_nav();
1207
1208 tracing::trace!("on focus scope move to {:?}", new_focus.as_ref().map(|w| w.widget_id()));
1209
1210 if let Some(args) = pending_args.take() {
1211 FOCUS_CHANGED_EVENT.notify(args);
1212 }
1213 pending_args = Some(FocusChangedArgs::now(
1214 prev_focus,
1215 new_focus.clone(),
1216 new_highlight,
1217 FocusChangedCause::ScopeGotFocus(reverse),
1218 new_enabled_nav,
1219 ));
1220
1221 if w.is_scope() {
1222 scope_info = w;
1224 } else {
1225 break;
1226 }
1227 }
1228 if let Some(args) = pending_args.take() {
1229 FOCUS_CHANGED_EVENT.notify(args);
1230 }
1231 } else {
1232 FOCUS_CHANGED_EVENT.notify(args);
1233 }
1234
1235 if focused_changed {
1236 self.focused.set(new_focus);
1237
1238 if prev_focus_window != new_focus_window
1239 && let Some(w) = new_focus_window
1240 && let Some(mode) = WINDOWS.mode(w)
1241 && mode.is_headed()
1242 && let Some(vars) = WINDOWS.vars(w)
1243 && matches!(vars.instance_state().get(), WindowInstanceState::Loaded { has_view: true })
1244 {
1245 tracing::trace!("focus changed to another window, from {prev_focus_window:?} to {new_focus_window:?}");
1246
1247 if prev_focus_window.is_some() {
1248 WINDOWS_FOCUS.focus(w);
1249 } else if request.force_window_focus {
1250 tracing::trace!("attempting to steal focus from other app");
1251 vars.focus_indicator().set(Some(FocusIndicator::Critical));
1253 WINDOWS_FOCUS.focus(w);
1254 } else if let Some(i) = request.window_indicator {
1255 tracing::trace!("set focus indicator {i:?}");
1256 vars.focus_indicator().set(i);
1257 } else {
1258 tracing::debug!("app does not have focus and force or indicator request, set info indicator");
1259 vars.focus_indicator().set(FocusIndicator::Info);
1260 }
1261 }
1262 }
1263
1264 if self.enabled_nav != new_enabled_nav {
1265 self.enabled_nav = new_enabled_nav;
1266 tracing::trace!("update cmds {:?}", new_enabled_nav);
1267 self.commands.update_enabled(new_enabled_nav);
1268 }
1269
1270 self.navigation_origin.set(new_origin);
1271 }
1272
1273 fn fulfill_highlight_request(&mut self) {
1274 if self.request.is_some() || self.fallback_request.is_some() {
1275 debug_assert!(self.request_highlight);
1277 return;
1278 }
1279
1280 if !self.is_highlighting.get() {
1281 self.focused.with(|f| {
1282 if let Some(p) = f {
1283 tracing::trace!("highlight request to {:?}", p.widget_id());
1285 self.request = Some(FocusRequest::direct(p.widget_id(), true));
1286 }
1287 });
1288 if self.request.is_some() {
1289 self.fulfill_request(None, true);
1290 }
1291 }
1292 }
1293
1294 fn focus_direct_recovery(&mut self, wgt_id: WidgetId, tree_hint: Option<&WidgetInfoTree>) {
1295 let pending_request = self.request.take();
1296 let pending_fallback_request = self.fallback_request.take();
1297 self.request = Some(FocusRequest::direct_or_related(wgt_id, false, self.is_highlighting.get()));
1298 self.fulfill_request(tree_hint, false);
1299 self.request = pending_request;
1300 self.fallback_request = pending_fallback_request;
1301 }
1302}
1303
1304fn hooks() {
1305 ACCESS_FOCUS_EVENT
1306 .hook(|args| {
1307 let is_focused = FOCUS_SV.read().focused.with(|p| p.as_ref().map(|p| p.widget_id())) == Some(args.target.widget_id());
1308 if args.focus {
1309 if !is_focused {
1310 tracing::trace!("access focus request {}", args.target.widget_id());
1311 FOCUS.focus_widget(args.target.widget_id(), false);
1312 } else {
1313 tracing::debug!("access focus request {} ignored, already focused", args.target.widget_id());
1314 }
1315 } else if is_focused {
1316 tracing::trace!("access focus exit request {}", args.target.widget_id());
1317 FOCUS.focus_exit(false);
1318 } else {
1319 tracing::debug!("access focus exit request {} ignored, not focused", args.target.widget_id());
1320 }
1321 true
1322 })
1323 .perm();
1324
1325 ACCESS_FOCUS_NAV_ORIGIN_EVENT
1326 .hook(|args| {
1327 let is_window_focused = FOCUS_SV.read().focused.with(|p| p.as_ref().map(|p| p.window_id())) == Some(args.target.window_id());
1328 if is_window_focused {
1329 tracing::trace!("access focus nav origin request {}", args.target.widget_id());
1330 FOCUS.navigation_origin().set(Some(args.target.widget_id()));
1331 } else {
1332 tracing::debug!(
1333 "access focus nav origin request {} ignored, not in focused window",
1334 args.target.widget_id()
1335 );
1336 }
1337 true
1338 })
1339 .perm();
1340
1341 MOUSE_INPUT_EVENT
1342 .hook(|args| {
1343 if args.is_mouse_down() {
1344 tracing::trace!("mouse press focus request {}", args.target.widget_id());
1345 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1346 }
1347 true
1348 })
1349 .perm();
1350
1351 TOUCH_INPUT_EVENT
1352 .hook(|args| {
1353 if args.is_touch_start() {
1354 tracing::trace!("touch start focus request {}", args.target.widget_id());
1355 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1356 }
1357 true
1358 })
1359 .perm();
1360
1361 ACCESS_CLICK_EVENT
1362 .hook(|args| {
1363 tracing::trace!("access click focus request {}", args.target.widget_id());
1364 FOCUS.focus(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
1365 true
1366 })
1367 .perm();
1368
1369 WIDGET_TREE_CHANGED_EVENT
1370 .hook(|args| {
1371 let mut s = FOCUS_SV.write();
1372 if let Some((win_id, wgt_id)) = s.focused.with(|f| f.as_ref().map(|f| (f.window_id(), f.widget_id())))
1373 && args.tree.window_id() == win_id
1374 {
1375 tracing::trace!("tree changed recovery");
1383 s.focus_direct_recovery(wgt_id, Some(&args.tree));
1384
1385 s.return_focused.retain(|scope_id, ret| {
1387 if let Some((win_id, wgt_id)) = ret.with(|f| f.as_ref().map(|f| (f.window_id(), f.widget_id())))
1388 && win_id == args.tree.window_id()
1389 {
1390 if win_id != args.tree.window_id() {
1391 return true;
1393 }
1394
1395 if let Some(scope) = args.tree.get(*scope_id) {
1396 if let Some(wgt) = args.tree.get(wgt_id) {
1399 let wgt_path = wgt.interaction_path();
1402 if ret.with(|p| p.as_ref() != Some(&wgt_path)) {
1403 tracing::trace!("return_focus of {scope_id} ({wgt_id}) changed");
1405
1406 let was_inside_scope = ret.with(|p| p.as_ref().unwrap().contains(*scope_id));
1407 let is_inside_scope = scope.is_ancestor(&wgt);
1408
1409 if was_inside_scope == is_inside_scope {
1410 ret.set(Some(wgt_path.clone()));
1411 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1412 scope.interaction_path(),
1413 ret.get(),
1414 Some(wgt_path),
1415 ));
1416
1417 return true;
1419 } else {
1420 tracing::trace!("return_focus of {scope_id} ({wgt_id}) cannot be return anymore");
1421
1422 ret.set(None);
1423 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1424 scope.interaction_path(),
1425 ret.get(),
1426 None,
1427 ));
1428 }
1429 } else {
1430 return true;
1432 }
1433 } else {
1434 tracing::trace!("return_focus of {scope_id} ({wgt_id}) no longer in focus tree");
1435 ret.set(None);
1436 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(scope.interaction_path(), ret.get(), None));
1437 }
1438 }
1439 }
1440
1441 ret.strong_count() > 1
1443 });
1444 }
1445 if !args.is_update {
1446 focus_info::FocusTreeData::consolidate_alt_scopes(&args.prev_tree, &args.tree);
1447 }
1448 true
1449 })
1450 .perm();
1451
1452 WINDOW_FOCUS_CHANGED_EVENT
1453 .hook(|args| {
1454 let mut s = FOCUS_SV.write();
1455 let current_focus = s.focused.with(|p| p.as_ref().map(|p| p.window_id()));
1456 if current_focus != args.new_focus {
1457 if let Some(id) = args.new_focus
1458 && let Some(tree) = WINDOWS.widget_tree(id)
1459 {
1460 tracing::trace!("window focus changed to {id:?}");
1461 let tree = FocusInfoTree::new(tree, s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get());
1462 if let Some(root) = tree.focusable_root() {
1463 let pending_request = s.request.take();
1464 let pending_fallback_request = s.fallback_request.take();
1465 tracing::trace!("window focus changed focus request {:?}", root.info().id());
1466 s.request = Some(FocusRequest::direct_or_related(root.info().id(), false, s.is_highlighting.get()));
1467 s.fulfill_request(Some(tree.tree()), false);
1468 s.request = pending_request;
1469 s.fallback_request = pending_fallback_request;
1470 return true;
1471 } else {
1472 tracing::debug!("focused window does not have any focusable widget");
1473 }
1474 } else {
1475 tracing::debug!("all windows lost focus");
1476 }
1477
1478 if let Some(win_id) = current_focus {
1479 let wgt_id = s.focused.with(|p| p.as_ref().map(|p| p.widget_id())).unwrap();
1480
1481 s.focused.set(None);
1483 s.is_highlighting.set(false);
1484 if !s.enabled_nav.is_empty() {
1485 s.enabled_nav = FocusNavAction::empty();
1486 s.commands.update_enabled(FocusNavAction::empty());
1487 }
1488
1489 FOCUS_CHANGED_EVENT.notify(FocusChangedArgs::now(
1490 s.focused.get(),
1491 None,
1492 false,
1493 FocusChangedCause::Recovery,
1494 s.enabled_nav,
1495 ));
1496
1497 let prev_tree = WINDOWS.widget_tree(win_id);
1498 if let Some(prev_tree) = &prev_tree
1499 && let Some(wgt) = prev_tree.get(wgt_id)
1500 && let Some(wgt) = wgt.into_focusable(s.focus_disabled_widgets.get(), s.focus_hidden_widgets.get())
1501 && let Some(root_scope) = wgt.self_and_ancestors().filter(|w| w.is_scope()).last()
1502 && !root_scope.is_alt_scope()
1503 && matches!(
1504 root_scope.focus_info().scope_on_focus(),
1505 FocusScopeOnFocus::LastFocused | FocusScopeOnFocus::LastFocusedIgnoreBounds
1506 )
1507 {
1508 let mut return_change = None;
1511 if let Some(alt_scope) = wgt.self_and_ancestors().find(|w| w.is_alt_scope()) {
1512 if let Some(ret) = s.return_focused.get(&alt_scope.info().id())
1514 && let Some(path) = ret.get()
1515 {
1516 return_change = Some(path);
1517 }
1518 } else {
1519 return_change = s.focused.get();
1521 }
1522
1523 if return_change.is_some() {
1524 let mut prev = None;
1525 match s.return_focused.entry(root_scope.info().id()) {
1526 IdEntry::Occupied(e) => {
1527 prev = e.get().get();
1528 e.get().set(return_change.clone());
1529 }
1530 IdEntry::Vacant(e) => {
1531 e.insert(var(return_change.clone()));
1532 }
1533 }
1534 RETURN_FOCUS_CHANGED_EVENT.notify(ReturnFocusChangedArgs::now(
1535 Some(root_scope.info().interaction_path()),
1536 prev,
1537 return_change,
1538 ));
1539 }
1540 } else if prev_tree.is_none() {
1541 s.return_focused.retain(|scope_id, v| {
1543 if let Some(p_win_id) = v.with(|p| p.as_ref().map(|p| p.window_id()))
1544 && p_win_id == win_id
1545 {
1546 #[cfg(debug_assertions)]
1550 if WINDOWS.widget_info(*scope_id).is_some() {
1551 tracing::error!("expected focus scope {scope_id} to not exist after window close");
1552 }
1553 #[cfg(not(debug_assertions))]
1554 let _ = scope_id;
1555
1556 return false;
1557 }
1558 true
1559 });
1560 }
1561 }
1562 }
1563 true
1564 })
1565 .perm();
1566}