diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-12 02:13:29 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-12 02:13:29 +0300 |
| commit | 6e7ce87bab6a5d4f277613658d0f19afb114128b (patch) | |
| tree | abd3b3c95a7173694f076b77f6febf0516fd8093 /src/layout.rs | |
| parent | 6d763e58454e86cca953b151af1c3be303507cb4 (diff) | |
fix: allow resize keys to shrink panes
refs #562
Diffstat (limited to 'src/layout.rs')
| -rw-r--r-- | src/layout.rs | 136 |
1 files changed, 116 insertions, 20 deletions
diff --git a/src/layout.rs b/src/layout.rs index 220fd8b7..bd7828c6 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -197,12 +197,9 @@ impl TileLayout { }; let grows = matches!(nav, NavDirection::Right | NavDirection::Down); - let best = splits - .iter() - .filter(|s| s.direction == target_dir) - .filter(|s| split_area_overlaps_focused_pane(s, focused_rect, nav)) - .filter(|s| split_on_requested_edge(s, focused_rect, nav)) - .min_by_key(|s| split_edge_distance(s, focused_rect, nav)); + let best = nearest_resize_split(&splits, target_dir, focused_rect, nav).or_else(|| { + nearest_resize_split(&splits, target_dir, focused_rect, opposite_direction(nav)) + }); if let Some(split) = best { let path = split.path.clone(); @@ -327,6 +324,29 @@ fn split_area_overlaps_focused_pane(split: &SplitBorder, focused: Rect, nav: Nav } } +fn nearest_resize_split( + splits: &[SplitBorder], + target_dir: Direction, + focused: Rect, + nav: NavDirection, +) -> Option<&SplitBorder> { + splits + .iter() + .filter(|s| s.direction == target_dir) + .filter(|s| split_area_overlaps_focused_pane(s, focused, nav)) + .filter(|s| split_on_requested_edge(s, focused, nav)) + .min_by_key(|s| split_edge_distance(s, focused, nav)) +} + +fn opposite_direction(nav: NavDirection) -> NavDirection { + match nav { + NavDirection::Left => NavDirection::Right, + NavDirection::Right => NavDirection::Left, + NavDirection::Up => NavDirection::Down, + NavDirection::Down => NavDirection::Up, + } +} + fn split_edge_distance(split: &SplitBorder, focused: Rect, nav: NavDirection) -> u32 { match nav { NavDirection::Left => (split.pos as i32 - focused.x as i32).unsigned_abs(), @@ -624,6 +644,13 @@ mod tests { .collect() } + fn pane_rect(layout: &TileLayout, pane_id: PaneId) -> Rect { + pane_rects(layout) + .into_iter() + .find_map(|(id, rect)| (id == pane_id).then_some(rect)) + .expect("pane should exist") + } + fn split_snapshot(layout: &TileLayout) -> Vec<(Direction, f32)> { fn collect(node: &Node, out: &mut Vec<(Direction, f32)>) { match node { @@ -722,27 +749,96 @@ mod tests { } #[test] - fn resize_outer_edges_are_noops() { + fn resize_outer_edges_shrink_focused_pane() { let (mut horizontal, left) = TileLayout::new(); + horizontal.split_focused(Direction::Horizontal); + + assert!(horizontal.resize_pane(left, NavDirection::Left, 0.05, Rect::new(0, 0, 100, 40),)); + let split = split_snapshot(&horizontal)[0]; + assert_eq!(split.0, Direction::Horizontal); + assert!((split.1 - 0.45).abs() < f32::EPSILON); + + let (mut horizontal, _left) = TileLayout::new(); let right = horizontal.split_focused(Direction::Horizontal); - let horizontal_before = split_snapshot(&horizontal); - assert!(!horizontal.resize_pane(left, NavDirection::Left, 0.05, Rect::new(0, 0, 100, 40),)); - assert!(!horizontal.resize_pane( - right, - NavDirection::Right, - 0.05, - Rect::new(0, 0, 100, 40), - )); - assert_eq!(split_snapshot(&horizontal), horizontal_before); + assert!(horizontal.resize_pane(right, NavDirection::Right, 0.05, Rect::new(0, 0, 100, 40),)); + let split = split_snapshot(&horizontal)[0]; + assert_eq!(split.0, Direction::Horizontal); + assert!((split.1 - 0.55).abs() < f32::EPSILON); let (mut vertical, top) = TileLayout::new(); + vertical.split_focused(Direction::Vertical); + + assert!(vertical.resize_pane(top, NavDirection::Up, 0.05, Rect::new(0, 0, 100, 40),)); + let split = split_snapshot(&vertical)[0]; + assert_eq!(split.0, Direction::Vertical); + assert!((split.1 - 0.45).abs() < f32::EPSILON); + + let (mut vertical, _top) = TileLayout::new(); let bottom = vertical.split_focused(Direction::Vertical); - let vertical_before = split_snapshot(&vertical); - assert!(!vertical.resize_pane(top, NavDirection::Up, 0.05, Rect::new(0, 0, 100, 40),)); - assert!(!vertical.resize_pane(bottom, NavDirection::Down, 0.05, Rect::new(0, 0, 100, 40),)); - assert_eq!(split_snapshot(&vertical), vertical_before); + assert!(vertical.resize_pane(bottom, NavDirection::Down, 0.05, Rect::new(0, 0, 100, 40),)); + let split = split_snapshot(&vertical)[0]; + assert_eq!(split.0, Direction::Vertical); + assert!((split.1 - 0.55).abs() < f32::EPSILON); + } + + #[test] + fn resize_outer_edge_falls_back_to_horizontal_ancestor_split() { + let mut layout = TileLayout::from_saved( + Node::Split { + direction: Direction::Horizontal, + ratio: 0.6, + first: Box::new(Node::Split { + direction: Direction::Vertical, + ratio: 0.5, + first: Box::new(Node::Pane(pane(1))), + second: Box::new(Node::Pane(pane(2))), + }), + second: Box::new(Node::Pane(pane(3))), + }, + pane(1), + ); + let before = pane_rect(&layout, pane(1)); + + assert!(layout.resize_pane(pane(1), NavDirection::Left, 0.05, Rect::new(0, 0, 100, 40),)); + + let after = pane_rect(&layout, pane(1)); + assert_eq!(after.height, before.height); + assert!(after.width < before.width); + let splits = split_snapshot(&layout); + assert_eq!(splits[0].0, Direction::Horizontal); + assert!((splits[0].1 - 0.55).abs() < f32::EPSILON); + assert_eq!(splits[1], (Direction::Vertical, 0.5)); + } + + #[test] + fn resize_outer_edge_falls_back_to_vertical_ancestor_split() { + let mut layout = TileLayout::from_saved( + Node::Split { + direction: Direction::Vertical, + ratio: 0.6, + first: Box::new(Node::Split { + direction: Direction::Horizontal, + ratio: 0.5, + first: Box::new(Node::Pane(pane(1))), + second: Box::new(Node::Pane(pane(2))), + }), + second: Box::new(Node::Pane(pane(3))), + }, + pane(1), + ); + let before = pane_rect(&layout, pane(1)); + + assert!(layout.resize_pane(pane(1), NavDirection::Up, 0.05, Rect::new(0, 0, 100, 40),)); + + let after = pane_rect(&layout, pane(1)); + assert_eq!(after.width, before.width); + assert!(after.height < before.height); + let splits = split_snapshot(&layout); + assert_eq!(splits[0].0, Direction::Vertical); + assert!((splits[0].1 - 0.55).abs() < f32::EPSILON); + assert_eq!(splits[1], (Direction::Horizontal, 0.5)); } #[test] |
