@@ -856,6 +856,59 @@ impl WmState { |
| 856 | 856 | self.focus_window_impl(id, true); |
| 857 | 857 | } |
| 858 | 858 | |
| 859 | + fn dismiss_special_on_monitor(&mut self, monitor_idx: usize) { |
| 860 | + if monitor_idx >= self.active_specials.len() { |
| 861 | + return; |
| 862 | + } |
| 863 | + let Some(special_idx) = self.active_specials[monitor_idx] else { |
| 864 | + return; |
| 865 | + }; |
| 866 | + let wids = self.workspaces.get(special_idx).all_window_ids(); |
| 867 | + self.active_specials[monitor_idx] = None; |
| 868 | + self.sync_workspace_visibility(); |
| 869 | + for wid in wids { |
| 870 | + self.hide_window(wid); |
| 871 | + } |
| 872 | + } |
| 873 | + |
| 874 | + fn adopt_external_focus(&mut self, id: WindowId) { |
| 875 | + let Some(ws_idx) = self.workspaces.find_window(id) else { |
| 876 | + return; |
| 877 | + }; |
| 878 | + |
| 879 | + self.focus_window_impl(id, false); |
| 880 | + |
| 881 | + if let Some(mi) = self.monitor_showing_workspace(ws_idx) { |
| 882 | + if mi < self.active_specials.len() && self.active_specials[mi] != Some(ws_idx) { |
| 883 | + self.dismiss_special_on_monitor(mi); |
| 884 | + } |
| 885 | + self.focused_monitor = mi; |
| 886 | + self.ffm_last_window = None; |
| 887 | + return; |
| 888 | + } |
| 889 | + |
| 890 | + let ws_id = self.workspaces.get(ws_idx).id.clone(); |
| 891 | + match ws_id { |
| 892 | + super::workspace::WorkspaceId::Special(name) => { |
| 893 | + let current_overlay = self |
| 894 | + .active_specials |
| 895 | + .iter() |
| 896 | + .position(|special| *special == Some(ws_idx)); |
| 897 | + if let Some(mi) = current_overlay { |
| 898 | + self.focused_monitor = mi; |
| 899 | + } else { |
| 900 | + self.toggle_special(&name); |
| 901 | + } |
| 902 | + self.focus_window_impl(id, false); |
| 903 | + } |
| 904 | + _ => { |
| 905 | + if let Some(target) = ws_id.as_regular_target() { |
| 906 | + self.switch_workspace(&target); |
| 907 | + } |
| 908 | + } |
| 909 | + } |
| 910 | + } |
| 911 | + |
| 859 | 912 | fn focus_window_impl(&mut self, id: WindowId, activate_app: bool) { |
| 860 | 913 | if let Some(ax_ref) = self.ax_refs.get(&id) { |
| 861 | 914 | if activate_app { |
@@ -2735,15 +2788,7 @@ impl WmState { |
| 2735 | 2788 | if let Ok(id) = ax_get_window_id(element) |
| 2736 | 2789 | && self.registry.contains(id) |
| 2737 | 2790 | { |
| 2738 | | - if self.is_window_hidden(id) { |
| 2739 | | - return; |
| 2740 | | - } |
| 2741 | | - // Record on the workspace that contains this window, |
| 2742 | | - // not the active workspace (same fix as focus_window_impl) |
| 2743 | | - if let Some(ws_idx) = self.workspaces.find_window(id) { |
| 2744 | | - self.workspaces.get_mut(ws_idx).tree.set_stack_active(id); |
| 2745 | | - self.workspaces.get_mut(ws_idx).record_focus(id); |
| 2746 | | - } |
| 2791 | + self.adopt_external_focus(id); |
| 2747 | 2792 | } |
| 2748 | 2793 | } |
| 2749 | 2794 | WindowEvent::Moved { element, .. } => { |
@@ -3183,6 +3228,89 @@ mod tests { |
| 3183 | 3228 | assert_eq!(state.active_workspace().focused, Some(3)); |
| 3184 | 3229 | } |
| 3185 | 3230 | |
| 3231 | + #[test] |
| 3232 | + fn external_focus_reveals_hidden_workspace_and_tracks_target_window() { |
| 3233 | + let mut state = WmState::new(); |
| 3234 | + state.monitors = vec![Monitor { |
| 3235 | + id: 42, |
| 3236 | + frame: Rect::new(0.0, 0.0, 1920.0, 1080.0), |
| 3237 | + usable_frame: Rect::new(0.0, 33.0, 1920.0, 1047.0), |
| 3238 | + is_primary: true, |
| 3239 | + active_workspace: 0, |
| 3240 | + }]; |
| 3241 | + state.sync_workspace_visibility(); |
| 3242 | + let screen = state.monitors[0].usable_frame; |
| 3243 | + |
| 3244 | + state |
| 3245 | + .workspaces |
| 3246 | + .get_or_create_target(&WorkspaceTarget::Numbered(2)); |
| 3247 | + |
| 3248 | + { |
| 3249 | + let ws = state.workspaces.get_mut(1); |
| 3250 | + ws.tree.insert_with_rect(10, None, screen); |
| 3251 | + ws.tree.insert_with_rect(11, Some(10), screen); |
| 3252 | + assert!(ws.tree.make_stack_for_window(11)); |
| 3253 | + ws.tree.set_stack_active(10); |
| 3254 | + ws.record_focus(10); |
| 3255 | + } |
| 3256 | + |
| 3257 | + state.adopt_external_focus(11); |
| 3258 | + |
| 3259 | + assert_eq!(state.monitors[0].active_workspace, 1); |
| 3260 | + assert_eq!(state.focused_monitor, 0); |
| 3261 | + assert_eq!(state.active_workspace().focused, Some(11)); |
| 3262 | + assert_eq!( |
| 3263 | + state.active_workspace().tree.stack_info(11), |
| 3264 | + Some((vec![10, 11], 1)) |
| 3265 | + ); |
| 3266 | + } |
| 3267 | + |
| 3268 | + #[test] |
| 3269 | + fn external_focus_jumps_to_monitor_showing_window_workspace() { |
| 3270 | + let mut state = WmState::new(); |
| 3271 | + state.monitors = vec![ |
| 3272 | + Monitor { |
| 3273 | + id: 42, |
| 3274 | + frame: Rect::new(0.0, 0.0, 1920.0, 1080.0), |
| 3275 | + usable_frame: Rect::new(0.0, 33.0, 1920.0, 1047.0), |
| 3276 | + is_primary: true, |
| 3277 | + active_workspace: 0, |
| 3278 | + }, |
| 3279 | + Monitor { |
| 3280 | + id: 43, |
| 3281 | + frame: Rect::new(1920.0, 0.0, 1920.0, 1080.0), |
| 3282 | + usable_frame: Rect::new(1920.0, 0.0, 1920.0, 1080.0), |
| 3283 | + is_primary: false, |
| 3284 | + active_workspace: 1, |
| 3285 | + }, |
| 3286 | + ]; |
| 3287 | + state |
| 3288 | + .workspaces |
| 3289 | + .get_or_create_target(&WorkspaceTarget::Numbered(2)); |
| 3290 | + state.sync_workspace_visibility(); |
| 3291 | + let screen = state.monitors[1].usable_frame; |
| 3292 | + |
| 3293 | + { |
| 3294 | + let ws = state.workspaces.get_mut(1); |
| 3295 | + ws.tree.insert_with_rect(21, None, screen); |
| 3296 | + ws.tree.insert_with_rect(22, Some(21), screen); |
| 3297 | + assert!(ws.tree.make_stack_for_window(22)); |
| 3298 | + ws.tree.set_stack_active(21); |
| 3299 | + ws.record_focus(21); |
| 3300 | + } |
| 3301 | + |
| 3302 | + state.focused_monitor = 0; |
| 3303 | + state.adopt_external_focus(22); |
| 3304 | + |
| 3305 | + assert_eq!(state.focused_monitor, 1); |
| 3306 | + assert_eq!(state.monitors[1].active_workspace, 1); |
| 3307 | + assert_eq!(state.workspaces.get(1).focused, Some(22)); |
| 3308 | + assert_eq!( |
| 3309 | + state.workspaces.get(1).tree.stack_info(22), |
| 3310 | + Some((vec![21, 22], 1)) |
| 3311 | + ); |
| 3312 | + } |
| 3313 | + |
| 3186 | 3314 | #[test] |
| 3187 | 3315 | fn unstack_focused_restores_original_tree() { |
| 3188 | 3316 | let mut state = WmState::new(); |