gardesk/tarmac / 4f78c8c

Browse files

Remember ambiguous directional focus returns

Authored by espadonne
SHA
4f78c8c0f0b296001eb15c8598bdbbf03b64f24c
Parents
1581167
Tree
919a273

1 changed file

StatusFile+-
M tarmac/src/core/state.rs 77 1
tarmac/src/core/state.rsmodified
@@ -52,6 +52,7 @@ pub struct WmState {
5252
     event_queue: Rc<RefCell<Vec<QueuedEvent>>>,
5353
     ffm_cooldown_until: Option<std::time::Instant>,
5454
     ffm_last_window: Option<WindowId>,
55
+    focus_return_memory: HashMap<(WindowId, super::tree::Direction), WindowId>,
5556
     pending_internal_focus: Option<(WindowId, i32, std::time::Instant)>,
5657
     drag: Option<DragState>,
5758
     pub focus_follows_mouse: bool,
@@ -88,6 +89,7 @@ impl WmState {
8889
             event_queue: Rc::new(RefCell::new(Vec::new())),
8990
             ffm_cooldown_until: None,
9091
             ffm_last_window: None,
92
+            focus_return_memory: HashMap::new(),
9193
             pending_internal_focus: None,
9294
             drag: None,
9395
             focus_follows_mouse: true,
@@ -702,8 +704,16 @@ impl WmState {
702704
                 false
703705
             };
704706
 
705
-            if !at_edge && let Some(target) = Node::find_adjacent(&geoms, from, direction) {
707
+            if !at_edge && let Some(default_target) = Node::find_adjacent(&geoms, from, direction) {
708
+                let candidates = Node::adjacent_candidates(&geoms, from, direction);
709
+                let target = self
710
+                    .focus_return_memory
711
+                    .get(&(from, direction))
712
+                    .copied()
713
+                    .filter(|remembered| candidates.contains(remembered))
714
+                    .unwrap_or(default_target);
706715
                 self.focus_window(target);
716
+                self.remember_focus_transition(from, direction, target);
707717
                 if self.mouse_follows_focus
708718
                     && let Some((_, rect)) = geoms.iter().find(|(id, _)| *id == target)
709719
                 {
@@ -892,6 +902,32 @@ impl WmState {
892902
             Some(std::time::Instant::now() + std::time::Duration::from_millis(200));
893903
     }
894904
 
905
+    fn opposite_direction(direction: super::tree::Direction) -> super::tree::Direction {
906
+        use super::tree::Direction;
907
+        match direction {
908
+            Direction::Left => Direction::Right,
909
+            Direction::Right => Direction::Left,
910
+            Direction::Up => Direction::Down,
911
+            Direction::Down => Direction::Up,
912
+        }
913
+    }
914
+
915
+    fn remember_focus_transition(
916
+        &mut self,
917
+        from: WindowId,
918
+        direction: super::tree::Direction,
919
+        to: WindowId,
920
+    ) {
921
+        self.focus_return_memory.insert((from, direction), to);
922
+        self.focus_return_memory
923
+            .insert((to, Self::opposite_direction(direction)), from);
924
+    }
925
+
926
+    fn prune_focus_memory_for_window(&mut self, window: WindowId) {
927
+        self.focus_return_memory
928
+            .retain(|(from, _), to| *from != window && *to != window);
929
+    }
930
+
895931
     fn mark_internal_focus(&mut self, id: WindowId) {
896932
         let Some(window) = self.registry.get(id) else {
897933
             return;
@@ -2586,6 +2622,7 @@ impl WmState {
25862622
         self.borders.remove_border(id);
25872623
         self.registry.remove(id);
25882624
         self.ax_refs.remove(&id);
2625
+        self.prune_focus_memory_for_window(id);
25892626
 
25902627
         // Find the workspace containing this window and remove from it
25912628
         if let Some(ws_idx) = self.workspaces.find_window(id) {
@@ -2641,6 +2678,7 @@ impl WmState {
26412678
         self.observers.remove(&pid);
26422679
         for w in &removed {
26432680
             self.ax_refs.remove(&w.id);
2681
+            self.prune_focus_memory_for_window(w.id);
26442682
             // Find the workspace containing this window and remove from it
26452683
             if let Some(ws_idx) = self.workspaces.find_window(w.id) {
26462684
                 let ws = self.workspaces.get_mut(ws_idx);
@@ -2888,6 +2926,7 @@ impl WmState {
28882926
                     tracing::info!(id, app = app_name, "window destroyed -> retiling");
28892927
                     self.registry.remove(id);
28902928
                     self.ax_refs.remove(&id);
2929
+                    self.prune_focus_memory_for_window(id);
28912930
                     // Find the workspace containing this window and remove from it
28922931
                     if let Some(ws_idx) = self.workspaces.find_window(id) {
28932932
                         let ws = self.workspaces.get_mut(ws_idx);
@@ -3368,6 +3407,43 @@ mod tests {
33683407
         assert_eq!(state.active_workspace().focused, Some(3));
33693408
     }
33703409
 
3410
+    #[test]
3411
+    fn focus_direction_returns_to_last_ambiguous_tile() {
3412
+        let mut state = WmState::new();
3413
+        state.monitors = vec![Monitor {
3414
+            id: 42,
3415
+            frame: Rect::new(0.0, 0.0, 1920.0, 1080.0),
3416
+            usable_frame: Rect::new(0.0, 33.0, 1920.0, 1047.0),
3417
+            is_primary: true,
3418
+            active_workspace: 0,
3419
+        }];
3420
+        state.sync_workspace_visibility();
3421
+        let screen = state.monitors[0].usable_frame;
3422
+
3423
+        {
3424
+            let ws = state.workspaces.get_mut(0);
3425
+            ws.tree.insert_with_rect(1, None, screen);
3426
+            ws.tree.insert_with_rect(2, Some(1), screen);
3427
+            ws.tree.insert_with_rect(3, Some(2), screen);
3428
+            ws.record_focus(3);
3429
+        }
3430
+
3431
+        state.focus_direction(Direction::Left);
3432
+        assert_eq!(state.active_workspace().focused, Some(1));
3433
+
3434
+        state.focus_direction(Direction::Right);
3435
+        assert_eq!(state.active_workspace().focused, Some(3));
3436
+
3437
+        state.focus_direction(Direction::Up);
3438
+        assert_eq!(state.active_workspace().focused, Some(2));
3439
+
3440
+        state.focus_direction(Direction::Left);
3441
+        assert_eq!(state.active_workspace().focused, Some(1));
3442
+
3443
+        state.focus_direction(Direction::Right);
3444
+        assert_eq!(state.active_workspace().focused, Some(2));
3445
+    }
3446
+
33713447
     #[test]
33723448
     fn mouse_hover_on_inactive_stack_sliver_does_not_change_focus() {
33733449
         let mut state = WmState::new();