@@ -61,9 +61,6 @@ impl Rect { |
| 61 | 61 | } |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | | -const STACK_REVEAL_OFFSET_X: f64 = 4.0; |
| 65 | | -const STACK_REVEAL_OFFSET_Y: f64 = 4.0; |
| 66 | | - |
| 67 | 64 | #[derive(Debug, Clone, PartialEq)] |
| 68 | 65 | pub enum Node { |
| 69 | 66 | Internal { |
@@ -89,12 +86,6 @@ impl Default for Node { |
| 89 | 86 | } |
| 90 | 87 | |
| 91 | 88 | impl Node { |
| 92 | | - fn stack_reveal_direction(windows_len: usize, active: usize) -> f64 { |
| 93 | | - let left_count = active; |
| 94 | | - let right_count = windows_len.saturating_sub(active + 1); |
| 95 | | - if left_count > right_count { -1.0 } else { 1.0 } |
| 96 | | - } |
| 97 | | - |
| 98 | 89 | pub fn empty() -> Self { |
| 99 | 90 | Node::Leaf { window: None } |
| 100 | 91 | } |
@@ -317,35 +308,17 @@ impl Node { |
| 317 | 308 | match self { |
| 318 | 309 | Node::Leaf { window: Some(w) } => vec![(*w, padded)], |
| 319 | 310 | Node::Leaf { window: None } => vec![], |
| 320 | | - Node::Stack { |
| 321 | | - windows, active, .. |
| 322 | | - } => { |
| 323 | | - let active_window = windows.get(*active).copied(); |
| 324 | | - let x_direction = Self::stack_reveal_direction(windows.len(), *active); |
| 325 | | - let mut geoms = Vec::with_capacity(windows.len()); |
| 326 | | - let mut depth = 0usize; |
| 327 | | - for (idx, wid) in windows.iter().enumerate() { |
| 328 | | - if Some(*wid) == active_window { |
| 329 | | - continue; |
| 330 | | - } |
| 331 | | - depth += 1; |
| 332 | | - geoms.push(( |
| 333 | | - *wid, |
| 334 | | - Rect::new( |
| 335 | | - padded.x + x_direction * STACK_REVEAL_OFFSET_X * depth as f64, |
| 336 | | - padded.y + STACK_REVEAL_OFFSET_Y * depth as f64, |
| 337 | | - padded.width, |
| 338 | | - padded.height, |
| 339 | | - ), |
| 340 | | - )); |
| 341 | | - if idx == *active { |
| 342 | | - depth = depth.saturating_sub(1); |
| 343 | | - } |
| 344 | | - } |
| 345 | | - if let Some(wid) = active_window { |
| 346 | | - geoms.push((wid, padded)); |
| 347 | | - } |
| 348 | | - geoms |
| 311 | + Node::Stack { windows, .. } => { |
| 312 | + // All stack members share the tile's full rect. Non-active |
| 313 | + // windows sit fully behind the active in z-order. The |
| 314 | + // previous "Slack-style reveal" offset (origin += depth * |
| 315 | + // STACK_REVEAL_OFFSET) leaked: the depth counter never |
| 316 | + // reset past the active member, so windows iterated after |
| 317 | + // the active in insertion order accumulated progressively |
| 318 | + // larger offsets and visibly drifted away from the tile |
| 319 | + // (user-reported as "the windows are not sized and |
| 320 | + // positioned as if they are members of the stack"). |
| 321 | + windows.iter().map(|wid| (*wid, padded)).collect() |
| 349 | 322 | } |
| 350 | 323 | Node::Internal { |
| 351 | 324 | split, |
@@ -840,6 +813,35 @@ impl Node { |
| 840 | 813 | self.make_stack_for_window_impl(window) |
| 841 | 814 | } |
| 842 | 815 | |
| 816 | + /// Replace the entire tree with a single root-level Stack containing |
| 817 | + /// every window currently in the tree. Used as overflow remediation |
| 818 | + /// during initial discovery: if even one window can't fit its tile in |
| 819 | + /// the BSP layout we'd otherwise produce, just stack everything into |
| 820 | + /// one tile so the user gets a clean, navigable layout instead of a |
| 821 | + /// staircase with a partial stack at the bottom. |
| 822 | + pub fn collapse_to_root_stack(&mut self, active_window: WindowId) -> bool { |
| 823 | + match self { |
| 824 | + Node::Internal { .. } => { |
| 825 | + let previous = self.clone(); |
| 826 | + let windows = self.windows(); |
| 827 | + if windows.is_empty() { |
| 828 | + return false; |
| 829 | + } |
| 830 | + let active = windows |
| 831 | + .iter() |
| 832 | + .position(|wid| *wid == active_window) |
| 833 | + .unwrap_or(0); |
| 834 | + *self = Node::Stack { |
| 835 | + windows, |
| 836 | + active, |
| 837 | + previous: Box::new(previous), |
| 838 | + }; |
| 839 | + true |
| 840 | + } |
| 841 | + _ => false, |
| 842 | + } |
| 843 | + } |
| 844 | + |
| 843 | 845 | fn make_stack_for_window_impl(&mut self, window: WindowId) -> bool { |
| 844 | 846 | match self { |
| 845 | 847 | Node::Internal { left, right, .. } => { |
@@ -1488,37 +1490,27 @@ mod tests { |
| 1488 | 1490 | } |
| 1489 | 1491 | |
| 1490 | 1492 | #[test] |
| 1491 | | - fn stacked_render_geometries_reveal_background_windows_to_the_right_when_right_biased() { |
| 1492 | | - let tree = Node::Stack { |
| 1493 | | - windows: vec![1, 2, 3], |
| 1494 | | - active: 0, |
| 1495 | | - previous: Box::new(Node::Leaf { window: Some(1) }), |
| 1496 | | - }; |
| 1497 | | - let geoms = tree.calculate_geometries(SCREEN); |
| 1498 | | - let g1 = geoms.iter().find(|(wid, _)| *wid == 1).unwrap().1; |
| 1499 | | - let g2 = geoms.iter().find(|(wid, _)| *wid == 2).unwrap().1; |
| 1500 | | - |
| 1501 | | - assert!(g2.x > g1.x); |
| 1502 | | - assert!(g2.y > g1.y); |
| 1503 | | - assert_eq!(g2.width, g1.width); |
| 1504 | | - assert_eq!(g2.height, g1.height); |
| 1505 | | - } |
| 1506 | | - |
| 1507 | | - #[test] |
| 1508 | | - fn stacked_render_geometries_reveal_background_windows_to_the_left_when_left_biased() { |
| 1509 | | - let tree = Node::Stack { |
| 1510 | | - windows: vec![1, 2, 3], |
| 1511 | | - active: 2, |
| 1512 | | - previous: Box::new(Node::Leaf { window: Some(3) }), |
| 1513 | | - }; |
| 1514 | | - let geoms = tree.calculate_geometries(SCREEN); |
| 1515 | | - let g2 = geoms.iter().find(|(wid, _)| *wid == 2).unwrap().1; |
| 1516 | | - let g3 = geoms.iter().find(|(wid, _)| *wid == 3).unwrap().1; |
| 1517 | | - |
| 1518 | | - assert!(g2.x < g3.x); |
| 1519 | | - assert!(g2.y > g3.y); |
| 1520 | | - assert_eq!(g2.width, g3.width); |
| 1521 | | - assert_eq!(g2.height, g3.height); |
| 1493 | + fn stacked_render_geometries_share_the_tile_rect() { |
| 1494 | + // All stack members occupy the full tile rect, regardless of |
| 1495 | + // which is active. Non-active windows sit fully behind the |
| 1496 | + // active in z-order — no Slack-style peek offset (removed: |
| 1497 | + // its depth counter accumulated past the active position |
| 1498 | + // and produced visibly drifting non-active windows). |
| 1499 | + for active in 0..3 { |
| 1500 | + let tree = Node::Stack { |
| 1501 | + windows: vec![1, 2, 3], |
| 1502 | + active, |
| 1503 | + previous: Box::new(Node::Leaf { window: Some(1) }), |
| 1504 | + }; |
| 1505 | + let geoms = tree.calculate_geometries(SCREEN); |
| 1506 | + assert_eq!(geoms.len(), 3); |
| 1507 | + for (_, rect) in &geoms { |
| 1508 | + assert_eq!(rect.x, SCREEN.x); |
| 1509 | + assert_eq!(rect.y, SCREEN.y); |
| 1510 | + assert_eq!(rect.width, SCREEN.width); |
| 1511 | + assert_eq!(rect.height, SCREEN.height); |
| 1512 | + } |
| 1513 | + } |
| 1522 | 1514 | } |
| 1523 | 1515 | |
| 1524 | 1516 | #[test] |