aboutsummaryrefslogtreecommitdiff
path: root/src/layout.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-12 02:13:29 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-12 02:13:29 +0300
commit6e7ce87bab6a5d4f277613658d0f19afb114128b (patch)
treeabd3b3c95a7173694f076b77f6febf0516fd8093 /src/layout.rs
parent6d763e58454e86cca953b151af1c3be303507cb4 (diff)
fix: allow resize keys to shrink panes
refs #562
Diffstat (limited to 'src/layout.rs')
-rw-r--r--src/layout.rs136
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]