@@ -871,6 +871,25 @@ impl WmState { |
| 871 | 871 | } |
| 872 | 872 | } |
| 873 | 873 | |
| 874 | + fn sync_mouse_after_focus(&mut self, ws_idx: usize, id: WindowId) { |
| 875 | + self.ffm_last_window = Some(id); |
| 876 | + if !self.mouse_follows_focus { |
| 877 | + return; |
| 878 | + } |
| 879 | + |
| 880 | + let rect = self |
| 881 | + .monitor_showing_workspace(ws_idx) |
| 882 | + .map_or_else(|| self.focused_rect(), |mi| self.monitor_rect(mi)); |
| 883 | + let geoms = self.workspace_focus_geometries(ws_idx, rect); |
| 884 | + if let Some((_, target_rect)) = geoms.iter().find(|(wid, _)| *wid == id) { |
| 885 | + warp_mouse_to_center(target_rect); |
| 886 | + } else { |
| 887 | + warp_mouse_to_center(&rect); |
| 888 | + } |
| 889 | + self.ffm_cooldown_until = |
| 890 | + Some(std::time::Instant::now() + std::time::Duration::from_millis(200)); |
| 891 | + } |
| 892 | + |
| 874 | 893 | fn is_external_focus_candidate(&self, id: WindowId) -> bool { |
| 875 | 894 | self.registry |
| 876 | 895 | .get(id) |
@@ -930,7 +949,7 @@ impl WmState { |
| 930 | 949 | self.dismiss_special_on_monitor(mi); |
| 931 | 950 | } |
| 932 | 951 | self.focused_monitor = mi; |
| 933 | | - self.ffm_last_window = None; |
| 952 | + self.sync_mouse_after_focus(ws_idx, id); |
| 934 | 953 | return; |
| 935 | 954 | } |
| 936 | 955 | |
@@ -956,6 +975,7 @@ impl WmState { |
| 956 | 975 | self.dismiss_special_on_monitor(self.focused_monitor); |
| 957 | 976 | } |
| 958 | 977 | self.switch_workspace(&target); |
| 978 | + self.sync_mouse_after_focus(ws_idx, id); |
| 959 | 979 | } |
| 960 | 980 | } |
| 961 | 981 | } |
@@ -3433,6 +3453,45 @@ mod tests { |
| 3433 | 3453 | ); |
| 3434 | 3454 | } |
| 3435 | 3455 | |
| 3456 | + #[test] |
| 3457 | + fn external_focus_arms_mouse_follow_for_promoted_stack_window() { |
| 3458 | + let mut state = WmState::new(); |
| 3459 | + state.monitors = vec![Monitor { |
| 3460 | + id: 42, |
| 3461 | + frame: Rect::new(0.0, 0.0, 1920.0, 1080.0), |
| 3462 | + usable_frame: Rect::new(0.0, 33.0, 1920.0, 1047.0), |
| 3463 | + is_primary: true, |
| 3464 | + active_workspace: 0, |
| 3465 | + }]; |
| 3466 | + state.sync_workspace_visibility(); |
| 3467 | + let screen = state.monitors[0].usable_frame; |
| 3468 | + |
| 3469 | + state.registry.add(tracked_window(50, 11, "Firefox")); |
| 3470 | + state.registry.add(tracked_window(51, 11, "Messages")); |
| 3471 | + state |
| 3472 | + .workspaces |
| 3473 | + .get_or_create_target(&WorkspaceTarget::Numbered(2)); |
| 3474 | + |
| 3475 | + { |
| 3476 | + let ws = state.workspaces.get_mut(1); |
| 3477 | + ws.tree.insert_with_rect(50, None, screen); |
| 3478 | + ws.tree.insert_with_rect(51, Some(50), screen); |
| 3479 | + assert!(ws.tree.make_stack_for_window(51)); |
| 3480 | + ws.tree.set_stack_active(50); |
| 3481 | + ws.record_focus(50); |
| 3482 | + } |
| 3483 | + |
| 3484 | + state.adopt_external_app_focus(11, Some(51)); |
| 3485 | + |
| 3486 | + assert_eq!(state.active_workspace().focused, Some(51)); |
| 3487 | + assert_eq!(state.ffm_last_window, Some(51)); |
| 3488 | + assert!(state.ffm_cooldown_until.is_some()); |
| 3489 | + assert_eq!( |
| 3490 | + state.active_workspace().tree.stack_info(51), |
| 3491 | + Some((vec![50, 51], 1)) |
| 3492 | + ); |
| 3493 | + } |
| 3494 | + |
| 3436 | 3495 | #[test] |
| 3437 | 3496 | fn external_app_focus_dismisses_overlay_before_switching() { |
| 3438 | 3497 | let mut state = WmState::new(); |