1pub mod iter;
4
5mod focus_info;
6pub use focus_info::*;
7
8use zng_app::{
9 APP, AppExtension, DInstant, INSTANT,
10 access::{ACCESS_CLICK_EVENT, ACCESS_FOCUS_EVENT, ACCESS_FOCUS_NAV_ORIGIN_EVENT},
11 event::{event, event_args},
12 update::{EventUpdate, InfoUpdates, RenderUpdates, UPDATES},
13 view_process::raw_events::RAW_KEY_INPUT_EVENT,
14 widget::{
15 WidgetId,
16 info::{InteractionPath, WIDGET_INFO_CHANGED_EVENT, WidgetBoundsInfo, WidgetInfoTree},
17 },
18 window::WindowId,
19};
20
21pub mod cmd;
22use cmd::FocusCommands;
23use zng_app_context::app_local;
24use zng_ext_window::{WINDOW_FOCUS, WINDOW_FOCUS_CHANGED_EVENT, WINDOWS};
25use zng_layout::unit::{Px, PxPoint, PxRect, TimeUnits};
26use zng_unique_id::{IdEntry, IdMap};
27use zng_var::{Var, var};
28use zng_view_api::window::FrameId;
29
30use std::{mem, time::Duration};
31
32use crate::{mouse::MOUSE_INPUT_EVENT, touch::TOUCH_INPUT_EVENT};
33
34event_args! {
35 pub struct FocusChangedArgs {
37 pub prev_focus: Option<InteractionPath>,
39
40 pub new_focus: Option<InteractionPath>,
42
43 pub highlight: bool,
49
50 pub cause: FocusChangedCause,
52
53 pub enabled_nav: FocusNavAction,
57
58 ..
59
60 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
62 if let Some(prev) = &self.prev_focus {
63 list.insert_wgt(prev);
64 }
65 if let Some(new) = &self.new_focus {
66 list.insert_wgt(new);
67 }
68 }
69 }
70
71 pub struct ReturnFocusChangedArgs {
73 pub scope: Option<InteractionPath>,
77
78 pub prev_return: Option<InteractionPath>,
80
81 pub new_return: Option<InteractionPath>,
83
84 ..
85
86 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
89 if let Some(scope) = &self.scope {
90 list.insert_wgt(scope)
91 }
92 if let Some(prev_return) = &self.prev_return {
93 list.insert_wgt(prev_return)
94 }
95 if let Some(new_return) = &self.new_return {
96 list.insert_wgt(new_return)
97 }
98 }
99 }
100}
101
102impl FocusChangedArgs {
103 pub fn is_widget_move(&self) -> bool {
105 match (&self.prev_focus, &self.new_focus) {
106 (Some(prev), Some(new)) => prev.widget_id() == new.widget_id() && prev.as_path() != new.as_path(),
107 _ => false,
108 }
109 }
110
111 pub fn is_enabled_change(&self) -> bool {
113 match (&self.prev_focus, &self.new_focus) {
114 (Some(prev), Some(new)) => prev.as_path() == new.as_path() && prev.disabled_index() != new.disabled_index(),
115 _ => false,
116 }
117 }
118
119 pub fn is_highlight_changed(&self) -> bool {
121 self.prev_focus == self.new_focus
122 }
123
124 pub fn is_focus(&self, widget_id: WidgetId) -> bool {
126 match (&self.prev_focus, &self.new_focus) {
127 (Some(prev), Some(new)) => prev.widget_id() != widget_id && new.widget_id() == widget_id,
128 (None, Some(new)) => new.widget_id() == widget_id,
129 (_, None) => false,
130 }
131 }
132
133 pub fn is_blur(&self, widget_id: WidgetId) -> bool {
135 match (&self.prev_focus, &self.new_focus) {
136 (Some(prev), Some(new)) => prev.widget_id() == widget_id && new.widget_id() != widget_id,
137 (Some(prev), None) => prev.widget_id() == widget_id,
138 (None, _) => false,
139 }
140 }
141
142 pub fn is_focus_enter(&self, widget_id: WidgetId) -> bool {
144 match (&self.prev_focus, &self.new_focus) {
145 (Some(prev), Some(new)) => !prev.contains(widget_id) && new.contains(widget_id),
146 (None, Some(new)) => new.contains(widget_id),
147 (_, None) => false,
148 }
149 }
150
151 pub fn is_focus_enter_enabled(&self, widget_id: WidgetId) -> bool {
154 match (&self.prev_focus, &self.new_focus) {
155 (Some(prev), Some(new)) => !prev.contains_enabled(widget_id) && new.contains_enabled(widget_id),
156 (None, Some(new)) => new.contains_enabled(widget_id),
157 (_, None) => false,
158 }
159 }
160
161 pub fn is_focus_leave(&self, widget_id: WidgetId) -> bool {
163 match (&self.prev_focus, &self.new_focus) {
164 (Some(prev), Some(new)) => prev.contains(widget_id) && !new.contains(widget_id),
165 (Some(prev), None) => prev.contains(widget_id),
166 (None, _) => false,
167 }
168 }
169
170 pub fn is_focus_leave_enabled(&self, widget_id: WidgetId) -> bool {
173 match (&self.prev_focus, &self.new_focus) {
174 (Some(prev), Some(new)) => prev.contains_enabled(widget_id) && !new.contains_enabled(widget_id),
175 (Some(prev), None) => prev.contains_enabled(widget_id),
176 (None, _) => false,
177 }
178 }
179
180 pub fn is_focused(&self, widget_id: WidgetId) -> bool {
182 self.new_focus.as_ref().map(|p| p.widget_id() == widget_id).unwrap_or(false)
183 }
184
185 pub fn is_focus_within(&self, widget_id: WidgetId) -> bool {
187 self.new_focus.as_ref().map(|p| p.contains(widget_id)).unwrap_or(false)
188 }
189}
190
191impl ReturnFocusChangedArgs {
192 pub fn is_widget_move(&self) -> bool {
194 match (&self.prev_return, &self.new_return) {
195 (Some(prev), Some(new)) => prev.widget_id() == new.widget_id() && prev != new,
196 _ => false,
197 }
198 }
199
200 pub fn is_alt_return(&self) -> bool {
203 if let Some(scope) = &self.scope {
204 match (&self.prev_return, &self.new_return) {
205 (Some(prev), None) => !prev.contains(scope.widget_id()),
206 (None, Some(new)) => !new.contains(scope.widget_id()),
207 _ => false,
208 }
209 } else {
210 false
211 }
212 }
213
214 pub fn lost_return_focus(&self, widget_id: WidgetId) -> bool {
219 self.prev_return.as_ref().map(|p| p.contains(widget_id)).unwrap_or(false)
220 && self.new_return.as_ref().map(|p| !p.contains(widget_id)).unwrap_or(true)
221 }
222
223 pub fn got_return_focus(&self, widget_id: WidgetId) -> bool {
228 self.prev_return.as_ref().map(|p| !p.contains(widget_id)).unwrap_or(true)
229 && self.new_return.as_ref().map(|p| p.contains(widget_id)).unwrap_or(false)
230 }
231
232 pub fn was_return_focus(&self, widget_id: WidgetId) -> bool {
237 self.prev_return.as_ref().map(|p| p.widget_id() == widget_id).unwrap_or(false)
238 && self.new_return.as_ref().map(|p| p.widget_id() != widget_id).unwrap_or(true)
239 }
240
241 pub fn is_return_focus(&self, widget_id: WidgetId) -> bool {
246 self.prev_return.as_ref().map(|p| p.widget_id() != widget_id).unwrap_or(true)
247 && self.new_return.as_ref().map(|p| p.widget_id() == widget_id).unwrap_or(false)
248 }
249
250 pub fn is_return_focus_enter(&self, widget_id: WidgetId) -> bool {
252 match (&self.prev_return, &self.new_return) {
253 (Some(prev), Some(new)) => !prev.contains(widget_id) && new.contains(widget_id),
254 (None, Some(new)) => new.contains(widget_id),
255 (_, None) => false,
256 }
257 }
258
259 pub fn is_return_focus_leave(&self, widget_id: WidgetId) -> bool {
261 match (&self.prev_return, &self.new_return) {
262 (Some(prev), Some(new)) => prev.contains(widget_id) && !new.contains(widget_id),
263 (Some(prev), None) => prev.contains(widget_id),
264 (None, _) => false,
265 }
266 }
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
271pub enum FocusChangedCause {
272 Request(FocusRequest),
274
275 ScopeGotFocus(bool),
279
280 Recovery,
282}
283impl FocusChangedCause {
284 pub fn request_target(self) -> Option<FocusTarget> {
286 match self {
287 Self::Request(r) => Some(r.target),
288 _ => None,
289 }
290 }
291}
292
293event! {
294 pub static FOCUS_CHANGED_EVENT: FocusChangedArgs;
300
301 pub static RETURN_FOCUS_CHANGED_EVENT: ReturnFocusChangedArgs;
307}
308
309#[derive(Default)]
343pub struct FocusManager {
344 commands: Option<FocusCommands>,
345 pending_render: Option<WidgetInfoTree>,
346}
347
348impl AppExtension for FocusManager {
349 fn init(&mut self) {
350 WINDOW_FOCUS.hook_focus_service(FOCUS.focused());
351 self.commands = Some(FocusCommands::new());
352 }
353
354 fn event_preview(&mut self, update: &mut EventUpdate) {
355 if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
356 if FOCUS_SV
357 .read()
358 .focused
359 .as_ref()
360 .map(|f| f.path.window_id() == args.window_id)
361 .unwrap_or_default()
362 {
363 if UPDATES.is_pending_render(args.window_id) {
366 self.pending_render = Some(args.tree.clone());
367 } else {
368 self.pending_render = None;
370 self.on_info_tree_update(args.tree.clone());
371 }
372 }
373 focus_info::FocusTreeData::consolidate_alt_scopes(&args.prev_tree, &args.tree);
374 } else if let Some(args) = ACCESS_FOCUS_EVENT.on(update) {
375 let is_focused = FOCUS.is_focused(args.widget_id).get();
376 if args.focus {
377 if !is_focused {
378 FOCUS.focus_widget(args.widget_id, false);
379 }
380 } else if is_focused {
381 FOCUS.focus_exit();
382 }
383 } else if let Some(args) = ACCESS_FOCUS_NAV_ORIGIN_EVENT.on(update) {
384 FOCUS.navigation_origin().set(Some(args.widget_id));
385 } else {
386 self.commands.as_mut().unwrap().event_preview(update);
387 }
388 }
389
390 fn render(&mut self, _: &mut RenderUpdates, _: &mut RenderUpdates) {
391 if let Some(tree) = self.pending_render.take() {
392 self.on_info_tree_update(tree);
393 } else {
394 let focus = FOCUS_SV.read();
396 let mut invalidated_cmds_or_focused = None;
397
398 if let Some(f) = &focus.focused {
399 let w_id = f.path.window_id();
400 if let Ok(tree) = WINDOWS.widget_tree(w_id)
401 && focus.enabled_nav.needs_refresh(&tree)
402 {
403 invalidated_cmds_or_focused = Some(tree);
404 }
405 }
406
407 if let Some(tree) = invalidated_cmds_or_focused {
408 drop(focus);
409 self.on_info_tree_update(tree);
410 }
411 }
412 }
413
414 fn event(&mut self, update: &mut EventUpdate) {
415 let mut request = None;
416
417 if let Some(args) = MOUSE_INPUT_EVENT.on(update) {
418 if args.is_mouse_down() {
419 request = Some(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
421 }
422 } else if let Some(args) = TOUCH_INPUT_EVENT.on(update) {
423 if args.is_touch_start() {
424 request = Some(FocusRequest::direct_or_exit(args.target.widget_id(), true, false));
426 }
427 } else if let Some(args) = ACCESS_CLICK_EVENT.on(update) {
428 request = Some(FocusRequest::direct_or_exit(args.widget_id, true, false));
430 } else if let Some(args) = WINDOW_FOCUS_CHANGED_EVENT.on(update) {
431 let mut focus = FOCUS_SV.write();
433 if args.new_focus.is_some()
434 && let Some(pending) = focus.pending_window_focus.take()
435 && args.is_focus(pending.window)
436 {
437 request = Some(FocusRequest::direct_or_related(
438 pending.target,
439 pending.nav_origin.is_some(),
440 pending.highlight,
441 ));
442 }
443 if request.is_none()
444 && let Some(args) = focus.continue_focus()
445 {
446 self.notify(&mut focus, Some(args));
447 }
448
449 if let Some(window_id) = args.closed() {
450 for args in focus.cleanup_returns_win_closed(window_id) {
451 RETURN_FOCUS_CHANGED_EVENT.notify(args);
452 }
453 }
454 } else if let Some(args) = RAW_KEY_INPUT_EVENT.on(update) {
455 FOCUS_SV.write().last_keyboard_event = args.timestamp;
456 }
457
458 if let Some(request) = request {
459 let mut focus = FOCUS_SV.write();
460 if !matches!(&focus.request, PendingFocusRequest::Update(_)) {
461 focus.request = PendingFocusRequest::None;
462 focus.pending_highlight = false;
463 focus.pending_window_focus = None;
464 let args = focus.fulfill_request(request, false);
465 self.notify(&mut focus, args);
466 }
467 }
468 }
469
470 fn update(&mut self) {
471 let mut focus = FOCUS_SV.write();
472 if let Some((request, is_retry)) = focus.request.take_update() {
473 focus.pending_highlight = false;
474 let args = focus.fulfill_request(request, is_retry);
475 self.notify(&mut focus, args);
476 } else if mem::take(&mut focus.pending_highlight) {
477 let args = focus.continue_focus_highlight(true);
478 self.notify(&mut focus, args);
479 }
480
481 if let Some(wgt_id) = focus.navigation_origin_var.get_new()
482 && wgt_id != focus.navigation_origin
483 {
484 focus.navigation_origin = wgt_id;
485 focus.update_enabled_nav_with_origin();
486 let commands = self.commands.as_mut().unwrap();
487 commands.update_enabled(focus.enabled_nav.nav);
488 }
489 }
490
491 fn info(&mut self, _: &mut InfoUpdates) {
492 let mut focus = FOCUS_SV.write();
493 if let Some(r) = focus.request.take_info() {
494 focus.request = PendingFocusRequest::RetryUpdate(r);
495 UPDATES.update(None);
496 }
497 }
498}
499impl FocusManager {
500 fn on_info_tree_update(&mut self, tree: WidgetInfoTree) {
501 let mut focus = FOCUS_SV.write();
502 let focus = &mut *focus;
503 focus.update_focused_center();
504
505 let args = focus.continue_focus();
507 self.notify(focus, args);
508
509 for args in focus.cleanup_returns(FocusInfoTree::new(
511 tree,
512 focus.focus_disabled_widgets.get(),
513 focus.focus_hidden_widgets.get(),
514 )) {
515 RETURN_FOCUS_CHANGED_EVENT.notify(args);
516 }
517 }
518
519 fn notify(&mut self, focus: &mut FocusService, args: Option<FocusChangedArgs>) {
520 if let Some(mut args) = args {
521 if !args.highlight && args.new_focus.is_some() && focus.auto_highlight(args.timestamp) {
522 args.highlight = true;
523 focus.is_highlighting = true;
524 focus.is_highlighting_var.set(true);
525 }
526
527 let is_tab_cycle_reentry = matches!(args.cause.request_target(), Some(FocusTarget::Prev | FocusTarget::Next))
529 && match (&args.prev_focus, &args.new_focus) {
530 (Some(p), Some(n)) => p.contains(n.widget_id()),
531 _ => false,
532 };
533
534 let reverse = matches!(args.cause.request_target(), Some(FocusTarget::Prev));
535 let prev_focus = args.prev_focus.clone();
536 FOCUS_CHANGED_EVENT.notify(args);
537
538 while let Some(after_args) = focus.move_after_focus(is_tab_cycle_reentry, reverse) {
540 FOCUS_CHANGED_EVENT.notify(after_args);
541 }
542
543 for return_args in focus.update_returns(prev_focus) {
544 RETURN_FOCUS_CHANGED_EVENT.notify(return_args);
545 }
546 }
547
548 let commands = self.commands.as_mut().unwrap();
549 commands.update_enabled(focus.enabled_nav.nav);
550 }
551}
552
553app_local! {
554 static FOCUS_SV: FocusService = {
555 APP.extensions().require::<FocusManager>();
556 FocusService::new()
557 };
558}
559
560pub struct FOCUS;
566impl FOCUS {
567 #[must_use]
572 pub fn auto_highlight(&self) -> Var<Option<Duration>> {
573 FOCUS_SV.read().auto_highlight.clone()
574 }
575
576 #[must_use]
587 pub fn focus_disabled_widgets(&self) -> Var<bool> {
588 FOCUS_SV.read().focus_disabled_widgets.clone()
589 }
590
591 #[must_use]
602 pub fn focus_hidden_widgets(&self) -> Var<bool> {
603 FOCUS_SV.read().focus_hidden_widgets.clone()
604 }
605
606 #[must_use]
618 pub fn navigation_origin(&self) -> Var<Option<WidgetId>> {
619 FOCUS_SV.read().navigation_origin_var.clone()
620 }
621
622 #[must_use]
624 pub fn focused(&self) -> Var<Option<InteractionPath>> {
625 FOCUS_SV.read().focused_var.read_only()
626 }
627
628 #[must_use]
630 pub fn return_focused(&self, scope_id: WidgetId) -> Var<Option<InteractionPath>> {
631 FOCUS_SV
632 .write()
633 .return_focused_var
634 .entry(scope_id)
635 .or_insert_with(|| var(None))
636 .read_only()
637 }
638
639 pub fn is_window_focused(&self, window_id: WindowId) -> Var<bool> {
643 self.focused().map(move |p| matches!(p, Some(p) if p.window_id() == window_id))
644 }
645
646 pub fn is_focus_within(&self, widget_id: WidgetId) -> Var<bool> {
650 self.focused().map(move |p| matches!(p, Some(p) if p.contains(widget_id)))
651 }
652
653 pub fn is_focused(&self, widget_id: WidgetId) -> Var<bool> {
657 self.focused().map(move |p| matches!(p, Some(p) if p.widget_id() == widget_id))
658 }
659
660 #[must_use]
662 pub fn alt_return(&self) -> Var<Option<InteractionPath>> {
663 FOCUS_SV.read().alt_return_var.read_only()
664 }
665
666 #[must_use]
668 pub fn in_alt(&self) -> Var<bool> {
669 FOCUS_SV.read().alt_return_var.map(|p| p.is_some())
670 }
671
672 #[must_use]
674 pub fn is_highlighting(&self) -> Var<bool> {
675 FOCUS_SV.read().is_highlighting_var.read_only()
676 }
677
678 pub fn focus(&self, mut request: FocusRequest) {
682 let mut f = FOCUS_SV.write();
683 if !request.highlight && f.auto_highlight(INSTANT.now()) {
684 request.highlight = true;
685 }
686 f.pending_window_focus = None;
687 f.request = PendingFocusRequest::Update(request);
688 UPDATES.update(None);
689 }
690
691 fn on_disabled_cmd(&self) {
693 let f = FOCUS_SV.read();
694 if f.auto_highlight.get().is_some() && !f.is_highlighting {
695 drop(f);
696 self.highlight();
697 }
698 }
699
700 pub fn highlight(&self) {
704 let mut f = FOCUS_SV.write();
705 f.pending_highlight = true;
706 UPDATES.update(None);
707 }
708
709 pub fn focus_widget(&self, widget_id: impl Into<WidgetId>, highlight: bool) {
720 self.focus(FocusRequest::direct(widget_id.into(), highlight));
721 }
722
723 pub fn focus_widget_or_exit(&self, widget_id: impl Into<WidgetId>, navigation_origin: bool, highlight: bool) {
735 self.focus(FocusRequest::direct_or_exit(widget_id.into(), navigation_origin, highlight));
736 }
737
738 pub fn focus_widget_or_enter(&self, widget_id: impl Into<WidgetId>, navigation_origin: bool, highlight: bool) {
750 self.focus(FocusRequest::direct_or_enter(widget_id.into(), navigation_origin, highlight));
751 }
752
753 pub fn focus_widget_or_related(&self, widget_id: impl Into<WidgetId>, navigation_origin: bool, highlight: bool) {
766 self.focus(FocusRequest::direct_or_related(widget_id.into(), navigation_origin, highlight));
767 }
768
769 pub fn focus_enter(&self) {
775 let req = FocusRequest::enter(FOCUS_SV.read().is_highlighting);
776 self.focus(req);
777 }
778
779 pub fn focus_exit(&self) {
786 let req = FocusRequest::exit(FOCUS_SV.read().is_highlighting);
787 self.focus(req)
788 }
789
790 pub fn focus_next(&self) {
796 let req = FocusRequest::next(FOCUS_SV.read().is_highlighting);
797 self.focus(req);
798 }
799
800 pub fn focus_prev(&self) {
806 let req = FocusRequest::prev(FOCUS_SV.read().is_highlighting);
807 self.focus(req);
808 }
809
810 pub fn focus_up(&self) {
816 let req = FocusRequest::up(FOCUS_SV.read().is_highlighting);
817 self.focus(req);
818 }
819
820 pub fn focus_right(&self) {
826 let req = FocusRequest::right(FOCUS_SV.read().is_highlighting);
827 self.focus(req);
828 }
829
830 pub fn focus_down(&self) {
836 let req = FocusRequest::down(FOCUS_SV.read().is_highlighting);
837 self.focus(req);
838 }
839
840 pub fn focus_left(&self) {
846 let req = FocusRequest::left(FOCUS_SV.read().is_highlighting);
847 self.focus(req);
848 }
849
850 pub fn focus_alt(&self) {
856 let req = FocusRequest::alt(FOCUS_SV.read().is_highlighting);
857 self.focus(req);
858 }
859}
860
861enum PendingFocusRequest {
862 None,
863 InfoRetry(FocusRequest, DInstant),
864 Update(FocusRequest),
865 RetryUpdate(FocusRequest),
866}
867impl PendingFocusRequest {
868 fn take_update(&mut self) -> Option<(FocusRequest, bool)> {
869 match mem::replace(self, PendingFocusRequest::None) {
870 PendingFocusRequest::Update(r) => Some((r, false)),
871 PendingFocusRequest::RetryUpdate(r) => Some((r, true)),
872 r => {
873 *self = r;
874 None
875 }
876 }
877 }
878 fn take_info(&mut self) -> Option<FocusRequest> {
879 match mem::replace(self, PendingFocusRequest::None) {
880 PendingFocusRequest::InfoRetry(r, i) => {
881 if i.elapsed() < 100.ms() {
882 Some(r)
883 } else {
884 None
885 }
886 }
887 r => {
888 *self = r;
889 None
890 }
891 }
892 }
893}
894
895struct PendingWindowFocus {
896 window: WindowId,
897 target: WidgetId,
898 highlight: bool,
899 nav_origin: Option<WidgetId>,
900}
901
902struct FocusService {
903 auto_highlight: Var<Option<Duration>>,
904 last_keyboard_event: DInstant,
905
906 focus_disabled_widgets: Var<bool>,
907 focus_hidden_widgets: Var<bool>,
908
909 request: PendingFocusRequest,
910
911 focused_var: Var<Option<InteractionPath>>,
912 focused: Option<FocusedInfo>,
913 navigation_origin_var: Var<Option<WidgetId>>,
914 navigation_origin: Option<WidgetId>,
915
916 return_focused_var: IdMap<WidgetId, Var<Option<InteractionPath>>>,
917 return_focused: IdMap<WidgetId, InteractionPath>,
918
919 alt_return_var: Var<Option<InteractionPath>>,
920 alt_return: Option<(InteractionPath, InteractionPath)>,
921
922 is_highlighting_var: Var<bool>,
923 is_highlighting: bool,
924
925 enabled_nav: EnabledNavWithFrame,
926
927 pending_window_focus: Option<PendingWindowFocus>,
928 pending_highlight: bool,
929}
930impl FocusService {
931 #[must_use]
932 fn new() -> Self {
933 Self {
934 auto_highlight: var(Some(300.ms())),
935 last_keyboard_event: DInstant::EPOCH,
936
937 focus_disabled_widgets: var(true),
938 focus_hidden_widgets: var(true),
939
940 request: PendingFocusRequest::None,
941
942 focused_var: var(None),
943 focused: None,
944 navigation_origin_var: var(None),
945 navigation_origin: None,
946
947 return_focused_var: IdMap::default(),
948 return_focused: IdMap::default(),
949
950 alt_return_var: var(None),
951 alt_return: None,
952
953 is_highlighting_var: var(false),
954 is_highlighting: false,
955
956 enabled_nav: EnabledNavWithFrame::invalid(),
957
958 pending_window_focus: None,
959 pending_highlight: false,
960 }
961 }
962
963 fn auto_highlight(&self, timestamp: DInstant) -> bool {
964 if let Some(dur) = self.auto_highlight.get()
965 && timestamp.duration_since(self.last_keyboard_event) <= dur
966 {
967 return true;
968 }
969 false
970 }
971
972 fn update_enabled_nav_with_origin(&mut self) {
973 let mut origin = self
974 .focused
975 .as_ref()
976 .and_then(|f| WINDOWS.widget_tree(f.path.window_id()).ok()?.get(f.path.widget_id()));
977 if let Some(id) = self.navigation_origin
978 && let Some(focused) = &origin
979 && let Some(o) = focused.tree().get(id)
980 {
981 origin = Some(o);
982 }
983
984 if let Some(o) = origin {
985 let o = o.into_focus_info(self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get());
986 self.enabled_nav = o.enabled_nav_with_frame();
987 } else {
988 self.enabled_nav.nav = FocusNavAction::empty();
989 }
990 }
991
992 #[must_use]
993 fn fulfill_request(&mut self, request: FocusRequest, is_info_retry: bool) -> Option<FocusChangedArgs> {
994 match request.target {
995 FocusTarget::Direct { target } => self.focus_direct(target, false, request.highlight, false, false, request),
996 FocusTarget::DirectOrExit { target, navigation_origin } => {
997 self.focus_direct(target, navigation_origin, request.highlight, false, true, request)
998 }
999 FocusTarget::DirectOrEnter { target, navigation_origin } => {
1000 self.focus_direct(target, navigation_origin, request.highlight, true, false, request)
1001 }
1002 FocusTarget::DirectOrRelated { target, navigation_origin } => {
1003 self.focus_direct(target, navigation_origin, request.highlight, true, true, request)
1004 }
1005 move_ => {
1006 let origin;
1007 let origin_tree;
1008 if let Some(o) = self.navigation_origin_var.get() {
1009 origin = Some(o);
1010 origin_tree = WINDOWS.focused_info();
1011 self.navigation_origin_var.set(None);
1012 self.navigation_origin = None;
1013 } else if let Some(prev) = &self.focused {
1014 origin = Some(prev.path.widget_id());
1015 origin_tree = WINDOWS.widget_tree(prev.path.window_id()).ok();
1016 } else {
1017 origin = None;
1018 origin_tree = None;
1019 }
1020
1021 if let Some(info) = origin_tree
1022 && let Some(origin) = origin
1023 {
1024 if let Some(w) = info.get(origin) {
1025 let w = w.into_focus_info(self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get());
1026 if let Some(new_focus) = match move_ {
1027 FocusTarget::Next => w.next_tab(false),
1029 FocusTarget::Prev => w.prev_tab(false),
1030 FocusTarget::Enter => w.first_tab_descendant(),
1031 FocusTarget::Exit => {
1032 if self.alt_return.is_some() && (w.is_alt_scope() || w.ancestors().any(|w| w.is_alt_scope())) {
1033 self.new_focus_for_alt_exit(w, is_info_retry, request.highlight)
1034 } else {
1035 w.ancestors().next()
1036 }
1037 }
1038 FocusTarget::Up => w.next_up(),
1040 FocusTarget::Right => w.next_right(),
1041 FocusTarget::Down => w.next_down(),
1042 FocusTarget::Left => w.next_left(),
1043 FocusTarget::Alt => {
1045 if let Some(alt) = w.alt_scope() {
1046 Some(alt)
1047 } else if self.alt_return.is_some() {
1048 self.new_focus_for_alt_exit(w, is_info_retry, request.highlight)
1051 } else {
1052 None
1053 }
1054 }
1055 FocusTarget::Direct { .. }
1057 | FocusTarget::DirectOrExit { .. }
1058 | FocusTarget::DirectOrEnter { .. }
1059 | FocusTarget::DirectOrRelated { .. } => {
1060 unreachable!()
1061 }
1062 } {
1063 self.enabled_nav = new_focus.enabled_nav_with_frame();
1065 self.move_focus(
1066 Some(FocusedInfo::new(new_focus)),
1067 None,
1068 request.highlight,
1069 FocusChangedCause::Request(request),
1070 )
1071 } else {
1072 self.continue_focus_highlight(request.highlight)
1074 }
1075 } else {
1076 self.continue_focus_highlight(request.highlight)
1078 }
1079 } else {
1080 self.continue_focus_highlight(request.highlight)
1082 }
1083 }
1084 }
1085 }
1086
1087 #[must_use]
1089 fn new_focus_for_alt_exit(&mut self, prev_w: WidgetFocusInfo, is_info_retry: bool, highlight: bool) -> Option<WidgetFocusInfo> {
1090 let (_, return_path) = self.alt_return.as_ref().unwrap();
1091
1092 let return_int = return_path.interactivity();
1093 let return_id = return_path.widget_id();
1094 let info = prev_w.focus_tree();
1095
1096 let r = info.get_or_parent(return_path);
1097 if let Some(w) = &r
1098 && w.info().id() != return_id
1099 && !is_info_retry
1100 && return_int.is_blocked()
1101 {
1102 if let Some(exists) = info.tree().get(return_id) {
1105 let exists = exists.into_focus_info(info.focus_disabled_widgets(), info.focus_hidden_widgets());
1106 if !exists.is_focusable() && exists.info().interactivity().is_blocked() {
1107 self.request =
1115 PendingFocusRequest::InfoRetry(FocusRequest::direct_or_related(return_id, false, highlight), INSTANT.now());
1116 }
1117 }
1118 }
1119
1120 r
1121 }
1122
1123 #[must_use]
1125 fn continue_focus(&mut self) -> Option<FocusChangedArgs> {
1126 if let Some(focused) = &self.focused
1127 && let Ok(true) = WINDOWS.is_focused(focused.path.window_id())
1128 {
1129 let info = WINDOWS.widget_tree(focused.path.window_id()).unwrap();
1130 if let Some(widget) = info
1131 .get(focused.path.widget_id())
1132 .map(|w| w.into_focus_info(self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get()))
1133 {
1134 if widget.is_focusable() {
1135 self.enabled_nav = widget.enabled_nav_with_frame();
1137 return self.move_focus(
1138 Some(FocusedInfo::new(widget)),
1139 self.navigation_origin_var.get(),
1140 self.is_highlighting,
1141 FocusChangedCause::Recovery,
1142 );
1143 } else {
1144 if let Some(parent) = widget.parent() {
1146 let new_focus = parent.nearest(focused.center, Px::MAX).unwrap_or(parent);
1148 self.enabled_nav = new_focus.enabled_nav_with_frame();
1149 return self.move_focus(
1150 Some(FocusedInfo::new(new_focus)),
1151 self.navigation_origin_var.get(),
1152 self.is_highlighting,
1153 FocusChangedCause::Recovery,
1154 );
1155 } else {
1156 return self.focus_focused_window(self.is_highlighting);
1158 }
1159 }
1160 } else {
1161 for &parent in focused.path.ancestors().iter().rev() {
1163 if let Some(parent) = info
1164 .get(parent)
1165 .and_then(|w| w.into_focusable(self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get()))
1166 {
1167 let new_focus = parent.nearest(focused.center, Px::MAX).unwrap_or(parent);
1169 self.enabled_nav = new_focus.enabled_nav_with_frame();
1170 return self.move_focus(
1171 Some(FocusedInfo::new(new_focus)),
1172 self.navigation_origin_var.get(),
1173 self.is_highlighting,
1174 FocusChangedCause::Recovery,
1175 );
1176 }
1177 }
1178 }
1179 } self.focus_focused_window(false)
1182 }
1183
1184 #[must_use]
1185 fn continue_focus_highlight(&mut self, highlight: bool) -> Option<FocusChangedArgs> {
1186 if let Some(mut args) = self.continue_focus() {
1187 args.highlight = highlight;
1188 self.is_highlighting = highlight;
1189 self.is_highlighting_var.set(highlight);
1190 Some(args)
1191 } else if self.is_highlighting != highlight {
1192 self.is_highlighting = highlight;
1193 self.is_highlighting_var.set(highlight);
1194 let focused = self.focused.as_ref().map(|p| p.path.clone());
1195 Some(FocusChangedArgs::now(
1196 focused.clone(),
1197 focused,
1198 highlight,
1199 FocusChangedCause::Recovery,
1200 self.enabled_nav.nav,
1201 ))
1202 } else {
1203 None
1204 }
1205 }
1206
1207 #[must_use]
1208 fn focus_direct(
1209 &mut self,
1210 widget_id: WidgetId,
1211 navigation_origin: bool,
1212 highlight: bool,
1213 fallback_to_children: bool,
1214 fallback_to_parents: bool,
1215 request: FocusRequest,
1216 ) -> Option<FocusChangedArgs> {
1217 let mut next_origin = None;
1218 let mut target = None;
1219 if let Some(w) = WINDOWS
1220 .widget_trees()
1221 .iter()
1222 .find_map(|info| info.get(widget_id))
1223 .map(|w| w.into_focus_info(self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get()))
1224 {
1225 if w.is_focusable() {
1226 let enable = w.enabled_nav_with_frame();
1227 target = Some((FocusedInfo::new(w), enable));
1228 } else if fallback_to_children {
1229 let enable = if navigation_origin {
1230 next_origin = Some(widget_id);
1231 Some(w.enabled_nav_with_frame())
1232 } else {
1233 None
1234 };
1235 if let Some(w) = w.descendants().next() {
1236 let enable = enable.unwrap_or_else(|| w.enabled_nav_with_frame());
1237 target = Some((FocusedInfo::new(w), enable));
1238 }
1239 } else if fallback_to_parents {
1240 let enable = if navigation_origin {
1241 next_origin = Some(widget_id);
1242 Some(w.enabled_nav_with_frame())
1243 } else {
1244 None
1245 };
1246 if let Some(w) = w.parent() {
1247 let enable = enable.unwrap_or_else(|| w.enabled_nav_with_frame());
1248 target = Some((FocusedInfo::new(w), enable));
1249 }
1250 }
1251 }
1252
1253 if let Some((target, enabled_nav)) = target {
1254 if let Ok(false) = WINDOWS.is_focused(target.path.window_id()) {
1255 if request.force_window_focus || WINDOWS.focused_window_id().is_some() {
1256 WINDOWS.focus(target.path.window_id()).unwrap();
1258 } else if request.window_indicator.is_some() {
1259 WINDOWS
1261 .vars(target.path.window_id())
1262 .unwrap()
1263 .focus_indicator()
1264 .set(request.window_indicator);
1265 }
1266
1267 self.pending_window_focus = Some(PendingWindowFocus {
1269 window: target.path.window_id(),
1270 target: target.path.widget_id(),
1271 highlight,
1272 nav_origin: next_origin,
1273 });
1274 self.navigation_origin = next_origin;
1275 self.navigation_origin_var.set(next_origin);
1276 None
1277 } else {
1278 self.enabled_nav = enabled_nav;
1279 self.move_focus(Some(target), next_origin, highlight, FocusChangedCause::Request(request))
1280 }
1281 } else {
1282 self.navigation_origin = next_origin;
1283 self.navigation_origin_var.set(next_origin);
1284 self.change_highlight(highlight, request)
1285 }
1286 }
1287
1288 #[must_use]
1289 fn change_highlight(&mut self, highlight: bool, request: FocusRequest) -> Option<FocusChangedArgs> {
1290 if self.is_highlighting != highlight {
1291 self.is_highlighting = highlight;
1292 self.is_highlighting_var.set(highlight);
1293 let focused = self.focused.as_ref().map(|p| p.path.clone());
1294 Some(FocusChangedArgs::now(
1295 focused.clone(),
1296 focused,
1297 highlight,
1298 FocusChangedCause::Request(request),
1299 self.enabled_nav.nav,
1300 ))
1301 } else {
1302 None
1303 }
1304 }
1305
1306 #[must_use]
1307 fn focus_focused_window(&mut self, highlight: bool) -> Option<FocusChangedArgs> {
1308 if let Some(info) = WINDOWS.focused_info() {
1309 let info = FocusInfoTree::new(info, self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get());
1310 if let Some(root) = info.focusable_root() {
1311 self.enabled_nav = root.enabled_nav_with_frame();
1313 self.move_focus(Some(FocusedInfo::new(root)), None, highlight, FocusChangedCause::Recovery)
1314 } else {
1315 self.enabled_nav = EnabledNavWithFrame::invalid();
1317 self.move_focus(None, None, false, FocusChangedCause::Recovery)
1318 }
1319 } else {
1320 self.enabled_nav = EnabledNavWithFrame::invalid();
1322 self.move_focus(None, None, false, FocusChangedCause::Recovery)
1323 }
1324 }
1325
1326 #[must_use]
1327 fn move_focus(
1328 &mut self,
1329 new_focus: Option<FocusedInfo>,
1330 new_origin: Option<WidgetId>,
1331 highlight: bool,
1332 cause: FocusChangedCause,
1333 ) -> Option<FocusChangedArgs> {
1334 let prev_highlight = std::mem::replace(&mut self.is_highlighting, highlight);
1335 self.is_highlighting_var.set(highlight);
1336
1337 self.navigation_origin = new_origin;
1338 if self.navigation_origin_var.get() != new_origin {
1339 self.navigation_origin_var.set(new_origin);
1340 }
1341
1342 let r = if self.focused.as_ref().map(|p| &p.path) != new_focus.as_ref().map(|p| &p.path) {
1343 let new_focus = new_focus.as_ref().map(|p| p.path.clone());
1344 let args = FocusChangedArgs::now(
1345 self.focused.take().map(|p| p.path),
1346 new_focus.clone(),
1347 self.is_highlighting,
1348 cause,
1349 self.enabled_nav.nav,
1350 );
1351 self.focused_var.set(new_focus);
1352 Some(args)
1353 } else if prev_highlight != highlight {
1354 let new_focus = new_focus.as_ref().map(|p| p.path.clone());
1355 Some(FocusChangedArgs::now(
1356 new_focus.clone(),
1357 new_focus,
1358 highlight,
1359 cause,
1360 self.enabled_nav.nav,
1361 ))
1362 } else {
1363 None
1364 };
1365
1366 self.focused = new_focus;
1368
1369 r
1370 }
1371
1372 #[must_use]
1373 fn move_after_focus(&mut self, is_tab_cycle_reentry: bool, reverse: bool) -> Option<FocusChangedArgs> {
1374 if let Some(focused) = &self.focused
1375 && let Some(info) = WINDOWS.focused_info()
1376 && let Some(widget) =
1377 FocusInfoTree::new(info, self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get()).get(focused.path.widget_id())
1378 {
1379 if let Some(nested) = widget.nested_window() {
1380 tracing::debug!("focus nested window {nested:?}");
1381 let _ = WINDOWS.focus(nested);
1382 } else if widget.is_scope() {
1383 let last_focused = |id| self.return_focused.get(&id).map(|p| p.as_path());
1384 if let Some(widget) = widget.on_focus_scope_move(last_focused, is_tab_cycle_reentry, reverse) {
1385 self.enabled_nav = widget.enabled_nav_with_frame();
1386 return self.move_focus(
1387 Some(FocusedInfo::new(widget)),
1388 self.navigation_origin,
1389 self.is_highlighting,
1390 FocusChangedCause::ScopeGotFocus(reverse),
1391 );
1392 }
1393 }
1394 }
1395 None
1396 }
1397
1398 #[must_use]
1400 fn update_returns(&mut self, prev_focus: Option<InteractionPath>) -> Vec<ReturnFocusChangedArgs> {
1401 let mut r = vec![];
1402
1403 if let Some((scope, _)) = &mut self.alt_return {
1404 let mut retain_alt = false;
1407 if let Some(new_focus) = &self.focused {
1408 if let Some(s) = new_focus.path.ancestor_path(scope.widget_id()) {
1409 retain_alt = true; *scope = s.into_owned();
1411 } else if let Ok(info) = WINDOWS.widget_tree(new_focus.path.window_id())
1412 && let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get())
1413 .get(new_focus.path.widget_id())
1414 {
1415 let alt_scope = if widget.is_alt_scope() {
1416 Some(widget)
1417 } else {
1418 widget.scopes().find(|s| s.is_alt_scope())
1419 };
1420
1421 if let Some(alt_scope) = alt_scope {
1422 retain_alt = true;
1424 *scope = alt_scope.info().interaction_path();
1425 }
1426 }
1427 }
1428
1429 if !retain_alt {
1430 let (scope, widget_path) = self.alt_return.take().unwrap();
1431 self.alt_return_var.set(None);
1432 r.push(ReturnFocusChangedArgs::now(scope, Some(widget_path), None));
1433 }
1434 } else if let Some(new_focus) = &self.focused {
1435 if let Ok(info) = WINDOWS.widget_tree(new_focus.path.window_id())
1439 && let Some(widget) = FocusInfoTree::new(info, self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get())
1440 .get(new_focus.path.widget_id())
1441 {
1442 let alt_scope = if widget.is_alt_scope() {
1443 Some(widget)
1444 } else {
1445 widget.scopes().find(|s| s.is_alt_scope())
1446 };
1447 if let Some(alt_scope) = alt_scope {
1448 let scope = alt_scope.info().interaction_path();
1449 if let Some(prev) = &prev_focus {
1452 r.push(ReturnFocusChangedArgs::now(scope.clone(), None, Some(prev.clone())));
1454 self.alt_return = Some((scope, prev.clone()));
1455 self.alt_return_var.set(prev.clone());
1456 } else if let Some(parent) = alt_scope.parent() {
1457 let parent_path = parent.info().interaction_path();
1459 r.push(ReturnFocusChangedArgs::now(scope.clone(), None, Some(parent_path.clone())));
1460 self.alt_return = Some((scope, parent_path.clone()));
1461 self.alt_return_var.set(parent_path);
1462 }
1463 }
1464 }
1465 }
1466
1467 if let Some(new_focus) = &self.focused
1472 && let Ok(info) = WINDOWS.widget_tree(new_focus.path.window_id())
1473 && let Some(widget) =
1474 FocusInfoTree::new(info, self.focus_disabled_widgets.get(), self.focus_hidden_widgets.get()).get(new_focus.path.widget_id())
1475 && !widget.is_alt_scope()
1476 && widget.scopes().all(|s| !s.is_alt_scope())
1477 {
1478 for scope in widget
1481 .scopes()
1482 .filter(|s| s.focus_info().scope_on_focus() == FocusScopeOnFocus::LastFocused)
1483 {
1484 let scope = scope.info().interaction_path();
1485 let path = widget.info().interaction_path();
1486 if let Some(current) = self.return_focused.get_mut(&scope.widget_id()) {
1487 if current != &path {
1488 let prev = std::mem::replace(current, path);
1489 self.return_focused_var.get(&scope.widget_id()).unwrap().set(current.clone());
1490 r.push(ReturnFocusChangedArgs::now(scope, Some(prev), Some(current.clone())));
1491 }
1492 } else {
1493 self.return_focused.insert(scope.widget_id(), path.clone());
1494 match self.return_focused_var.entry(scope.widget_id()) {
1495 IdEntry::Occupied(e) => e.get().set(Some(path.clone())),
1496 IdEntry::Vacant(e) => {
1497 e.insert(var(Some(path.clone())));
1498 }
1499 }
1500 r.push(ReturnFocusChangedArgs::now(scope, None, Some(path)));
1501 }
1502 }
1503 }
1504
1505 r
1506 }
1507
1508 #[must_use]
1510 fn cleanup_returns(&mut self, info: FocusInfoTree) -> Vec<ReturnFocusChangedArgs> {
1511 let mut r = vec![];
1512
1513 if self.return_focused_var.len() > 20 {
1514 self.return_focused_var
1515 .retain(|_, var| var.strong_count() > 1 || var.with(Option::is_some))
1516 }
1517
1518 self.return_focused.retain(|&scope_id, widget_path| {
1519 if widget_path.window_id() != info.tree().window_id() {
1520 return true; }
1522
1523 let mut retain = false;
1524
1525 if let Some(widget) = info.tree().get(widget_path.widget_id()) {
1526 if let Some(scope) = widget
1527 .clone()
1528 .into_focus_info(info.focus_disabled_widgets(), info.focus_hidden_widgets())
1529 .scopes()
1530 .find(|s| s.info().id() == scope_id)
1531 {
1532 if scope.focus_info().scope_on_focus() == FocusScopeOnFocus::LastFocused {
1533 retain = true; let path = widget.interaction_path();
1536 if &path != widget_path {
1537 r.push(ReturnFocusChangedArgs::now(
1539 scope.info().interaction_path(),
1540 Some(widget_path.clone()),
1541 Some(path.clone()),
1542 ));
1543 *widget_path = path;
1544 }
1545 }
1546 } else if let Some(scope) = info.get(scope_id)
1547 && scope.focus_info().scope_on_focus() == FocusScopeOnFocus::LastFocused
1548 {
1549 if let Some(first) = scope.first_tab_descendant() {
1551 retain = true;
1553
1554 let path = first.info().interaction_path();
1555 r.push(ReturnFocusChangedArgs::now(
1556 scope.info().interaction_path(),
1557 Some(widget_path.clone()),
1558 Some(path.clone()),
1559 ));
1560 *widget_path = path;
1561 }
1562 }
1563 } else if let Some(parent) = info.get_or_parent(widget_path) {
1564 if let Some(scope) = parent.scopes().find(|s| s.info().id() == scope_id)
1566 && scope.focus_info().scope_on_focus() == FocusScopeOnFocus::LastFocused
1567 {
1568 retain = true;
1570
1571 let path = parent.info().interaction_path();
1572 r.push(ReturnFocusChangedArgs::now(
1573 scope.info().interaction_path(),
1574 Some(widget_path.clone()),
1575 Some(path.clone()),
1576 ));
1577 *widget_path = path;
1578 }
1579 }
1580
1581 if !retain {
1582 let scope_path = info.get(scope_id).map(|i| i.info().interaction_path());
1583
1584 if scope_path.is_some() {
1585 match self.return_focused_var.entry(scope_id) {
1586 IdEntry::Occupied(e) => {
1587 if e.get().strong_count() == 1 {
1588 e.remove();
1589 } else {
1590 e.get().set(None);
1591 }
1592 }
1593 IdEntry::Vacant(_) => {}
1594 }
1595 } else if let Some(var) = self.return_focused_var.remove(&scope_id)
1596 && var.strong_count() > 1
1597 {
1598 var.set(None);
1599 }
1600
1601 r.push(ReturnFocusChangedArgs::now(scope_path, Some(widget_path.clone()), None));
1602 }
1603 retain
1604 });
1605
1606 let mut retain_alt = true;
1607 if let Some((scope, widget_path)) = &mut self.alt_return
1608 && widget_path.window_id() == info.tree().window_id()
1609 {
1610 retain_alt = false; if let Some(widget) = info.tree().get(widget_path.widget_id()) {
1615 if !widget
1616 .clone()
1617 .into_focus_info(info.focus_disabled_widgets(), info.focus_hidden_widgets())
1618 .scopes()
1619 .any(|s| s.info().id() == scope.widget_id())
1620 {
1621 retain_alt = true; let path = widget.interaction_path();
1624 if &path != widget_path {
1625 r.push(ReturnFocusChangedArgs::now(
1627 scope.clone(),
1628 Some(widget_path.clone()),
1629 Some(path.clone()),
1630 ));
1631 *widget_path = path;
1632 }
1633 }
1634 } else if let Some(parent) = info.get_or_parent(widget_path) {
1635 if !parent.scopes().any(|s| s.info().id() == scope.widget_id()) {
1637 retain_alt = true;
1639
1640 let path = parent.info().interaction_path();
1641 r.push(ReturnFocusChangedArgs::now(
1642 scope.clone(),
1643 Some(widget_path.clone()),
1644 Some(path.clone()),
1645 ));
1646 *widget_path = path.clone();
1647 self.alt_return_var.set(path);
1648 }
1649 }
1650 }
1651 if !retain_alt {
1652 let (scope_id, widget_path) = self.alt_return.take().unwrap();
1653 self.alt_return_var.set(None);
1654 r.push(ReturnFocusChangedArgs::now(scope_id, Some(widget_path), None));
1655 }
1656
1657 r
1658 }
1659
1660 #[must_use]
1662 fn cleanup_returns_win_closed(&mut self, window_id: WindowId) -> Vec<ReturnFocusChangedArgs> {
1663 let mut r = vec![];
1664
1665 if self
1666 .alt_return
1667 .as_ref()
1668 .map(|(_, w)| w.window_id() == window_id)
1669 .unwrap_or_default()
1670 {
1671 let (_, widget_path) = self.alt_return.take().unwrap();
1672 self.alt_return_var.set(None);
1673 r.push(ReturnFocusChangedArgs::now(None, Some(widget_path), None));
1674 }
1675
1676 self.return_focused.retain(|&scope_id, widget_path| {
1677 let retain = widget_path.window_id() != window_id;
1678
1679 if !retain {
1680 let var = self.return_focused_var.remove(&scope_id).unwrap();
1681 var.set(None);
1682
1683 r.push(ReturnFocusChangedArgs::now(None, Some(widget_path.clone()), None));
1684 }
1685
1686 retain
1687 });
1688
1689 r
1690 }
1691
1692 fn update_focused_center(&mut self) {
1693 if let Some(f) = &mut self.focused {
1694 let bounds = f.bounds_info.inner_bounds();
1695 if bounds != PxRect::zero() {
1696 f.center = bounds.center();
1697 }
1698 }
1699 }
1700}
1701
1702#[derive(Debug)]
1703struct FocusedInfo {
1704 path: InteractionPath,
1705 bounds_info: WidgetBoundsInfo,
1706 center: PxPoint,
1707}
1708impl FocusedInfo {
1709 pub fn new(focusable: WidgetFocusInfo) -> Self {
1710 FocusedInfo {
1711 path: focusable.info().interaction_path(),
1712 bounds_info: focusable.info().bounds_info(),
1713 center: focusable.info().center(),
1714 }
1715 }
1716}
1717
1718struct EnabledNavWithFrame {
1719 nav: FocusNavAction,
1720 spatial_frame_id: FrameId,
1721 visibility_id: FrameId,
1722}
1723impl EnabledNavWithFrame {
1724 fn invalid() -> Self {
1725 Self {
1726 nav: FocusNavAction::empty(),
1727 spatial_frame_id: FrameId::INVALID,
1728 visibility_id: FrameId::INVALID,
1729 }
1730 }
1731 fn needs_refresh(&self, tree: &WidgetInfoTree) -> bool {
1732 let stats = tree.stats();
1733 stats.bounds_updated_frame != self.spatial_frame_id || stats.vis_updated_frame != self.visibility_id
1734 }
1735}
1736trait EnabledNavWithFrameExt {
1737 fn enabled_nav_with_frame(&self) -> EnabledNavWithFrame;
1738}
1739impl EnabledNavWithFrameExt for WidgetFocusInfo {
1740 fn enabled_nav_with_frame(&self) -> EnabledNavWithFrame {
1741 let stats = self.info().tree().stats();
1742 EnabledNavWithFrame {
1743 nav: self.enabled_nav(),
1744 spatial_frame_id: stats.bounds_updated_frame,
1745 visibility_id: stats.vis_updated_frame,
1746 }
1747 }
1748}