gardesk/tarmac / 1936bc4

Browse files

Follow external app focus across workspaces

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
1936bc4bccdc549ae8fdedf9a0615534cd3c616a
Parents
4138e88
Tree
646e11b

1 changed file

StatusFile+-
M tarmac/src/core/state.rs 137 9
tarmac/src/core/state.rsmodified
@@ -856,6 +856,59 @@ impl WmState {
856856
         self.focus_window_impl(id, true);
857857
     }
858858
 
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
+
859912
     fn focus_window_impl(&mut self, id: WindowId, activate_app: bool) {
860913
         if let Some(ax_ref) = self.ax_refs.get(&id) {
861914
             if activate_app {
@@ -2735,15 +2788,7 @@ impl WmState {
27352788
                 if let Ok(id) = ax_get_window_id(element)
27362789
                     && self.registry.contains(id)
27372790
                 {
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);
27472792
                 }
27482793
             }
27492794
             WindowEvent::Moved { element, .. } => {
@@ -3183,6 +3228,89 @@ mod tests {
31833228
         assert_eq!(state.active_workspace().focused, Some(3));
31843229
     }
31853230
 
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
+
31863314
     #[test]
31873315
     fn unstack_focused_restores_original_tree() {
31883316
         let mut state = WmState::new();