@@ -1357,8 +1357,9 @@ impl WmState { |
| 1357 | 1357 | if floating_hit.is_some() { |
| 1358 | 1358 | floating_hit |
| 1359 | 1359 | } else { |
| 1360 | | - // Check tiled windows within the overlay rect |
| 1361 | | - let geoms = ws.tree.calculate_geometries_with_gaps( |
| 1360 | + // Use focus geometry here so inactive stack slivers don't |
| 1361 | + // steal focus on hover. |
| 1362 | + let geoms = ws.tree.calculate_focus_geometries_with_gaps( |
| 1362 | 1363 | overlay_rect, |
| 1363 | 1364 | gap_inner, |
| 1364 | 1365 | gap_outer, |
@@ -1384,9 +1385,10 @@ impl WmState { |
| 1384 | 1385 | if floating_under.is_some() { |
| 1385 | 1386 | floating_under |
| 1386 | 1387 | } else { |
| 1387 | | - // Check tiled windows using gap-aware geometry matching actual layout |
| 1388 | + // Use focus geometry here so inactive stack slivers don't |
| 1389 | + // steal focus on hover. |
| 1388 | 1390 | let geoms = |
| 1389 | | - self.workspace_render_geometries(self.active_ws_idx(), self.focused_rect()); |
| 1391 | + self.workspace_focus_geometries(self.active_ws_idx(), self.focused_rect()); |
| 1390 | 1392 | geoms |
| 1391 | 1393 | .iter() |
| 1392 | 1394 | .rev() |
@@ -3366,6 +3368,42 @@ mod tests { |
| 3366 | 3368 | assert_eq!(state.active_workspace().focused, Some(3)); |
| 3367 | 3369 | } |
| 3368 | 3370 | |
| 3371 | + #[test] |
| 3372 | + fn mouse_hover_on_inactive_stack_sliver_does_not_change_focus() { |
| 3373 | + let mut state = WmState::new(); |
| 3374 | + state.monitors = vec![Monitor { |
| 3375 | + id: 42, |
| 3376 | + frame: Rect::new(0.0, 0.0, 1920.0, 1080.0), |
| 3377 | + usable_frame: Rect::new(0.0, 33.0, 1920.0, 1047.0), |
| 3378 | + is_primary: true, |
| 3379 | + active_workspace: 0, |
| 3380 | + }]; |
| 3381 | + state.sync_workspace_visibility(); |
| 3382 | + let screen = state.monitors[0].usable_frame; |
| 3383 | + |
| 3384 | + { |
| 3385 | + let ws = state.workspaces.get_mut(0); |
| 3386 | + ws.tree.insert_with_rect(70, None, screen); |
| 3387 | + ws.tree.insert_with_rect(71, Some(70), screen); |
| 3388 | + assert!(ws.tree.make_stack_for_window(71)); |
| 3389 | + ws.tree.set_stack_active(70); |
| 3390 | + ws.record_focus(70); |
| 3391 | + } |
| 3392 | + |
| 3393 | + let geoms = state.workspace_render_geometries(0, screen); |
| 3394 | + let inactive = geoms.iter().find(|(wid, _)| *wid == 71).unwrap().1; |
| 3395 | + let hover_x = inactive.x + inactive.width - 1.0; |
| 3396 | + let hover_y = inactive.y + 1.0; |
| 3397 | + |
| 3398 | + state.mouse_moved(hover_x, hover_y); |
| 3399 | + |
| 3400 | + assert_eq!(state.active_workspace().focused, Some(70)); |
| 3401 | + assert_eq!( |
| 3402 | + state.active_workspace().tree.stack_info(70), |
| 3403 | + Some((vec![70, 71], 0)) |
| 3404 | + ); |
| 3405 | + } |
| 3406 | + |
| 3369 | 3407 | #[test] |
| 3370 | 3408 | fn external_focus_reveals_hidden_workspace_and_tracks_target_window() { |
| 3371 | 3409 | let mut state = WmState::new(); |