diff --git a/example/default.kdl b/example/default.kdl index 15f1fd54a5..2a4b3f9cdd 100644 --- a/example/default.kdl +++ b/example/default.kdl @@ -408,3 +408,8 @@ load_plugins { // Default: true (if the host terminal supports it) // // support_kitty_keyboard_protocol false + +// Whether to stack panes when resizing beyond a certain size +// Default: true +// +// stacked_resize false diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 9882781fcd..1c05f502fb 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -455,7 +455,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -493,7 +493,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -531,7 +531,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -572,7 +572,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -620,7 +620,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -658,7 +658,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -696,7 +696,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -735,7 +735,7 @@ impl RemoteRunner { y: 0, rows, cols, - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index a4daf6f9d6..ae90ba937e 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -363,6 +363,7 @@ impl SessionMetaData { auto_layout: new_config.options.auto_layout.unwrap_or(true), rounded_corners: new_config.ui.pane_frames.rounded_corners, hide_session_name: new_config.ui.pane_frames.hide_session_name, + stacked_resize: new_config.options.stacked_resize.unwrap_or(true), }) .unwrap(); self.senders diff --git a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs index a88d6e4239..51ef6b00da 100644 --- a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs +++ b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs @@ -856,7 +856,7 @@ pub fn half_size_middle_geom(space: &Viewport, offset: usize) -> PaneGeom { y: space.y + (space.rows as f64 / 4.0).round() as usize + offset, cols: Dimension::fixed(space.cols / 2), rows: Dimension::fixed(space.rows / 2), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -871,7 +871,7 @@ fn half_size_top_left_geom(space: &Viewport, offset: usize) -> PaneGeom { y: space.y + 2 + offset, cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -886,7 +886,7 @@ fn half_size_top_right_geom(space: &Viewport, offset: usize) -> PaneGeom { y: space.y + 2 + offset, cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -901,7 +901,7 @@ fn half_size_bottom_left_geom(space: &Viewport, offset: usize) -> PaneGeom { y: ((space.y + space.rows) - (space.rows / 3) - 2).saturating_sub(offset), cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; @@ -916,7 +916,7 @@ fn half_size_bottom_right_geom(space: &Viewport, offset: usize) -> PaneGeom { y: ((space.y + space.rows) - (space.rows / 3) - 2).saturating_sub(offset), cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }; diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 088d1b4f86..ae3bd40709 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -18,7 +18,7 @@ use crate::{ }; use stacked_panes::StackedPanes; use zellij_utils::{ - data::{Direction, ModeInfo, Palette, PaneInfo, ResizeStrategy, Style}, + data::{Direction, ModeInfo, Palette, PaneInfo, Resize, ResizeStrategy, Style}, errors::prelude::*, input::{ command::RunCommand, @@ -61,6 +61,7 @@ pub struct TiledPanes { connected_clients_in_app: Rc>>, mode_info: Rc>>, character_cell_size: Rc>>, + stacked_resize: Rc>, default_mode_info: ModeInfo, style: Style, session_is_mirrored: bool, @@ -71,6 +72,8 @@ pub struct TiledPanes { senders: ThreadSenders, window_title: Option, client_id_to_boundaries: HashMap, + tombstones_before_increase: Option<(PaneId, Vec>)>, + tombstones_before_decrease: Option<(PaneId, Vec>)>, } impl TiledPanes { @@ -82,6 +85,7 @@ impl TiledPanes { connected_clients_in_app: Rc>>, mode_info: Rc>>, character_cell_size: Rc>>, + stacked_resize: Rc>, session_is_mirrored: bool, draw_pane_frames: bool, default_mode_info: ModeInfo, @@ -97,6 +101,7 @@ impl TiledPanes { connected_clients_in_app, mode_info, character_cell_size, + stacked_resize, default_mode_info, style, session_is_mirrored, @@ -107,6 +112,8 @@ impl TiledPanes { senders, window_title: None, client_id_to_boundaries: HashMap::new(), + tombstones_before_increase: None, + tombstones_before_decrease: None, } } pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, mut pane: Box) { @@ -162,13 +169,23 @@ impl TiledPanes { self.reapply_pane_frames(); removed_pane } - pub fn insert_pane(&mut self, pane_id: PaneId, pane: Box) { + pub fn insert_pane( + &mut self, + pane_id: PaneId, + pane: Box, + client_id: Option, + ) { let should_relayout = true; - self.add_pane(pane_id, pane, should_relayout); + self.add_pane(pane_id, pane, should_relayout, client_id); } - pub fn insert_pane_without_relayout(&mut self, pane_id: PaneId, pane: Box) { + pub fn insert_pane_without_relayout( + &mut self, + pane_id: PaneId, + pane: Box, + client_id: Option, + ) { let should_relayout = false; - self.add_pane(pane_id, pane, should_relayout); + self.add_pane(pane_id, pane, should_relayout, client_id); } pub fn has_room_for_new_pane(&mut self) -> bool { let cursor_height_width_ratio = self.cursor_height_width_ratio(); @@ -200,15 +217,29 @@ impl TiledPanes { .copied() { if let Some(pane) = self.panes.remove(&pane_id) { - self.add_pane(pane.pid(), pane, true); + self.add_pane(pane.pid(), pane, true, None); } } } - fn add_pane(&mut self, pane_id: PaneId, mut pane: Box, should_relayout: bool) { + fn add_pane( + &mut self, + pane_id: PaneId, + mut pane: Box, + should_relayout: bool, + client_id: Option, + ) { if self.panes.is_empty() { self.panes.insert(pane_id, pane); return; } + let stacked_resize = { *self.stacked_resize.borrow() }; + + if let Some(client_id) = client_id { + if stacked_resize { + self.add_pane_with_stacked_resize(pane_id, pane, should_relayout, client_id); + return; + } + } let cursor_height_width_ratio = self.cursor_height_width_ratio(); let mut pane_grid = TiledPaneGrid::new( &mut self.panes, @@ -216,6 +247,8 @@ impl TiledPanes { *self.display_area.borrow(), *self.viewport.borrow(), ); + // TODO: make sure this is really the same as the pre-stacked-resizes function... and + // behaves the same and all let pane_id_and_split_direction = pane_grid.find_room_for_new_pane(cursor_height_width_ratio); match pane_id_and_split_direction { @@ -248,6 +281,76 @@ impl TiledPanes { }, } } + fn add_pane_with_stacked_resize( + &mut self, + pane_id: PaneId, + mut pane: Box, + should_relayout: bool, + client_id: ClientId, + ) { + let cursor_height_width_ratio = self.cursor_height_width_ratio(); + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + let Some(active_pane_id) = self.active_panes.get(&client_id) else { + log::error!("Could not find active pane id for client_id"); + return; + }; + if pane_grid + .get_pane_geom(active_pane_id) + .map(|p| p.is_stacked()) + .unwrap_or(false) + { + // try to add the pane to the stack of the active pane + match pane_grid.make_room_in_stack_of_pane_id_for_pane(active_pane_id) { + Ok(new_pane_geom) => { + pane.set_geom(new_pane_geom); + self.panes.insert(pane_id, pane); + self.set_force_render(); // TODO: why do we need this? + return; + }, + Err(e) => { + log::error!("Failed to add pane to stack: {:?}", e); + }, + } + } + let pane_id_and_split_direction = + pane_grid.split_pane(active_pane_id, cursor_height_width_ratio); + match pane_id_and_split_direction { + Some((pane_id_to_split, split_direction)) => { + // this unwrap is safe because floating panes should not be visible if there are no floating panes + let pane_to_split = self.panes.get_mut(&pane_id_to_split).unwrap(); + let size_of_both_panes = pane_to_split.position_and_size(); + if let Some((first_geom, second_geom)) = split(split_direction, &size_of_both_panes) + { + pane_to_split.set_geom(first_geom); + pane.set_geom(second_geom); + self.panes.insert(pane_id, pane); + if should_relayout { + self.relayout(!split_direction); + } + } + }, + None => { + // we couldn't add the pane normally, let's see if there's room in one of the + // stacks... + let _ = pane_grid.make_pane_stacked(active_pane_id); + match pane_grid.make_room_in_stack_of_pane_id_for_pane(active_pane_id) { + Ok(new_pane_geom) => { + pane.set_geom(new_pane_geom); + self.panes.insert(pane_id, pane); // TODO: is set_geom the right one? + return; + }, + Err(e) => { + log::error!("Failed to add pane to stack: {:?}", e); + }, + } + }, + } + } pub fn fixed_pane_geoms(&self) -> Vec { self.panes .values() @@ -349,7 +452,7 @@ impl TiledPanes { let position_and_size = pane.current_geom(); let (pane_columns_offset, pane_rows_offset) = pane_content_offset(&position_and_size, &viewport); - if !draw_pane_frames && pane.current_geom().is_stacked { + if !draw_pane_frames && pane.current_geom().is_stacked() { // stacked panes should always leave 1 top row for a title pane.set_content_offset(Offset::shift_right_and_top(pane_columns_offset, 1)); } else { @@ -364,10 +467,20 @@ impl TiledPanes { pub fn can_split_pane_horizontally(&mut self, client_id: ClientId) -> bool { if let Some(active_pane_id) = &self.active_panes.get(&client_id) { if let Some(active_pane) = self.panes.get_mut(active_pane_id) { - let full_pane_size = active_pane.position_and_size(); - if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 - || full_pane_size.is_stacked - { + let mut full_pane_size = active_pane.position_and_size(); + + if full_pane_size.is_stacked() { + let Some(position_and_size_of_stack) = + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .position_and_size_of_stack(&active_pane_id) + else { + log::error!("Failed to find position and size of stack"); + return false; + }; + full_pane_size = position_and_size_of_stack; + } + + if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 { return false; } else { return split(SplitDirection::Horizontal, &full_pane_size).is_some(); @@ -379,10 +492,20 @@ impl TiledPanes { pub fn can_split_pane_vertically(&mut self, client_id: ClientId) -> bool { if let Some(active_pane_id) = &self.active_panes.get(&client_id) { if let Some(active_pane) = self.panes.get_mut(active_pane_id) { - let full_pane_size = active_pane.position_and_size(); - if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 - || full_pane_size.is_stacked - { + let mut full_pane_size = active_pane.position_and_size(); + + if full_pane_size.is_stacked() { + let Some(position_and_size_of_stack) = + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .position_and_size_of_stack(&active_pane_id) + else { + log::error!("Failed to find position and size of stack"); + return false; + }; + full_pane_size = position_and_size_of_stack; + } + + if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 { return false; } return split(SplitDirection::Vertical, &full_pane_size).is_some(); @@ -390,19 +513,6 @@ impl TiledPanes { } false } - pub fn can_split_active_pane_horizontally(&self, client_id: ClientId) -> bool { - let active_pane_id = &self.active_panes.get(&client_id).unwrap(); - let active_pane = self.panes.get(active_pane_id).unwrap(); - let full_pane_size = active_pane.position_and_size(); - if full_pane_size.rows.is_fixed() || full_pane_size.is_stacked { - return false; - } - if split(SplitDirection::Horizontal, &full_pane_size).is_some() { - true - } else { - false - } - } pub fn split_pane_horizontally( &mut self, pid: PaneId, @@ -410,30 +520,44 @@ impl TiledPanes { client_id: ClientId, ) { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); + let mut full_pane_size = self + .panes + .get(active_pane_id) + .map(|p| p.position_and_size()) + .unwrap(); + if full_pane_size.is_stacked() { + match StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .position_and_size_of_stack(&active_pane_id) + { + Some(position_and_size_of_stack) => { + full_pane_size = position_and_size_of_stack; + }, + None => { + log::error!("Failed to find position and size of stack"); + }, + } + } let active_pane = self.panes.get_mut(active_pane_id).unwrap(); - let full_pane_size = active_pane.position_and_size(); if let Some((top_winsize, bottom_winsize)) = split(SplitDirection::Horizontal, &full_pane_size) { - active_pane.set_geom(top_winsize); + if active_pane.position_and_size().is_stacked() { + match StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .resize_panes_in_stack(&active_pane_id, top_winsize) + { + Ok(_) => {}, + Err(e) => { + log::error!("Failed to resize stack: {}", e); + }, + } + } else { + active_pane.set_geom(top_winsize); + } new_pane.set_geom(bottom_winsize); self.panes.insert(pid, new_pane); self.relayout(SplitDirection::Vertical); } } - pub fn can_split_active_pane_vertically(&self, client_id: ClientId) -> bool { - let active_pane_id = &self.active_panes.get(&client_id).unwrap(); - let active_pane = self.panes.get(active_pane_id).unwrap(); - let full_pane_size = active_pane.position_and_size(); - if full_pane_size.cols.is_fixed() || full_pane_size.is_stacked { - return false; - } - if split(SplitDirection::Vertical, &full_pane_size).is_some() { - true - } else { - false - } - } pub fn split_pane_vertically( &mut self, pid: PaneId, @@ -441,12 +565,39 @@ impl TiledPanes { client_id: ClientId, ) { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); + let mut full_pane_size = self + .panes + .get(active_pane_id) + .map(|p| p.position_and_size()) + .unwrap(); + if full_pane_size.is_stacked() { + match StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .position_and_size_of_stack(&active_pane_id) + { + Some(position_and_size_of_stack) => { + full_pane_size = position_and_size_of_stack; + }, + None => { + log::error!("Failed to find position and size of stack"); + }, + } + } let active_pane = self.panes.get_mut(active_pane_id).unwrap(); - let full_pane_size = active_pane.position_and_size(); if let Some((left_winsize, right_winsize)) = split(SplitDirection::Vertical, &full_pane_size) { - active_pane.set_geom(left_winsize); + if active_pane.position_and_size().is_stacked() { + match StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .resize_panes_in_stack(&active_pane_id, left_winsize) + { + Ok(_) => {}, + Err(e) => { + log::error!("Failed to resize stack: {}", e); + }, + } + } else { + active_pane.set_geom(left_winsize); + } new_pane.set_geom(right_winsize); self.panes.insert(pid, new_pane); self.relayout(SplitDirection::Horizontal); @@ -459,7 +610,7 @@ impl TiledPanes { if self .panes .get(&pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) @@ -481,7 +632,7 @@ impl TiledPanes { if self .panes .get(&pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = @@ -498,7 +649,7 @@ impl TiledPanes { if self .panes .get(&pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = StackedPanes::new_from_btreemap( @@ -562,7 +713,7 @@ impl TiledPanes { if self .panes .get(&pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) @@ -694,7 +845,7 @@ impl TiledPanes { let pane_is_stacked_over = stacked_pane_ids_over_flexible_pane.contains(&pane.pid()); let should_draw_pane_frames = self.draw_pane_frames; - let pane_is_stacked = pane.current_geom().is_stacked; + let pane_is_stacked = pane.current_geom().is_stacked(); let mut pane_contents_and_ui = PaneContentsAndUi::new( pane, output, @@ -828,9 +979,16 @@ impl TiledPanes { }, } } + fn display_area_changed(&self, new_screen_size: Size) -> bool { + let display_area = self.display_area.borrow(); + new_screen_size.rows != display_area.rows || new_screen_size.cols != display_area.cols + } pub fn resize(&mut self, new_screen_size: Size) { // this is blocked out to appease the borrow checker { + if self.display_area_changed(new_screen_size) { + self.clear_tombstones(); + } let mut display_area = self.display_area.borrow_mut(); let mut viewport = self.viewport.borrow_mut(); let Size { rows, cols } = new_screen_size; @@ -884,13 +1042,388 @@ impl TiledPanes { client_id: ClientId, strategy: &ResizeStrategy, ) -> Result<()> { - if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - self.resize_pane_with_id(*strategy, active_pane_id)?; + if *self.stacked_resize.borrow() && strategy.direction.is_none() { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + self.stacked_resize_pane_with_id(active_pane_id, strategy)?; + self.reapply_pane_frames(); + } + } else { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + self.resize_pane_with_id(*strategy, active_pane_id, None)?; + } } Ok(()) } - pub fn resize_pane_with_id(&mut self, strategy: ResizeStrategy, pane_id: PaneId) -> Result<()> { + fn resize_or_stack_pane_up(&mut self, pane_id: PaneId, resize_percent: (f64, f64)) -> bool { + // true - successfully resized + let mut strategy = ResizeStrategy::new(Resize::Increase, Some(Direction::Up)); + strategy.invert_on_boundaries = false; + let successfully_resized_up = + match self.resize_pane_with_id(strategy, pane_id, Some(resize_percent)) { + Ok(_) => true, + Err(_) => false, + }; + if successfully_resized_up { + return true; + } else { + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if let Some(pane_ids_to_resize) = pane_grid.stack_pane_up(&pane_id) { + for pane_id in pane_ids_to_resize { + if let Some(pane) = self.panes.get_mut(&pane_id) { + let _ = + resize_pty!(pane, self.os_api, self.senders, self.character_cell_size); + } + } + return true; + } + } + false + } + fn resize_or_stack_pane_down(&mut self, pane_id: PaneId, resize_percent: (f64, f64)) -> bool { + // true - successfully resized + let mut strategy = ResizeStrategy::new(Resize::Increase, Some(Direction::Down)); + strategy.invert_on_boundaries = false; + let successfully_resized_up = + match self.resize_pane_with_id(strategy, pane_id, Some(resize_percent)) { + Ok(_) => true, + Err(_) => false, + }; + if successfully_resized_up { + return true; + } else { + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if let Some(pane_ids_to_resize) = pane_grid.stack_pane_down(&pane_id) { + for pane_id in pane_ids_to_resize { + if let Some(pane) = self.panes.get_mut(&pane_id) { + let _ = + resize_pty!(pane, self.os_api, self.senders, self.character_cell_size); + } + } + return true; + } + } + false + } + fn resize_or_stack_pane_left(&mut self, pane_id: PaneId, resize_percent: (f64, f64)) -> bool { + // true - successfully resized + let mut strategy = ResizeStrategy::new(Resize::Increase, Some(Direction::Left)); + strategy.invert_on_boundaries = false; + let successfully_resized_up = + match self.resize_pane_with_id(strategy, pane_id, Some(resize_percent)) { + Ok(_) => true, + Err(_) => false, + }; + if successfully_resized_up { + return true; + } else { + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if let Some(pane_ids_to_resize) = pane_grid.stack_pane_left(&pane_id) { + for pane_id in pane_ids_to_resize { + if let Some(pane) = self.panes.get_mut(&pane_id) { + let _ = + resize_pty!(pane, self.os_api, self.senders, self.character_cell_size); + } + } + return true; + } + } + false + } + fn resize_or_stack_pane_right(&mut self, pane_id: PaneId, resize_percent: (f64, f64)) -> bool { + // true - successfully resized + let mut strategy = ResizeStrategy::new(Resize::Increase, Some(Direction::Right)); + strategy.invert_on_boundaries = false; + let successfully_resized_up = + match self.resize_pane_with_id(strategy, pane_id, Some(resize_percent)) { + Ok(_) => true, + Err(_) => false, + }; + if successfully_resized_up { + return true; + } else { + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if let Some(pane_ids_to_resize) = pane_grid.stack_pane_right(&pane_id) { + for pane_id in pane_ids_to_resize { + if let Some(pane) = self.panes.get_mut(&pane_id) { + let _ = + resize_pty!(pane, self.os_api, self.senders, self.character_cell_size); + } + } + return true; + } + } + false + } + fn update_tombstones_before_increase( + &mut self, + focused_pane_id: PaneId, + pane_state: HashMap, + ) { + match self.tombstones_before_increase.as_mut() { + Some((focused_pane_id_in_tombstone, pane_geoms)) => { + let last_state = pane_geoms.last(); + let last_state_has_same_pane_count = last_state + .map(|last_state| last_state.len() == pane_state.len()) + .unwrap_or(false); + let last_state_equals_current_state = last_state + .map(|last_state| last_state == &pane_state) + .unwrap_or(false); + if last_state_equals_current_state { + return; + } + if *focused_pane_id_in_tombstone == focused_pane_id + && last_state_has_same_pane_count + { + pane_geoms.push(pane_state); + } else { + self.clear_tombstones(); + self.tombstones_before_increase = Some((focused_pane_id, vec![pane_state])); + } + }, + None => { + self.clear_tombstones(); + self.tombstones_before_increase = Some((focused_pane_id, vec![pane_state])); + }, + } + } + fn update_tombstones_before_decrease( + &mut self, + focused_pane_id: PaneId, + pane_state: HashMap, + ) { + match self.tombstones_before_decrease.as_mut() { + Some((focused_pane_id_in_tombstone, pane_geoms)) => { + let last_state = pane_geoms.last(); + let last_state_has_same_pane_count = last_state + .map(|last_state| last_state.len() == pane_state.len()) + .unwrap_or(false); + let last_state_equals_current_state = last_state + .map(|last_state| last_state == &pane_state) + .unwrap_or(false); + if last_state_equals_current_state { + return; + } + if *focused_pane_id_in_tombstone == focused_pane_id + && last_state_has_same_pane_count + { + pane_geoms.push(pane_state); + } else { + self.clear_tombstones(); + self.tombstones_before_decrease = Some((focused_pane_id, vec![pane_state])); + } + }, + None => { + self.clear_tombstones(); + self.tombstones_before_decrease = Some((focused_pane_id, vec![pane_state])); + }, + } + } + fn clear_tombstones(&mut self) { + self.tombstones_before_increase = None; + self.tombstones_before_decrease = None; + } + fn stacked_resize_pane_with_id( + &mut self, + pane_id: PaneId, + strategy: &ResizeStrategy, + ) -> Result { + let resize_percent = (30.0, 30.0); + match strategy.resize { + Resize::Increase => { + match self.tombstones_before_decrease.as_mut() { + Some((tombstone_pane_id, tombstone_pane_state)) + if *tombstone_pane_id == pane_id => + { + if let Some(last_state) = tombstone_pane_state.pop() { + if last_state.len() == self.panes.len() { + for (pane_id, pane_geom) in last_state { + self.panes + .get_mut(&pane_id) + .map(|pane| pane.set_geom(pane_geom)); + } + self.reapply_pane_frames(); + return Ok(true); + } else { + self.tombstones_before_decrease = None; + } + } + }, + _ => {}, + } + + let mut current_pane_state = HashMap::new(); + for (pane_id, pane) in &self.panes { + current_pane_state.insert(*pane_id, pane.current_geom()); + } + + let ( + direct_neighboring_pane_ids_above, + direct_neighboring_pane_ids_below, + direct_neighboring_pane_ids_to_the_left, + direct_neighboring_pane_ids_to_the_right, + ) = { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + ( + pane_grid.direct_neighboring_pane_ids_above(&pane_id), + pane_grid.direct_neighboring_pane_ids_below(&pane_id), + pane_grid.direct_neighboring_pane_ids_to_the_left(&pane_id), + pane_grid.direct_neighboring_pane_ids_to_the_right(&pane_id), + ) + }; + if !direct_neighboring_pane_ids_above.is_empty() { + if self.resize_or_stack_pane_up(pane_id, resize_percent) { + self.update_tombstones_before_increase(pane_id, current_pane_state); + return Ok(true); + } + } + if !direct_neighboring_pane_ids_below.is_empty() { + if self.resize_or_stack_pane_down(pane_id, resize_percent) { + self.update_tombstones_before_increase(pane_id, current_pane_state); + return Ok(true); + } + } + if !direct_neighboring_pane_ids_to_the_left.is_empty() { + if self.resize_or_stack_pane_left(pane_id, resize_percent) { + self.update_tombstones_before_increase(pane_id, current_pane_state); + return Ok(true); + } + } + if !direct_neighboring_pane_ids_to_the_right.is_empty() { + if self.resize_or_stack_pane_right(pane_id, resize_percent) { + self.update_tombstones_before_increase(pane_id, current_pane_state); + return Ok(true); + } + } + // normal resize if we can't do anything... + match self.resize_pane_with_id(*strategy, pane_id, None) { + Ok(size_changed) => { + if size_changed { + self.update_tombstones_before_increase(pane_id, current_pane_state); + } else { + if self.fullscreen_is_active.is_none() { + self.toggle_pane_fullscreen(pane_id); + return Ok(self.fullscreen_is_active.is_some()); + } else { + return Ok(false); + } + } + return Ok(size_changed); + }, + Err(e) => Err(e), + } + }, + Resize::Decrease => { + if self.fullscreen_is_active.is_some() { + self.unset_fullscreen(); + return Ok(true); + } + + match self.tombstones_before_increase.as_mut() { + Some((tombstone_pane_id, tombstone_pane_state)) + if *tombstone_pane_id == pane_id => + { + if let Some(last_state) = tombstone_pane_state.pop() { + if last_state.len() == self.panes.len() { + for (pane_id, pane_geom) in last_state { + self.panes + .get_mut(&pane_id) + .map(|pane| pane.set_geom(pane_geom)); + } + self.reapply_pane_frames(); + return Ok(true); + } else { + self.tombstones_before_increase = None; + } + } + }, + _ => {}, + } + + let mut current_pane_state = HashMap::new(); + for (pane_id, pane) in &self.panes { + current_pane_state.insert(*pane_id, pane.current_geom()); + } + + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if let Some(pane_ids_to_resize) = pane_grid.unstack_pane_up(&pane_id) { + for pane_id in pane_ids_to_resize { + if let Some(pane) = self.panes.get_mut(&pane_id) { + resize_pty!(pane, self.os_api, self.senders, self.character_cell_size) + .unwrap(); + } + } + self.reapply_pane_frames(); + self.update_tombstones_before_decrease(pane_id, current_pane_state); + Ok(true) + } else { + // normal resize if we were not inside a stack + // first try with our custom resize_percent + match self.resize_pane_with_id(*strategy, pane_id, Some(resize_percent)) { + Ok(pane_size_changed) => { + if pane_size_changed { + self.update_tombstones_before_decrease(pane_id, current_pane_state); + self.reapply_pane_frames(); + Ok(pane_size_changed) + } else { + // if it doesn't work, try with the default resize percent + match self.resize_pane_with_id(*strategy, pane_id, None) { + Ok(pane_size_changed) => { + if pane_size_changed { + self.update_tombstones_before_decrease( + pane_id, + current_pane_state, + ); + self.reapply_pane_frames(); + } + Ok(pane_size_changed) + }, + Err(e) => Err(e), + } + } + }, + Err(e) => Err(e), + } + } + }, + } + } + pub fn resize_pane_with_id( + &mut self, + strategy: ResizeStrategy, + pane_id: PaneId, + resize_percent: Option<(f64, f64)>, + ) -> Result { let err_context = || format!("failed to resize pand with id: {:?}", pane_id); let mut pane_grid = TiledPaneGrid::new( @@ -900,11 +1433,18 @@ impl TiledPanes { *self.viewport.borrow(), ); + let mut pane_size_changed = false; match pane_grid - .change_pane_size(&pane_id, &strategy, (RESIZE_PERCENT, RESIZE_PERCENT)) + .change_pane_size( + &pane_id, + &strategy, + resize_percent.unwrap_or((RESIZE_PERCENT, RESIZE_PERCENT)), + ) .with_context(err_context) { - Ok(_) => {}, + Ok(changed) => { + pane_size_changed = changed; + }, Err(err) => match err.downcast_ref::() { Some(ZellijError::PaneSizeUnchanged) => { // try once more with double the resize percent, but let's keep it at that @@ -932,10 +1472,11 @@ impl TiledPanes { } for pane in self.panes.values_mut() { + // TODO: only for the panes whose width/height actually changed resize_pty!(pane, self.os_api, self.senders, self.character_cell_size).unwrap(); } self.reset_boundaries(); - Ok(()) + Ok(pane_size_changed) } pub fn focus_next_pane(&mut self, client_id: ClientId) { @@ -954,7 +1495,7 @@ impl TiledPanes { if self .panes .get(&next_active_pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) @@ -986,7 +1527,7 @@ impl TiledPanes { if self .panes .get(&next_active_pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) @@ -1103,7 +1644,7 @@ impl TiledPanes { .unwrap(); let previously_active_pane_is_stacked = - previously_active_pane.current_geom().is_stacked; + previously_active_pane.current_geom().is_stacked(); previously_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) @@ -1111,7 +1652,7 @@ impl TiledPanes { let next_active_pane = self.panes.get_mut(&p).unwrap(); let next_active_pane_is_stacked = - next_active_pane.current_geom().is_stacked; + next_active_pane.current_geom().is_stacked(); next_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) @@ -1157,7 +1698,7 @@ impl TiledPanes { .unwrap(); let previously_active_pane_is_stacked = - previously_active_pane.current_geom().is_stacked; + previously_active_pane.current_geom().is_stacked(); previously_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) @@ -1165,7 +1706,7 @@ impl TiledPanes { let next_active_pane = self.panes.get_mut(&p).unwrap(); let next_active_pane_is_stacked = - next_active_pane.current_geom().is_stacked; + next_active_pane.current_geom().is_stacked(); next_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) @@ -1296,7 +1837,7 @@ impl TiledPanes { if self .panes .get(&new_position_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) @@ -1577,7 +2118,7 @@ impl TiledPanes { if self .panes .get(&next_active_pane_id) - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false) { self.expand_pane_in_stack(next_active_pane_id); diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index 2a2b5b6f99..24d5df4fdb 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -138,7 +138,7 @@ impl<'a> PaneResizer<'a> { .get(&span.pid) .unwrap() .current_geom() - .is_stacked; + .is_stacked(); if pane_is_stacked { let current_geom = StackedPanes::new(self.panes.clone()) .position_and_size_of_stack(&span.pid) @@ -156,7 +156,6 @@ impl<'a> PaneResizer<'a> { }, }; StackedPanes::new(self.panes.clone()).resize_panes_in_stack(&span.pid, new_geom)?; - // TODO: test with geom_override (fullscreen) if new_geom.rows.as_usize() != current_geom.rows.as_usize() || new_geom.cols.as_usize() != current_geom.cols.as_usize() { @@ -248,10 +247,10 @@ impl<'a> PaneResizer<'a> { fn get_span(&self, direction: SplitDirection, pane: &dyn Pane) -> Option { let position_and_size = { let pas = pane.current_geom(); - if pas.is_stacked && pas.rows.is_percent() { + if pas.is_stacked() && pas.rows.is_percent() { // this is the main pane of the stack StackedPanes::new(self.panes.clone()).position_and_size_of_stack(&pane.pid()) - } else if pas.is_stacked { + } else if pas.is_stacked() { // this is a one-liner stacked pane and should be handled as the same rect with // the rest of the stack, represented by the main pane in the if branch above None diff --git a/zellij-server/src/panes/tiled_panes/stacked_panes.rs b/zellij-server/src/panes/tiled_panes/stacked_panes.rs index 400045b99c..748e4fc6ae 100644 --- a/zellij-server/src/panes/tiled_panes/stacked_panes.rs +++ b/zellij-server/src/panes/tiled_panes/stacked_panes.rs @@ -33,21 +33,21 @@ impl<'a> StackedPanes<'a> { destination_pane_id: &PaneId, ) -> Result<()> { let err_context = || format!("Failed to move stacked pane focus down"); - let source_pane_is_stacked = self + let source_pane_stack_id = self .panes .borrow() .get(source_pane_id) .with_context(err_context)? .position_and_size() - .is_stacked; - let destination_pane_is_stacked = self + .stacked; + let destination_pane_stack_id = self .panes .borrow() .get(destination_pane_id) .with_context(err_context)? .position_and_size() - .is_stacked; - if source_pane_is_stacked && destination_pane_is_stacked { + .stacked; + if source_pane_stack_id == destination_pane_stack_id { let mut panes = self.panes.borrow_mut(); let source_pane = panes.get_mut(source_pane_id).with_context(err_context)?; let mut source_pane_geom = source_pane.position_and_size(); @@ -59,7 +59,7 @@ impl<'a> StackedPanes<'a> { .get_mut(&destination_pane_id) .with_context(err_context)?; destination_pane.set_geom(destination_pane_geom); - } else if destination_pane_is_stacked { + } else if destination_pane_stack_id.is_some() { // we're moving down to the highest pane in the stack, we need to expand it and shrink the // expanded stack pane self.make_highest_pane_in_stack_flexible(destination_pane_id)?; @@ -68,21 +68,21 @@ impl<'a> StackedPanes<'a> { } pub fn move_up(&mut self, source_pane_id: &PaneId, destination_pane_id: &PaneId) -> Result<()> { let err_context = || format!("Failed to move stacked pane focus up"); - let source_pane_is_stacked = self + let source_pane_stack_id = self .panes .borrow() .get(source_pane_id) .with_context(err_context)? .position_and_size() - .is_stacked; - let destination_pane_is_stacked = self + .stacked; + let destination_pane_stack_id = self .panes .borrow() .get(destination_pane_id) .with_context(err_context)? .position_and_size() - .is_stacked; - if source_pane_is_stacked && destination_pane_is_stacked { + .stacked; + if source_pane_stack_id == destination_pane_stack_id { let mut panes = self.panes.borrow_mut(); let source_pane = panes.get_mut(source_pane_id).with_context(err_context)?; let mut source_pane_geom = source_pane.position_and_size(); @@ -95,7 +95,7 @@ impl<'a> StackedPanes<'a> { .get_mut(&destination_pane_id) .with_context(err_context)?; destination_pane.set_geom(destination_pane_geom); - } else if destination_pane_is_stacked { + } else if destination_pane_stack_id.is_some() { // we're moving up to the lowest pane in the stack, we need to expand it and shrink the // expanded stack pane self.make_lowest_pane_in_stack_flexible(destination_pane_id)?; @@ -188,7 +188,7 @@ impl<'a> StackedPanes<'a> { x: first_pane_in_stack.x, cols: first_pane_in_stack.cols, rows, - is_stacked: true, // important because otherwise the minimum stack size will not be + stacked: None, // important because otherwise the minimum stack size will not be // respected ..Default::default() }) @@ -265,11 +265,10 @@ impl<'a> StackedPanes<'a> { let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; let position_of_flexible_pane = self.position_of_flexible_pane(&all_stacked_pane_positions)?; - let (flexible_pane_id, flexible_pane) = all_stacked_pane_positions + let (_flexible_pane_id, flexible_pane) = all_stacked_pane_positions .iter() .nth(position_of_flexible_pane) .with_context(err_context)?; - let current_rows = all_stacked_pane_positions.len() + (flexible_pane.rows.as_usize() - 1); let new_rows = new_full_stack_geom.rows.as_usize(); let adjust_stack_geoms = |new_flexible_pane_geom: PaneGeom| -> Result<()> { @@ -295,35 +294,15 @@ impl<'a> StackedPanes<'a> { } Ok(()) }; - - if new_rows >= current_rows { - let extra_rows = new_rows - current_rows; - let mut new_flexible_pane_geom = *flexible_pane; - new_flexible_pane_geom - .rows - .set_inner(new_flexible_pane_geom.rows.as_usize() + extra_rows); - self.panes - .borrow_mut() - .get_mut(&flexible_pane_id) - .with_context(err_context)? - .set_geom(new_flexible_pane_geom); - adjust_stack_geoms(new_flexible_pane_geom)?; - } else { - if new_rows < all_stacked_pane_positions.len() { - return Err(anyhow!("Not enough room for stacked panes")); - } - let rows_deficit = current_rows - new_rows; - let mut new_flexible_pane_geom = *flexible_pane; - new_flexible_pane_geom - .rows - .set_inner(new_flexible_pane_geom.rows.as_usize() - rows_deficit); - self.panes - .borrow_mut() - .get_mut(&flexible_pane_id) - .with_context(err_context)? - .set_geom(new_flexible_pane_geom); - adjust_stack_geoms(new_flexible_pane_geom)?; - } + let new_rows_for_flexible_pane = + new_rows.saturating_sub(all_stacked_pane_positions.len()) + 1; + let mut new_flexible_pane_geom = new_full_stack_geom; + new_flexible_pane_geom.stacked = flexible_pane.stacked; + new_flexible_pane_geom.logical_position = flexible_pane.logical_position; + new_flexible_pane_geom + .rows + .set_inner(new_rows_for_flexible_pane); + adjust_stack_geoms(new_flexible_pane_geom)?; Ok(()) } fn pane_is_one_liner(&self, id: &PaneId) -> Result { @@ -337,9 +316,12 @@ impl<'a> StackedPanes<'a> { let err_context = || format!("Failed to find stacked panes"); let panes = self.panes.borrow(); let pane_in_stack = panes.get(id).with_context(err_context)?; + let stack_id = pane_in_stack.position_and_size().stacked; let mut all_stacked_pane_positions: Vec<(PaneId, PaneGeom)> = panes .iter() - .filter(|(_pid, p)| p.position_and_size().is_stacked) + .filter(|(_pid, p)| { + p.position_and_size().is_stacked() && p.position_and_size().stacked == stack_id + }) .filter(|(_pid, p)| { p.position_and_size().x == pane_in_stack.position_and_size().x && p.position_and_size().cols == pane_in_stack.position_and_size().cols @@ -402,7 +384,7 @@ impl<'a> StackedPanes<'a> { self.panes .borrow() .iter() - .filter(|(_p_id, p)| p.position_and_size().is_stacked) + .filter(|(_p_id, p)| p.position_and_size().is_stacked()) .map(|(p_id, _p)| *p_id) .collect() }; @@ -464,7 +446,39 @@ impl<'a> StackedPanes<'a> { } Err(anyhow!("Not enough room for another pane!")) } - pub fn new_stack(&mut self, root_pane_id: PaneId, pane_count_in_stack: usize) -> Vec { + pub fn make_room_for_new_pane_in_stack(&mut self, pane_id: &PaneId) -> Result { + let err_context = || format!("Failed to add pane to stack"); + + let stack = self.positions_in_stack(pane_id).with_context(err_context)?; + if let Some((id_of_flexible_pane_in_stack, _flexible_pane_in_stack)) = stack + .iter() + .find(|(_p_id, p)| !p.rows.is_fixed() && p.rows.as_usize() > 1) + { + self.make_lowest_pane_in_stack_flexible(id_of_flexible_pane_in_stack)?; + let all_stacked_pane_positions = + self.positions_in_stack(id_of_flexible_pane_in_stack)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + let (flexible_pane_id, mut flexible_pane_geom) = *all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + let mut position_for_new_pane = flexible_pane_geom.clone(); + position_for_new_pane + .rows + .set_inner(position_for_new_pane.rows.as_usize() - 1); + position_for_new_pane.y = position_for_new_pane.y + 1; + flexible_pane_geom.rows = Dimension::fixed(1); + self.panes + .borrow_mut() + .get_mut(&flexible_pane_id) + .with_context(err_context)? + .set_geom(flexible_pane_geom); + return Ok(position_for_new_pane); + } + Err(anyhow!("Not enough room for another pane!")) + } + pub fn new_stack(&self, root_pane_id: PaneId, pane_count_in_stack: usize) -> Vec { let mut stacked_geoms = vec![]; let panes = self.panes.borrow(); let running_stack_geom = panes.get(&root_pane_id).map(|p| p.position_and_size()); @@ -472,7 +486,8 @@ impl<'a> StackedPanes<'a> { log::error!("Pane not found"); // TODO: better error return stacked_geoms; }; - running_stack_geom.is_stacked = true; + let stack_id = self.next_stack_id(); + running_stack_geom.stacked = Some(stack_id); let mut pane_index_in_stack = 0; loop { if pane_index_in_stack == pane_count_in_stack { @@ -493,13 +508,449 @@ impl<'a> StackedPanes<'a> { } stacked_geoms } + fn extract_geoms_from_stack( + &self, + root_pane_id: PaneId, + ) -> Option<(PaneGeom, Vec<(PaneId, PaneGeom)>)> { + let panes = self.panes.borrow(); + let mut geom_of_main_pane = panes.get(&root_pane_id).map(|p| p.position_and_size())?; + let mut extra_stacked_geoms_of_main_pane = vec![]; + if geom_of_main_pane.is_stacked() { + let other_panes_in_stack = self.positions_in_stack(&root_pane_id).ok()?; + for other_pane in other_panes_in_stack { + if other_pane.0 != root_pane_id { + // so it is not duplicated + extra_stacked_geoms_of_main_pane.push(other_pane); + } + } + let logical_position = geom_of_main_pane.logical_position; + geom_of_main_pane = self.position_and_size_of_stack(&root_pane_id)?; + geom_of_main_pane.logical_position = logical_position; + } + Some((geom_of_main_pane, extra_stacked_geoms_of_main_pane)) + } + fn positions_of_panes_and_their_stacks( + &self, + pane_ids: Vec, + ) -> Option> { + let mut positions = vec![]; + let panes = self.panes.borrow(); + for pane_id in &pane_ids { + let geom_of_pane = panes.get(pane_id).map(|p| p.position_and_size())?; + if geom_of_pane.is_stacked() { + let mut other_panes_in_stack = self.positions_in_stack(pane_id).ok()?; + positions.append(&mut other_panes_in_stack); + } else { + positions.push((*pane_id, geom_of_pane)); + } + } + Some(positions) + } + + fn combine_geoms_horizontally( + &self, + pane_ids_and_geoms: &Vec<(PaneId, PaneGeom)>, + ) -> Option { + let mut geoms_to_combine = HashSet::new(); + for (other_pane_id, other_geom) in pane_ids_and_geoms { + if other_geom.is_stacked() { + geoms_to_combine.insert(self.position_and_size_of_stack(other_pane_id)?); + } else { + geoms_to_combine.insert(*other_geom); + } + } + let mut geoms_to_combine: Vec = geoms_to_combine.iter().copied().collect(); + geoms_to_combine.sort_by(|a_geom, b_geom| a_geom.x.cmp(&b_geom.x)); + + let geom_to_combine = geoms_to_combine.get(0)?; + geom_to_combine + .combine_horizontally_with_many(&geoms_to_combine.iter().copied().skip(1).collect()) + } + fn combine_geoms_vertically( + &self, + pane_ids_and_geoms: &Vec<(PaneId, PaneGeom)>, + ) -> Option { + let mut geoms_to_combine = HashSet::new(); + for (other_pane_id, other_geom) in pane_ids_and_geoms { + if other_geom.is_stacked() { + geoms_to_combine.insert(self.position_and_size_of_stack(other_pane_id)?); + } else { + geoms_to_combine.insert(*other_geom); + } + } + let mut geoms_to_combine: Vec = geoms_to_combine.iter().copied().collect(); + + geoms_to_combine.sort_by(|a_geom, b_geom| a_geom.y.cmp(&b_geom.y)); + let geom_to_combine = geoms_to_combine.get(0)?; + geom_to_combine + .combine_vertically_with_many(&geoms_to_combine.iter().copied().skip(1).collect()) + } + pub fn combine_vertically_aligned_panes_to_stack( + &mut self, + root_pane_id: &PaneId, + neighboring_pane_ids: Vec, + ) -> Result<()> { + let (geom_of_main_pane, mut extra_stacked_geoms_of_main_pane) = self + .extract_geoms_from_stack(*root_pane_id) + .ok_or_else(|| anyhow!("Failed to extract geoms from stack"))?; + let mut other_pane_ids_and_geoms = self + .positions_of_panes_and_their_stacks(neighboring_pane_ids) + .ok_or_else(|| anyhow!("Failed to get pane geoms"))?; + if other_pane_ids_and_geoms.is_empty() { + // nothing to do + return Ok(()); + }; + let Some(geom_to_combine) = self.combine_geoms_horizontally(&other_pane_ids_and_geoms) + else { + log::error!("Failed to combine geoms horizontally"); + return Ok(()); + }; + let new_stack_geom = if geom_to_combine.y < geom_of_main_pane.y { + geom_to_combine.combine_vertically_with(&geom_of_main_pane) + } else { + geom_of_main_pane.combine_vertically_with(&geom_to_combine) + }; + let Some(new_stack_geom) = new_stack_geom else { + // nothing to do, likely the pane below is fixed + return Ok(()); + }; + let stack_id = self.next_stack_id(); + // we add the extra panes in the original stack (if any) so that they will be assigned pane + // positions but not affect the stack geometry + other_pane_ids_and_geoms.append(&mut extra_stacked_geoms_of_main_pane); + let mut panes = self.panes.borrow_mut(); + let mut running_y = new_stack_geom.y; + let mut geom_of_flexible_pane = new_stack_geom.clone(); + geom_of_flexible_pane + .rows + .decrease_inner(other_pane_ids_and_geoms.len()); + geom_of_flexible_pane.stacked = Some(stack_id); + let mut all_stack_geoms = other_pane_ids_and_geoms; + let original_geom_of_main_pane = panes + .get(&root_pane_id) + .ok_or_else(|| anyhow!("Failed to find root geom"))? + .position_and_size(); // for sorting purposes + all_stack_geoms.push((*root_pane_id, original_geom_of_main_pane)); + all_stack_geoms.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| { + if a_geom.y == b_geom.y { + a_geom.x.cmp(&b_geom.x) + } else { + a_geom.y.cmp(&b_geom.y) + } + }); + for (pane_id, mut pane_geom) in all_stack_geoms { + if let Some(pane_in_stack) = panes.get_mut(&pane_id) { + if &pane_id == root_pane_id { + pane_geom.x = new_stack_geom.x; + pane_geom.cols = new_stack_geom.cols; + pane_geom.y = running_y; + pane_geom.rows = geom_of_flexible_pane.rows; + pane_geom.stacked = Some(stack_id); + running_y += geom_of_flexible_pane.rows.as_usize(); + pane_in_stack.set_geom(pane_geom); + } else { + pane_geom.x = new_stack_geom.x; + pane_geom.cols = new_stack_geom.cols; + pane_geom.y = running_y; + pane_geom.rows = Dimension::fixed(1); + pane_geom.stacked = Some(stack_id); + running_y += 1; + pane_in_stack.set_geom(pane_geom); + } + } + } + Ok(()) + } + pub fn combine_horizontally_aligned_panes_to_stack( + &mut self, + root_pane_id: &PaneId, + neighboring_pane_ids: Vec, + ) -> Result<()> { + let (geom_of_main_pane, mut extra_stacked_geoms_of_main_pane) = self + .extract_geoms_from_stack(*root_pane_id) + .ok_or_else(|| anyhow!("Failed to extract geoms from stack"))?; + let mut other_pane_ids_and_geoms = self + .positions_of_panes_and_their_stacks(neighboring_pane_ids) + .ok_or_else(|| anyhow!("Failed to get pane geoms"))?; + if other_pane_ids_and_geoms.is_empty() { + // nothing to do + return Ok(()); + }; + let Some(geom_to_combine) = self.combine_geoms_vertically(&other_pane_ids_and_geoms) else { + log::error!("Failed to combine geoms vertically"); + return Ok(()); + }; + let new_stack_geom = if geom_to_combine.x < geom_of_main_pane.x { + geom_to_combine.combine_horizontally_with(&geom_of_main_pane) + } else { + geom_of_main_pane.combine_horizontally_with(&geom_to_combine) + }; + let Some(new_stack_geom) = new_stack_geom else { + // nothing to do, likely the pane below is fixed + return Ok(()); + }; + let stack_id = self.next_stack_id(); + // we add the extra panes in the original stack (if any) so that they will be assigned pane + // positions but not affect the stack geometry + other_pane_ids_and_geoms.append(&mut extra_stacked_geoms_of_main_pane); + let mut panes = self.panes.borrow_mut(); + let mut running_y = new_stack_geom.y; + let mut geom_of_flexible_pane = new_stack_geom.clone(); + geom_of_flexible_pane + .rows + .decrease_inner(other_pane_ids_and_geoms.len()); + let mut all_stacked_geoms = other_pane_ids_and_geoms; + let original_geom_of_main_pane = panes + .get(&root_pane_id) + .ok_or_else(|| anyhow!("Failed to find root geom"))? + .position_and_size(); // for sorting purposes + all_stacked_geoms.push((*root_pane_id, original_geom_of_main_pane)); + all_stacked_geoms.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| { + if a_geom.x == b_geom.x { + a_geom.y.cmp(&b_geom.y) + } else { + a_geom.x.cmp(&b_geom.x) + } + }); + for (pane_id, mut pane_geom) in all_stacked_geoms { + if &pane_id == root_pane_id { + if let Some(root_pane) = panes.get_mut(&root_pane_id) { + pane_geom.x = new_stack_geom.x; + pane_geom.cols = new_stack_geom.cols; + pane_geom.y = running_y; + pane_geom.rows = geom_of_flexible_pane.rows; + pane_geom.stacked = Some(stack_id); + pane_geom.logical_position = root_pane.position_and_size().logical_position; + root_pane.set_geom(pane_geom); + running_y += pane_geom.rows.as_usize(); + } + } else { + if let Some(pane_in_stack) = panes.get_mut(&pane_id) { + pane_geom.x = new_stack_geom.x; + pane_geom.cols = new_stack_geom.cols; + pane_geom.y = running_y; + pane_geom.rows = Dimension::fixed(1); + pane_geom.stacked = Some(stack_id); + running_y += 1; + pane_in_stack.set_geom(pane_geom); + } + } + } + Ok(()) + } + pub fn break_pane_out_of_stack(&mut self, pane_id: &PaneId) -> Option> { + let err_context = || "Failed to break pane out of stack"; + let mut pane_ids_that_were_resized = vec![]; + let Some(position_and_size_of_stack) = self.position_and_size_of_stack(pane_id) else { + log::error!("Could not find stack size for pane id: {:?}", pane_id); + return None; + }; + let mut all_stacked_pane_positions = self + .positions_in_stack(&pane_id) + .with_context(err_context) + .ok()?; + if all_stacked_pane_positions.is_empty() { + return None; + } + let flexible_pane_id_is_on_the_bottom = all_stacked_pane_positions + .iter() + .last() + .map(|(_, last_pane_geom_in_stack)| last_pane_geom_in_stack.rows.is_percent()) + .unwrap_or(false); + let ( + mut new_position_and_size_of_stack, + position_and_size_of_broken_out_pane, + pane_id_to_break_out, + ) = if flexible_pane_id_is_on_the_bottom { + self.break_out_stack_geom_upwards( + position_and_size_of_stack, + &mut all_stacked_pane_positions, + )? + } else { + self.break_out_stack_geom_downwards( + position_and_size_of_stack, + &mut all_stacked_pane_positions, + )? + }; + let stack_id = all_stacked_pane_positions + .iter() + .next() + .and_then(|(_, first_geom)| first_geom.stacked)?; + let flexible_pane_id = self.get_flexible_pane_id(&all_stacked_pane_positions)?; + self.set_geom_of_broken_out_pane( + pane_id_to_break_out, + position_and_size_of_broken_out_pane, + ); + pane_ids_that_were_resized.push(pane_id_to_break_out); + new_position_and_size_of_stack.stacked = Some(stack_id); + self.reset_stack_size( + &new_position_and_size_of_stack, + &all_stacked_pane_positions, + flexible_pane_id, + &mut pane_ids_that_were_resized, + ); + Some(pane_ids_that_were_resized) + } + pub fn next_stack_id(&self) -> usize { + let mut highest_stack_id = 0; + let panes = self.panes.borrow(); + for pane in panes.values() { + if let Some(stack_id) = pane.position_and_size().stacked { + highest_stack_id = std::cmp::max(highest_stack_id, stack_id + 1); + } + } + highest_stack_id + } + fn reset_stack_size( + &self, + new_position_and_size_of_stack: &PaneGeom, + all_stacked_pane_positions: &Vec<(PaneId, PaneGeom)>, + flexible_pane_id: PaneId, + pane_ids_that_were_resized: &mut Vec, + ) { + let mut running_pane_geom = new_position_and_size_of_stack.clone(); + let only_one_pane_left_in_stack = all_stacked_pane_positions.len() == 1; + let count_of_one_liners_in_stack = + all_stacked_pane_positions.iter().len().saturating_sub(1); + let flexible_pane_row_count = running_pane_geom + .rows + .as_usize() + .saturating_sub(count_of_one_liners_in_stack); + for (pane_id_in_stack, pane_geom_in_stack) in all_stacked_pane_positions.iter() { + let logical_position = pane_geom_in_stack.logical_position; + if only_one_pane_left_in_stack { + self.set_geom_of_broken_out_pane(*pane_id_in_stack, running_pane_geom); + } else if pane_id_in_stack == &flexible_pane_id { + self.set_geom_of_flexible_pane( + *pane_id_in_stack, + &mut running_pane_geom, + flexible_pane_row_count, + logical_position, + ); + } else { + self.set_geom_of_one_liner_pane( + *pane_id_in_stack, + &mut running_pane_geom, + logical_position, + ); + } + pane_ids_that_were_resized.push(*pane_id_in_stack); + } + } + fn set_geom_of_broken_out_pane( + &self, + pane_id_to_break_out: PaneId, + mut position_and_size: PaneGeom, + ) { + let mut panes = self.panes.borrow_mut(); + if let Some(pane_to_break_out) = panes.get_mut(&pane_id_to_break_out) { + let logical_position_of_pane = pane_to_break_out.current_geom().logical_position; + position_and_size.logical_position = logical_position_of_pane; + position_and_size.stacked = None; + pane_to_break_out.set_geom(position_and_size); + } + } + fn set_geom_of_flexible_pane( + &self, + pane_id_of_flexible_pane: PaneId, + running_pane_geom: &mut PaneGeom, + row_count: usize, + logical_position: Option, + ) { + let mut flexible_pane_geom = running_pane_geom.clone(); + let mut panes = self.panes.borrow_mut(); + if let Some(pane_in_stack) = panes.get_mut(&pane_id_of_flexible_pane) { + flexible_pane_geom.rows.set_inner(row_count); + running_pane_geom.y += flexible_pane_geom.rows.as_usize(); + running_pane_geom + .rows + .decrease_inner(flexible_pane_geom.rows.as_usize()); + flexible_pane_geom.logical_position = logical_position; + pane_in_stack.set_geom(flexible_pane_geom); + } + } + fn set_geom_of_one_liner_pane( + &self, + pane_id_of_one_liner_pane: PaneId, + running_pane_geom: &mut PaneGeom, + logical_position: Option, + ) { + let mut one_liner_geom = running_pane_geom.clone(); + let mut panes = self.panes.borrow_mut(); + if let Some(pane_in_stack) = panes.get_mut(&pane_id_of_one_liner_pane) { + one_liner_geom.rows = Dimension::fixed(1); + running_pane_geom.y += 1; + running_pane_geom.rows.decrease_inner(1); + one_liner_geom.logical_position = logical_position; + pane_in_stack.set_geom(one_liner_geom); + } + } + fn break_out_stack_geom_upwards( + &self, + position_and_size_of_stack: PaneGeom, + all_stacked_pane_positions: &mut Vec<(PaneId, PaneGeom)>, + ) -> Option<(PaneGeom, PaneGeom, PaneId)> { + let mut new_position_and_size_of_stack = position_and_size_of_stack.clone(); + let rows_for_broken_out_pane = new_position_and_size_of_stack + .rows + .split_out(all_stacked_pane_positions.len() as f64); + let mut position_and_size_of_broken_out_pane = position_and_size_of_stack.clone(); + position_and_size_of_broken_out_pane.stacked = None; + position_and_size_of_broken_out_pane.rows = rows_for_broken_out_pane; + new_position_and_size_of_stack.y = position_and_size_of_broken_out_pane.y + + position_and_size_of_broken_out_pane.rows.as_usize(); + let pane_id_to_break_out = all_stacked_pane_positions.remove(0).0; + Some(( + new_position_and_size_of_stack, + position_and_size_of_broken_out_pane, + pane_id_to_break_out, + )) + } + fn break_out_stack_geom_downwards( + &self, + position_and_size_of_stack: PaneGeom, + all_stacked_pane_positions: &mut Vec<(PaneId, PaneGeom)>, + ) -> Option<(PaneGeom, PaneGeom, PaneId)> { + let mut new_position_and_size_of_stack = position_and_size_of_stack.clone(); + let rows_for_broken_out_pane = new_position_and_size_of_stack + .rows + .split_out(all_stacked_pane_positions.len() as f64); + let mut position_and_size_of_broken_out_pane = position_and_size_of_stack.clone(); + position_and_size_of_broken_out_pane.stacked = None; + position_and_size_of_broken_out_pane.y = + new_position_and_size_of_stack.y + new_position_and_size_of_stack.rows.as_usize(); + position_and_size_of_broken_out_pane.rows = rows_for_broken_out_pane; + let pane_id_to_break_out = all_stacked_pane_positions.pop()?.0; + Some(( + new_position_and_size_of_stack, + position_and_size_of_broken_out_pane, + pane_id_to_break_out, + )) + } + fn get_flexible_pane_id( + &self, + all_stacked_pane_positions: &Vec<(PaneId, PaneGeom)>, + ) -> Option { + let err_context = || "Failed to get flexible pane id"; + let position_of_flexible_pane = self + .position_of_flexible_pane(&all_stacked_pane_positions) + .with_context(err_context) + .ok()?; + let (flexible_pane_id, _flexible_pane_geom) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .copied() + .with_context(err_context) + .ok()?; + Some(flexible_pane_id) + } fn get_all_stacks(&self) -> Result>> { let err_context = || "Failed to get positions in stack"; let panes = self.panes.borrow(); let all_flexible_panes_in_stack: Vec = panes .iter() .filter(|(_pid, p)| { - p.position_and_size().is_stacked && !p.position_and_size().rows.is_fixed() + p.position_and_size().is_stacked() && !p.position_and_size().rows.is_fixed() }) .map(|(pid, _p)| *pid) .collect(); @@ -528,6 +979,7 @@ impl<'a> StackedPanes<'a> { let pane_to_close = panes.get(id).with_context(err_context)?; let position_of_current_pane = self.position_of_current_pane(&all_stacked_pane_positions, &pane_to_close)?; + let only_one_pane_remaining_in_stack_after_close = all_stacked_pane_positions.len() == 2; if all_stacked_pane_positions.len() > position_of_current_pane + 1 { let mut pane_to_close_position_and_size = pane_to_close.position_and_size(); pane_to_close_position_and_size @@ -538,6 +990,9 @@ impl<'a> StackedPanes<'a> { .nth(position_of_current_pane + 1) .map(|(pid, _)| *pid) .with_context(err_context)?; + if only_one_pane_remaining_in_stack_after_close { + pane_to_close_position_and_size.stacked = None; + } let pane_below = panes.get_mut(&pane_id_below).with_context(err_context)?; pane_below.set_geom(pane_to_close_position_and_size); return Ok(true); @@ -552,6 +1007,9 @@ impl<'a> StackedPanes<'a> { .nth(position_of_current_pane - 1) .map(|(pid, _)| *pid) .with_context(err_context)?; + if only_one_pane_remaining_in_stack_after_close { + pane_to_close_position_and_size.stacked = None; + } let pane_above = panes.get_mut(&pane_id_above).with_context(err_context)?; pane_above.set_geom(pane_to_close_position_and_size); return Ok(true); @@ -710,3 +1168,7 @@ impl<'a> StackedPanes<'a> { Ok(()) } } + +#[cfg(test)] +#[path = "./unit/stacked_panes_tests.rs"] +mod stacked_panes_tests; diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs index fee6f56754..03581f169e 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -54,12 +54,17 @@ impl<'a> TiledPaneGrid<'a> { let mut pane_resizer = PaneResizer::new(self.panes.clone()); pane_resizer.layout(direction, space) } - fn get_pane_geom(&self, pane_id: &PaneId) -> Option { + pub fn get_pane_geom(&self, pane_id: &PaneId) -> Option { let panes = self.panes.borrow(); let pane_to_check = panes.get(pane_id)?; let pane_geom = pane_to_check.current_geom(); - if pane_geom.is_stacked { - StackedPanes::new(self.panes.clone()).position_and_size_of_stack(&pane_id) + if pane_geom.is_stacked() { + let mut stack_geom = + StackedPanes::new(self.panes.clone()).position_and_size_of_stack(&pane_id); + if let Some(stack_geom) = stack_geom.as_mut() { + stack_geom.stacked = pane_geom.stacked; // to get the stack id + } + stack_geom } else { Some(pane_geom) } @@ -540,7 +545,7 @@ impl<'a> TiledPaneGrid<'a> { .get_pane_geom(pane_id) .with_context(|| no_pane_id(pane_id)) .with_context(err_context)?; - let min_terminal_height = if pane.is_stacked { + let min_terminal_height = if pane.is_stacked() { StackedPanes::new(self.panes.clone()).min_stack_height(pane_id)? } else { MIN_TERMINAL_HEIGHT @@ -564,7 +569,7 @@ impl<'a> TiledPaneGrid<'a> { .get(id) .unwrap() .current_geom() - .is_stacked; + .is_stacked(); if current_pane_is_stacked { let _ = StackedPanes::new(self.panes.clone()).reduce_stack_height(&id, percent); } else { @@ -581,7 +586,7 @@ impl<'a> TiledPaneGrid<'a> { .get(id) .unwrap() .current_geom() - .is_stacked; + .is_stacked(); if current_pane_is_stacked { let _ = StackedPanes::new(self.panes.clone()).increase_stack_height(&id, percent); } else { @@ -597,7 +602,7 @@ impl<'a> TiledPaneGrid<'a> { .get(id) .unwrap() .current_geom() - .is_stacked; + .is_stacked(); if current_pane_is_stacked { let _ = StackedPanes::new(self.panes.clone()).increase_stack_width(&id, percent); } else { @@ -614,7 +619,7 @@ impl<'a> TiledPaneGrid<'a> { .get(id) .unwrap() .current_geom() - .is_stacked; + .is_stacked(); if current_pane_is_stacked { let _ = StackedPanes::new(self.panes.clone()).reduce_stack_width(&id, percent); } else { @@ -928,7 +933,7 @@ impl<'a> TiledPaneGrid<'a> { .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (_, pane))| pane); let next_pane_is_stacked = next_pane - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false); if next_pane_is_stacked { if let Some(next_pane_id) = next_pane.map(|p| p.pid()) { @@ -953,7 +958,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, (_, c))| { c.is_directly_above(Box::as_ref(source_pane)) && c.vertically_overlaps_with(Box::as_ref(source_pane)) - && c.current_geom().is_stacked + && c.current_geom().is_stacked() }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -986,7 +991,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, (_, c))| { c.is_directly_below(Box::as_ref(source_pane)) && c.vertically_overlaps_with(Box::as_ref(source_pane)) - && c.current_geom().is_stacked + && c.current_geom().is_stacked() }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -1018,7 +1023,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, (_, c))| { c.is_directly_below(Box::as_ref(current_pane)) && c.vertically_overlaps_with(Box::as_ref(current_pane)) - && !c.current_geom().is_stacked + && !c.current_geom().is_stacked() }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -1083,7 +1088,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, (_, c))| { c.is_directly_above(Box::as_ref(current_pane)) && c.vertically_overlaps_with(Box::as_ref(current_pane)) - && !c.current_geom().is_stacked + && !c.current_geom().is_stacked() }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -1109,7 +1114,7 @@ impl<'a> TiledPaneGrid<'a> { .map(|(_, (_pid, pane))| pane) .copied(); let next_pane_is_stacked = next_pane - .map(|p| p.current_geom().is_stacked) + .map(|p| p.current_geom().is_stacked()) .unwrap_or(false); if next_pane_is_stacked { if let Some(next_pane_id) = next_pane.map(|p| p.pid()) { @@ -1123,7 +1128,7 @@ impl<'a> TiledPaneGrid<'a> { pane_ids.iter().fold(HashSet::new(), |mut borders, p| { let panes = self.panes.borrow(); let pane = panes.get(p).unwrap(); - if pane.current_geom().is_stacked { + if pane.current_geom().is_stacked() { let pane_geom = StackedPanes::new(self.panes.clone()) .position_and_size_of_stack(&pane.pid()) .unwrap(); @@ -1298,7 +1303,7 @@ impl<'a> TiledPaneGrid<'a> { let freed_space = pane_to_close.position_and_size(); let freed_width = freed_space.cols.as_percent(); let freed_height = freed_space.rows.as_percent(); - let pane_to_close_is_stacked = pane_to_close.current_geom().is_stacked; + let pane_to_close_is_stacked = pane_to_close.current_geom().is_stacked(); (freed_width, freed_height, pane_to_close_is_stacked) }; if pane_to_close_is_stacked { @@ -1331,7 +1336,7 @@ impl<'a> TiledPaneGrid<'a> { let panes = self.panes.borrow(); let pane_sequence: Vec<(&PaneId, &&mut Box)> = panes .iter() - .filter(|(_, p)| p.selectable() && !p.current_geom().is_stacked) + .filter(|(_, p)| p.selectable() && !p.current_geom().is_stacked()) .collect(); let (_largest_pane_size, pane_id_to_split) = pane_sequence.iter().fold( (0, None), @@ -1370,12 +1375,38 @@ impl<'a> TiledPaneGrid<'a> { direction.map(|direction| (*t_id_to_split, direction)) }) } + pub fn split_pane( + &self, + active_pane_id: &PaneId, + cursor_height_width_ratio: Option, + ) -> Option<(PaneId, SplitDirection)> { + // right now the minimum here is hard-coded to a sane "I don't want my terminal smaller" + // number, but we might want to change this to be a percentage of the current screen if it + // feels better + let panes = self.panes.borrow(); + let Some(pane_to_split) = panes.get(active_pane_id) else { + return None; + }; + let direction = if pane_to_split.rows() + * cursor_height_width_ratio.unwrap_or(DEFAULT_CURSOR_HEIGHT_WIDTH_RATIO) + > pane_to_split.cols() + && pane_to_split.rows() > 10 * 2 + { + Some(SplitDirection::Horizontal) + } else if pane_to_split.cols() > 30 * 2 { + Some(SplitDirection::Vertical) + } else { + None + }; + + direction.map(|direction| (*active_pane_id, direction)) + } pub fn has_room_for_new_stacked_pane(&self) -> bool { let panes = self.panes.borrow(); let flexible_pane_in_stack: Vec<(&PaneId, &&mut Box)> = panes .iter() .filter(|(_, p)| { - p.selectable() && p.current_geom().is_stacked && !p.current_geom().rows.is_fixed() + p.selectable() && p.current_geom().is_stacked() && !p.current_geom().rows.is_fixed() }) .collect(); flexible_pane_in_stack @@ -1385,6 +1416,940 @@ impl<'a> TiledPaneGrid<'a> { pub fn make_room_in_stack_for_pane(&mut self) -> Result { StackedPanes::new(self.panes.clone()).make_room_for_new_pane() } + pub fn make_room_in_stack_of_pane_id_for_pane(&mut self, pane_id: &PaneId) -> Result { + StackedPanes::new(self.panes.clone()).make_room_for_new_pane_in_stack(pane_id) + } + fn pane_ids_have_the_same_y(&self, pane_ids: &[PaneId]) -> bool { + let panes = self.panes.borrow(); + let mut pane_y = None; + let mut panes_all_have_the_same_y = true; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + match pane_y { + Some(pane_y) => { + if pane_y != pane.position_and_size().y { + panes_all_have_the_same_y = false; + } + }, + None => { + pane_y = Some(pane.position_and_size().y); + }, + } + } + } + panes_all_have_the_same_y + } + fn group_panes_by_highest_y(&self, pane_ids: &[PaneId]) -> (Vec, Vec) { + // returns (left to right) + // 1. panes with the highest y (geometrically closer to the bottom) + // 2. if there are panes left, returns them as the second group + let panes = self.panes.borrow(); + let mut highest_pane_y = 0; + let mut first_group = vec![]; + let mut second_group = vec![]; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + let pane_geom = pane.position_and_size(); + let previous_highest_y = highest_pane_y; + highest_pane_y = std::cmp::max(pane_geom.y, highest_pane_y); + if previous_highest_y != highest_pane_y { + second_group.append(&mut first_group); + } + if pane_geom.y == highest_pane_y { + first_group.push(*p_id); + } else { + second_group.push(*p_id); + } + } + } + (first_group, second_group) + } + fn group_panes_by_highest_x(&self, pane_ids: &[PaneId]) -> (Vec, Vec) { + // returns (left to right) + // 1. panes with the highest x (geometrically closer to the left) + // 2. if there are panes remaining, returns them as the second group + let panes = self.panes.borrow(); + let mut highest_pane_x = 0; + let mut first_group = vec![]; + let mut second_group = vec![]; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + let pane_geom = pane.position_and_size(); + let previous_highest_x = highest_pane_x; + highest_pane_x = std::cmp::max(pane_geom.x, highest_pane_x); + if previous_highest_x != highest_pane_x { + second_group.append(&mut first_group); + } + if pane_geom.x == highest_pane_x { + first_group.push(*p_id); + } else { + second_group.push(*p_id); + } + } + } + (first_group, second_group) + } + fn group_panes_by_lowest_rows(&self, pane_ids: &[PaneId]) -> (Vec, Vec) { + // returns (left to right) + // 1. panes with the lowest rows + // 2. if there are panes left, returns them as the second group + // we expect all given panes here to have the same y + let panes = self.panes.borrow(); + let mut lowest_row_count = None; + let mut first_group = vec![]; + let mut second_group = vec![]; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + let pane_geom = pane.position_and_size(); + let previous_lowest_row_count = + lowest_row_count.unwrap_or_else(|| pane_geom.rows.as_usize()); + lowest_row_count = Some(std::cmp::min( + pane_geom.rows.as_usize(), + lowest_row_count.unwrap_or_else(|| pane_geom.rows.as_usize()), + )); + if Some(previous_lowest_row_count) != lowest_row_count { + second_group.append(&mut first_group); + } + if Some(pane_geom.rows.as_usize()) == lowest_row_count { + first_group.push(*p_id); + } else { + second_group.push(*p_id); + } + } + } + (first_group, second_group) + } + fn group_panes_by_lowest_cols(&self, pane_ids: &[PaneId]) -> (Vec, Vec) { + // returns (left to right) + // 1. panes with the lowest cols + // 2. if there are panes left, returns them as the second group + // we expect all given panes here to have the same x + let panes = self.panes.borrow(); + let mut lowest_col_count = None; + let mut first_group = vec![]; + let mut second_group = vec![]; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + let pane_geom = pane.position_and_size(); + let previous_lowest_col_count = + lowest_col_count.unwrap_or_else(|| pane_geom.cols.as_usize()); + lowest_col_count = Some(std::cmp::min( + pane_geom.cols.as_usize(), + lowest_col_count.unwrap_or_else(|| pane_geom.cols.as_usize()), + )); + if Some(previous_lowest_col_count) != lowest_col_count { + second_group.append(&mut first_group); + } + if Some(pane_geom.cols.as_usize()) == lowest_col_count { + first_group.push(*p_id); + } else { + second_group.push(*p_id); + } + } + } + (first_group, second_group) + } + fn pane_ids_have_the_same_x(&self, pane_ids: &[PaneId]) -> bool { + let panes = self.panes.borrow(); + let mut pane_x = None; + let mut panes_all_have_the_same_x = true; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + match pane_x { + Some(pane_x) => { + if pane_x != pane.position_and_size().x { + panes_all_have_the_same_x = false; + } + }, + None => { + pane_x = Some(pane.position_and_size().x); + }, + } + } + } + panes_all_have_the_same_x + } + fn pane_ids_have_the_same_height(&self, pane_ids: &[PaneId]) -> bool { + let panes = self.panes.borrow(); + let mut pane_height = None; + let mut panes_all_have_the_same_height = true; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + match pane_height { + Some(pane_height) => { + if pane_height != pane.position_and_size().rows.as_usize() { + panes_all_have_the_same_height = false; + } + }, + None => { + pane_height = Some(pane.position_and_size().rows.as_usize()); + }, + } + } + } + panes_all_have_the_same_height + } + fn pane_ids_have_the_same_width(&self, pane_ids: &[PaneId]) -> bool { + let panes = self.panes.borrow(); + let mut pane_width = None; + let mut panes_all_have_the_same_width = true; + for p_id in pane_ids { + if let Some(pane) = panes.get(p_id) { + match pane_width { + Some(pane_width) => { + if pane_width != pane.position_and_size().cols.as_usize() { + panes_all_have_the_same_width = false; + } + }, + None => { + pane_width = Some(pane.position_and_size().cols.as_usize()); + }, + } + } + } + panes_all_have_the_same_width + } + pub fn direct_neighboring_pane_ids_above(&self, root_pane_id: &PaneId) -> Vec { + // here we look for panes that are directly above the provided root pane but that do not + // exceed its vertical borders (x and x + cols) + let Some(root_pane_geom) = self.get_pane_geom(root_pane_id) else { + log::error!("Could nto find root pane geom"); + return vec![]; + }; + let Some(neighbor_pane_ids) = self.neighbor_pane_ids(root_pane_id, Direction::Up).ok() + else { + log::error!("Could not find neighbor pane ids above"); + return vec![]; + }; + let neighbor_pane_ids = neighbor_pane_ids + .iter() + .filter(|pane_id| { + self.pane_is_between_vertical_borders( + pane_id, + root_pane_geom.x, + root_pane_geom.x + root_pane_geom.cols.as_usize(), + ) + }) + .copied() + .collect::>(); + // we only want to return these if they cover the entire vertical surface of the root pane id + // (as in - one of the panes contains its x and one of the panes contains its x + cols) + let mut boundaries_of_pane_ids = vec![]; + for p_id in &neighbor_pane_ids { + let mut vertical_boundaries_of_pane = self.get_vertical_boundaries_of_pane(p_id); + boundaries_of_pane_ids.append(&mut vertical_boundaries_of_pane); + } + if boundaries_of_pane_ids.contains(&root_pane_geom.x) + && boundaries_of_pane_ids.contains(&(root_pane_geom.x + root_pane_geom.cols.as_usize())) + { + neighbor_pane_ids + } else { + vec![] + } + } + fn get_vertical_boundaries_of_pane(&self, pane_id: &PaneId) -> Vec { + let Some(geom_of_pane) = self.get_pane_geom(pane_id) else { + log::error!("Could not find geom of pawne"); + return vec![]; + }; + vec![ + geom_of_pane.x, + geom_of_pane.x + geom_of_pane.cols.as_usize(), + ] + } + fn get_horizontal_boundaries_of_pane(&self, pane_id: &PaneId) -> Vec { + let Some(geom_of_pane) = self.get_pane_geom(pane_id) else { + log::error!("Could not find geom of pawne"); + return vec![]; + }; + vec![ + geom_of_pane.y, + geom_of_pane.y + geom_of_pane.rows.as_usize(), + ] + } + fn fill_geom_holes_horizontally_upwards( + &mut self, + pane_ids_to_expand: &[PaneId], + holes: &[PaneId], + ) -> Result<()> { + // here we fill in the pane_ids_to_expand over the holes horizontally and then shorten the + // height of the holes by the pane_ids_to_expand (squeeze them upwards) + // we expect the pane_ids_to_expand to all have the same y and height and for the holes to + // all be higher + let err_context = || format!("Failed to fill_geom_holes_horizontally_upwards"); + let mut panes_to_expand = vec![]; + let mut hole_panes = vec![]; + for p_id in pane_ids_to_expand { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + panes_to_expand.push((p_id, pane.position_and_size())); + } + for p_id in holes { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + hole_panes.push((p_id, pane.position_and_size())); + } + panes_to_expand.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.x.cmp(&b_geom.x)); + hole_panes.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.x.cmp(&b_geom.x)); + let mut uncovered_hole = None; + for (hole_id, mut hole_geom) in hole_panes { + let hole_x = hole_geom.x; + let Some((pane_id_with_closest_x, mut pane_geom_with_closest_x)) = panes_to_expand + .iter() + .find(|(_p_id, p_geom)| p_geom.x >= hole_x) + else { + // can happen if the last geom was a hole + uncovered_hole = Some((hole_id, hole_geom)); + continue; + }; + pane_geom_with_closest_x + .cols + .increase_inner(hole_geom.cols.as_usize()); + pane_geom_with_closest_x.x = hole_x; + hole_geom.rows.reduce_by( + pane_geom_with_closest_x + .rows + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_x.rows.as_usize(), + ); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_x) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_x); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + if let Some((hole_id, mut hole_geom)) = uncovered_hole { + let (pane_id_with_closest_x, mut pane_geom_with_closest_x) = + panes_to_expand.last().with_context(err_context)?; + pane_geom_with_closest_x + .cols + .increase_inner(hole_geom.cols.as_usize()); + hole_geom.rows.reduce_by( + pane_geom_with_closest_x + .rows + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_x.rows.as_usize(), + ); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_x) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_x); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + Ok(()) + } + fn fill_geom_holes_horizontally_downwards( + &mut self, + pane_ids_to_expand: &[PaneId], + holes: &[PaneId], + ) -> Result<()> { + // here we fill in the pane_ids_to_expand over the holes horizontally and then shorten the + // height of the holes by the pane_ids_to_expand, as well as increase their y by the same + // count (squeeze them downwards) + // we expect the pane_ids_to_expand to all have the same y and height and for the holes to + // all be higher and have the same y + let err_context = || format!("Failed to fill_geom_holes_horizontally_downwards"); + let mut panes_to_expand = vec![]; + let mut hole_panes = vec![]; + for p_id in pane_ids_to_expand { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + panes_to_expand.push((p_id, pane.position_and_size())); + } + for p_id in holes { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + hole_panes.push((p_id, pane.position_and_size())); + } + panes_to_expand.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.x.cmp(&b_geom.x)); + hole_panes.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.x.cmp(&b_geom.x)); + let mut uncovered_hole = None; + for (hole_id, mut hole_geom) in hole_panes { + let hole_x = hole_geom.x; + let Some((pane_id_with_closest_x, mut pane_geom_with_closest_x)) = panes_to_expand + .iter() + .find(|(_p_id, p_geom)| p_geom.x >= hole_x) + else { + // can happen if the last geom was a hole + uncovered_hole = Some((hole_id, hole_geom)); + continue; + }; + pane_geom_with_closest_x + .cols + .increase_inner(hole_geom.cols.as_usize()); + pane_geom_with_closest_x.x = hole_x; + hole_geom.rows.reduce_by( + pane_geom_with_closest_x + .rows + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_x.rows.as_usize(), + ); + hole_geom.y += pane_geom_with_closest_x.rows.as_usize(); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_x) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_x); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + if let Some((hole_id, mut hole_geom)) = uncovered_hole { + let (pane_id_with_closest_x, mut pane_geom_with_closest_x) = + panes_to_expand.last().with_context(err_context)?; + pane_geom_with_closest_x + .cols + .increase_inner(hole_geom.cols.as_usize()); + hole_geom.rows.reduce_by( + pane_geom_with_closest_x + .rows + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_x.rows.as_usize(), + ); + hole_geom.y += pane_geom_with_closest_x.rows.as_usize(); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_x) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_x); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + Ok(()) + } + fn fill_geom_holes_vertically_to_the_right( + &mut self, + pane_ids_to_expand: &[PaneId], + holes: &[PaneId], + ) -> Result<()> { + // here we fill in the pane_ids_to_expand over the holes vertically and then shorten the + // width of the holes by the pane_ids_to_expand, as well as increase their x by the same + // count (squeeze them to the right) + // we expect the pane_ids_to_expand to all have the same x and width and for the holes to + // all be wider and have the same x + let err_context = || format!("Failed to fill_geom_holes_vertically_to_the_right"); + let mut panes_to_expand = vec![]; + let mut hole_panes = vec![]; + for p_id in pane_ids_to_expand { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + panes_to_expand.push((p_id, pane.position_and_size())); + } + for p_id in holes { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + hole_panes.push((p_id, pane.position_and_size())); + } + panes_to_expand.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.y.cmp(&b_geom.y)); + hole_panes.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.y.cmp(&b_geom.y)); + let mut uncovered_hole = None; + for (hole_id, mut hole_geom) in hole_panes { + let hole_y = hole_geom.y; + let Some((pane_id_with_closest_y, mut pane_geom_with_closest_y)) = panes_to_expand + .iter() + .find(|(_p_id, p_geom)| p_geom.y >= hole_y) + else { + // can happen if the last geom was a hole + uncovered_hole = Some((hole_id, hole_geom)); + continue; + }; + pane_geom_with_closest_y + .rows + .increase_inner(hole_geom.rows.as_usize()); + pane_geom_with_closest_y.y = hole_y; + hole_geom.cols.reduce_by( + pane_geom_with_closest_y + .cols + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_y.cols.as_usize(), + ); + hole_geom.x += pane_geom_with_closest_y.cols.as_usize(); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_y) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_y); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + if let Some((hole_id, mut hole_geom)) = uncovered_hole { + let (pane_id_with_closest_y, mut pane_geom_with_closest_y) = + panes_to_expand.last().with_context(err_context)?; + pane_geom_with_closest_y + .rows + .increase_inner(hole_geom.rows.as_usize()); + hole_geom.cols.reduce_by( + pane_geom_with_closest_y + .cols + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_y.cols.as_usize(), + ); + hole_geom.x += pane_geom_with_closest_y.cols.as_usize(); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_y) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_y); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + Ok(()) + } + fn fill_geom_holes_vertically_to_the_left( + &mut self, + pane_ids_to_expand: &[PaneId], + holes: &[PaneId], + ) -> Result<()> { + // here we fill in the pane_ids_to_expand over the holes vertically and then shorten the + // width of the holes by the pane_ids_to_expand's width (squeeze them to the right) + // we expect the pane_ids_to_expand to all have the same x and width and for the holes to + // all be wider + let err_context = || format!("Failed to fill_geom_holes_vertically_to_the_left"); + let mut panes_to_expand = vec![]; + let mut hole_panes = vec![]; + for p_id in pane_ids_to_expand { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + panes_to_expand.push((p_id, pane.position_and_size())); + } + for p_id in holes { + let panes = self.panes.borrow(); + let pane = panes.get(&p_id).with_context(err_context)?; + hole_panes.push((p_id, pane.position_and_size())); + } + panes_to_expand.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.y.cmp(&b_geom.y)); + hole_panes.sort_by(|(_a_id, a_geom), (_b_id, b_geom)| a_geom.y.cmp(&b_geom.y)); + let mut uncovered_hole = None; + for (hole_id, mut hole_geom) in hole_panes { + let hole_y = hole_geom.y; + let Some((pane_id_with_closest_y, mut pane_geom_with_closest_y)) = panes_to_expand + .iter() + .find(|(_p_id, p_geom)| p_geom.y >= hole_y) + else { + // can happen if the last geom was a hole + uncovered_hole = Some((hole_id, hole_geom)); + continue; + }; + pane_geom_with_closest_y + .rows + .increase_inner(hole_geom.rows.as_usize()); + pane_geom_with_closest_y.y = hole_y; + hole_geom.cols.reduce_by( + pane_geom_with_closest_y + .cols + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_y.cols.as_usize(), + ); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_y) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_y); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + if let Some((hole_id, mut hole_geom)) = uncovered_hole { + let (pane_id_with_closest_y, mut pane_geom_with_closest_y) = + panes_to_expand.last().with_context(err_context)?; + pane_geom_with_closest_y + .rows + .increase_inner(hole_geom.rows.as_usize()); + hole_geom.cols.reduce_by( + pane_geom_with_closest_y + .cols + .as_percent() + .with_context(err_context)?, + pane_geom_with_closest_y.cols.as_usize(), + ); + self.panes + .borrow_mut() + .get_mut(&pane_id_with_closest_y) + .with_context(err_context)? + .set_geom(pane_geom_with_closest_y); + self.panes + .borrow_mut() + .get_mut(&hole_id) + .with_context(err_context)? + .set_geom(hole_geom); + } + Ok(()) + } + pub fn stack_pane_up(&mut self, pane_id: &PaneId) -> Option> { + let mut neighboring_pane_ids_above = self.direct_neighboring_pane_ids_above(pane_id); + if !self.pane_ids_have_the_same_y(&neighboring_pane_ids_above) { + let (panes_with_highest_y, leftover_panes) = + self.group_panes_by_highest_y(&neighboring_pane_ids_above); + if let Err(e) = + self.fill_geom_holes_horizontally_upwards(&panes_with_highest_y, &leftover_panes) + { + log::error!("Failed to fill_geom_holes_horizontally upwards: {}", e); + return None; + } + neighboring_pane_ids_above = panes_with_highest_y; + } + let pane_is_selectable = |pane_id| { + self.panes + .borrow() + .get(pane_id) + .map(|pane| pane.selectable()) + .unwrap_or(false) + }; + if neighboring_pane_ids_above.is_empty() + || neighboring_pane_ids_above + .iter() + .any(|p| !pane_is_selectable(p)) + { + return None; + } + StackedPanes::new(self.panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id, neighboring_pane_ids_above); + StackedPanes::new(self.panes.clone()).expand_pane(&pane_id); + Some(vec![*pane_id]) + } + pub fn unstack_pane_up(&mut self, pane_id: &PaneId) -> Option> { + let pane_is_stacked = self + .get_pane_geom(pane_id) + .map(|pane_geom| pane_geom.is_stacked()) + .unwrap_or(false); + if pane_is_stacked { + StackedPanes::new(self.panes.clone()).break_pane_out_of_stack(&pane_id) + } else { + None + } + } + pub fn direct_neighboring_pane_ids_below(&self, root_pane_id: &PaneId) -> Vec { + // here we look for panes that are directly below the provided root pane but that do not + // exceed its vertical borders (x and x + cols) + let Some(root_pane_geom) = self.get_pane_geom(root_pane_id) else { + log::error!("Could nto find root pane geom"); + return vec![]; + }; + let Some(neighbor_pane_ids) = self.neighbor_pane_ids(root_pane_id, Direction::Down).ok() + else { + log::error!("Could not find neighbor pane ids above"); + return vec![]; + }; + let neighbor_pane_ids = neighbor_pane_ids + .iter() + .filter(|pane_id| { + self.pane_is_between_vertical_borders( + pane_id, + root_pane_geom.x, + root_pane_geom.x + root_pane_geom.cols.as_usize(), + ) + }) + .copied() + .collect::>(); + // we only want to return these if they cover the entire vertical surface of the root pane id + // (as in - one of the panes contains its x and one of the panes contains its x + cols) + let mut boundaries_of_pane_ids = vec![]; + for p_id in &neighbor_pane_ids { + let mut vertical_boundaries_of_pane = self.get_vertical_boundaries_of_pane(p_id); + boundaries_of_pane_ids.append(&mut vertical_boundaries_of_pane); + } + if boundaries_of_pane_ids.contains(&root_pane_geom.x) + && boundaries_of_pane_ids.contains(&(root_pane_geom.x + root_pane_geom.cols.as_usize())) + { + neighbor_pane_ids + } else { + vec![] + } + } + pub fn stack_pane_down(&mut self, pane_id: &PaneId) -> Option> { + let mut neighboring_pane_ids_below = self.direct_neighboring_pane_ids_below(pane_id); + if !self.pane_ids_have_the_same_height(&neighboring_pane_ids_below) { + let (panes_with_lowest_rows, leftover_panes) = + self.group_panes_by_lowest_rows(&neighboring_pane_ids_below); + if let Err(e) = self + .fill_geom_holes_horizontally_downwards(&panes_with_lowest_rows, &leftover_panes) + { + log::error!("Failed to fill_geom_holes_horizontally downwards: {}", e); + return None; + } + neighboring_pane_ids_below = panes_with_lowest_rows; + } + let pane_is_selectable = |pane_id| { + self.panes + .borrow() + .get(pane_id) + .map(|pane| pane.selectable()) + .unwrap_or(false) + }; + if neighboring_pane_ids_below.is_empty() + || neighboring_pane_ids_below + .iter() + .any(|p| !pane_is_selectable(p)) + { + return None; + } + StackedPanes::new(self.panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id, neighboring_pane_ids_below); + StackedPanes::new(self.panes.clone()).expand_pane(&pane_id); + Some(vec![*pane_id]) + } + pub fn direct_neighboring_pane_ids_to_the_left(&self, root_pane_id: &PaneId) -> Vec { + // here we look for panes that are directly to the left the provided root pane but that do not + // exceed its horizontal borders (y and y + rows) + let Some(root_pane_geom) = self.get_pane_geom(root_pane_id) else { + log::error!("Could nto find root pane geom"); + return vec![]; + }; + let Some(neighbor_pane_ids) = self.neighbor_pane_ids(root_pane_id, Direction::Left).ok() + else { + log::error!("Could not find neighbor pane ids to the left"); + return vec![]; + }; + let neighbor_pane_ids = neighbor_pane_ids + .iter() + .filter(|pane_id| { + self.pane_is_between_horizontal_borders( + pane_id, + root_pane_geom.y, + root_pane_geom.y + root_pane_geom.rows.as_usize(), + ) + }) + .copied() + .collect::>(); + // we only want to return these if they cover the entire horizontal surface of the root pane id + // (as in - one of the panes contains its y and one of the panes contains its y + rows) + let mut boundaries_of_pane_ids = vec![]; + for p_id in &neighbor_pane_ids { + let mut horizontal_boundaries_of_pane = self.get_horizontal_boundaries_of_pane(p_id); + boundaries_of_pane_ids.append(&mut horizontal_boundaries_of_pane); + } + if boundaries_of_pane_ids.contains(&root_pane_geom.y) + && boundaries_of_pane_ids.contains(&(root_pane_geom.y + root_pane_geom.rows.as_usize())) + { + neighbor_pane_ids + } else { + vec![] + } + } + pub fn stack_pane_left(&mut self, pane_id: &PaneId) -> Option> { + let mut neighboring_pane_ids_to_the_left = + self.direct_neighboring_pane_ids_to_the_left(pane_id); + if !self.pane_ids_have_the_same_x(&neighboring_pane_ids_to_the_left) { + let (panes_with_highest_x, leftover_panes) = + self.group_panes_by_highest_x(&neighboring_pane_ids_to_the_left); + if let Err(e) = + self.fill_geom_holes_vertically_to_the_left(&panes_with_highest_x, &leftover_panes) + { + log::error!("Failed to fill_geom_holes_vertically_to_the_left: {}", e); + return None; + } + neighboring_pane_ids_to_the_left = panes_with_highest_x; + } + let pane_is_selectable = |pane_id| { + self.panes + .borrow() + .get(pane_id) + .map(|pane| pane.selectable()) + .unwrap_or(false) + }; + if neighboring_pane_ids_to_the_left.is_empty() + || neighboring_pane_ids_to_the_left + .iter() + .any(|p| !pane_is_selectable(p)) + { + return None; + } + StackedPanes::new(self.panes.clone()).combine_horizontally_aligned_panes_to_stack( + &pane_id, + neighboring_pane_ids_to_the_left, + ); + StackedPanes::new(self.panes.clone()).expand_pane(&pane_id); + Some(vec![*pane_id]) + } + pub fn direct_neighboring_pane_ids_to_the_right(&self, root_pane_id: &PaneId) -> Vec { + // here we look for panes that are directly to the right the provided root pane but that do not + // exceed its horizontal borders (y and y + rows) + let Some(root_pane_geom) = self.get_pane_geom(root_pane_id) else { + log::error!("Could nto find root pane geom"); + return vec![]; + }; + let Some(neighbor_pane_ids) = self.neighbor_pane_ids(root_pane_id, Direction::Right).ok() + else { + log::error!("Could not find neighbor pane ids to the right"); + return vec![]; + }; + let neighbor_pane_ids = neighbor_pane_ids + .iter() + .filter(|pane_id| { + self.pane_is_between_horizontal_borders( + pane_id, + root_pane_geom.y, + root_pane_geom.y + root_pane_geom.rows.as_usize(), + ) + }) + .copied() + .collect::>(); + // we only want to return these if they cover the entire horizontal surface of the root pane id + // (as in - one of the panes contains its y and one of the panes contains its y + rows) + let mut boundaries_of_pane_ids = vec![]; + for p_id in &neighbor_pane_ids { + let mut horizontal_boundaries_of_pane = self.get_horizontal_boundaries_of_pane(p_id); + boundaries_of_pane_ids.append(&mut horizontal_boundaries_of_pane); + } + if boundaries_of_pane_ids.contains(&root_pane_geom.y) + && boundaries_of_pane_ids.contains(&(root_pane_geom.y + root_pane_geom.rows.as_usize())) + { + neighbor_pane_ids + } else { + vec![] + } + } + pub fn stack_pane_right(&mut self, pane_id: &PaneId) -> Option> { + let mut neighboring_pane_ids_to_the_right = + self.direct_neighboring_pane_ids_to_the_right(pane_id); + if !self.pane_ids_have_the_same_width(&neighboring_pane_ids_to_the_right) { + let (panes_with_lowest_cols, leftover_panes) = + self.group_panes_by_lowest_cols(&neighboring_pane_ids_to_the_right); + if let Err(e) = self + .fill_geom_holes_vertically_to_the_right(&panes_with_lowest_cols, &leftover_panes) + { + log::error!("Failed to fill_geom_holes_vertically_to_the_right: {}", e); + return None; + } + neighboring_pane_ids_to_the_right = panes_with_lowest_cols; + } + let pane_is_selectable = |pane_id| { + self.panes + .borrow() + .get(pane_id) + .map(|pane| pane.selectable()) + .unwrap_or(false) + }; + if neighboring_pane_ids_to_the_right.is_empty() + || neighboring_pane_ids_to_the_right + .iter() + .any(|p| !pane_is_selectable(p)) + { + return None; + } + if neighboring_pane_ids_to_the_right.is_empty() { + return None; + } + StackedPanes::new(self.panes.clone()).combine_horizontally_aligned_panes_to_stack( + &pane_id, + neighboring_pane_ids_to_the_right, + ); + StackedPanes::new(self.panes.clone()).expand_pane(&pane_id); + Some(vec![*pane_id]) + } + pub fn next_stack_id(&self) -> usize { + StackedPanes::new(self.panes.clone()).next_stack_id() + } + pub fn make_pane_stacked(&mut self, pane_id: &PaneId) -> Result<()> { + let mut geom_of_active_pane = self + .get_pane_geom(pane_id) + .ok_or_else(|| anyhow!("Failed to get pane geom"))?; + geom_of_active_pane.stacked = Some(self.next_stack_id()); + self.panes + .borrow_mut() + .get_mut(pane_id) + .ok_or_else(|| anyhow!("Failed to get pane geom"))? + .set_geom(geom_of_active_pane); + Ok(()) + } + fn get_vertically_aligned_pane_id_above(&self, pane_id: &PaneId) -> Option { + let Some(pane_geom) = self.get_pane_geom(pane_id) else { + return None; + }; + let panes = self.panes.borrow(); + let stacked_panes = StackedPanes::new(self.panes.clone()); + let pane_geom = if pane_geom.is_stacked() { + stacked_panes + .position_and_size_of_stack(pane_id) + .unwrap_or(pane_geom) + } else { + pane_geom + }; + panes.iter().find_map(|(candidate_pane_id, p)| { + if !p.selectable() { + return None; + } + let candidate_geom = p.current_geom(); + let candidate_geom = if candidate_geom.is_stacked() { + stacked_panes + .position_and_size_of_stack(candidate_pane_id) + .unwrap_or(candidate_geom) + } else { + candidate_geom + }; + if candidate_geom.y + candidate_geom.rows.as_usize() == pane_geom.y + && candidate_geom.x == pane_geom.x + && candidate_geom.cols.as_usize() == pane_geom.cols.as_usize() + { + return Some(p.pid()); + } + return None; + }) + } + fn get_vertically_aligned_pane_id_below(&self, pane_id: &PaneId) -> Option { + let Some(pane_geom) = self.get_pane_geom(pane_id) else { + return None; + }; + let panes = self.panes.borrow(); + let stacked_panes = StackedPanes::new(self.panes.clone()); + let pane_geom = if pane_geom.is_stacked() { + stacked_panes + .position_and_size_of_stack(pane_id) + .unwrap_or(pane_geom) + } else { + pane_geom + }; + panes.iter().find_map(|(candidate_pane_id, p)| { + if !p.selectable() { + return None; + } + let candidate_geom = p.current_geom(); + let candidate_geom = if candidate_geom.is_stacked() { + stacked_panes + .position_and_size_of_stack(candidate_pane_id) + .unwrap_or(candidate_geom) + } else { + candidate_geom + }; + if candidate_geom.y == pane_geom.y + pane_geom.rows.as_usize() + && candidate_geom.x == pane_geom.x + && candidate_geom.cols.as_usize() == pane_geom.cols.as_usize() + { + return Some(p.pid()); + } + return None; + }) + } } pub fn split(direction: SplitDirection, rect: &PaneGeom) -> Option<(PaneGeom, PaneGeom)> { diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_focused_pane_out_of_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_focused_pane_out_of_stack.snap new file mode 100644 index 0000000000..d1cb926a24 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_focused_pane_out_of_stack.snap @@ -0,0 +1,112 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 176 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: false, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: false, + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 66, + rows: Dimension { + constraint: Percent( + 11.1, + ), + inner: 11, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: false, + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 77, + rows: Dimension { + constraint: Percent( + 22.199999999999996, + ), + inner: 22, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: true, + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: true, + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_next_to_last_pane_out_of_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_next_to_last_pane_out_of_stack.snap new file mode 100644 index 0000000000..447bd016f9 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_next_to_last_pane_out_of_stack.snap @@ -0,0 +1,112 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 221 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 66, + rows: Dimension { + constraint: Percent( + 22.1, + ), + inner: 22, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 88, + rows: Dimension { + constraint: Percent( + 5.6, + ), + inner: 6, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 94, + rows: Dimension { + constraint: Percent( + 5.6, + ), + inner: 6, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack.snap new file mode 100644 index 0000000000..d5334b1b31 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack.snap @@ -0,0 +1,112 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 156 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: false, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: false, + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 66, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: true, + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 67, + rows: Dimension { + constraint: Percent( + 22.199999999999996, + ), + inner: 22, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: true, + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 89, + rows: Dimension { + constraint: Percent( + 11.1, + ), + inner: 11, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + is_stacked: false, + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_bottom.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_bottom.snap new file mode 100644 index 0000000000..0cc4eadb12 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_bottom.snap @@ -0,0 +1,116 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 201 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 66, + rows: Dimension { + constraint: Percent( + 11.1, + ), + inner: 11, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 77, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 78, + rows: Dimension { + constraint: Percent( + 22.199999999999996, + ), + inner: 22, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_middle.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_middle.snap new file mode 100644 index 0000000000..7afb38dc6d --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_middle.snap @@ -0,0 +1,116 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 179 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 66, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 67, + rows: Dimension { + constraint: Percent( + 22.199999999999996, + ), + inner: 22, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 89, + rows: Dimension { + constraint: Percent( + 11.1, + ), + inner: 11, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_top.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_top.snap new file mode 100644 index 0000000000..8e0008921e --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__break_pane_out_of_stack_top.snap @@ -0,0 +1,116 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 158 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 66, + rows: Dimension { + constraint: Percent( + 22.199999999999996, + ), + inner: 22, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 88, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 89, + rows: Dimension { + constraint: Percent( + 11.1, + ), + inner: 11, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack.snap new file mode 100644 index 0000000000..839c893463 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack.snap @@ -0,0 +1,76 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 233 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 98, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack_when_left_pane_is_stacked.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack_when_left_pane_is_stacked.snap new file mode 100644 index 0000000000..e7db8af531 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack_when_left_pane_is_stacked.snap @@ -0,0 +1,122 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 302 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 96, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 96, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 97, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack_when_right_pane_is_stacked.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack_when_right_pane_is_stacked.snap new file mode 100644 index 0000000000..945ed0a616 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_horizontally_aligned_panes_to_stack_when_right_pane_is_stacked.snap @@ -0,0 +1,122 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 324 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 2, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 96, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 1, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack.snap new file mode 100644 index 0000000000..ed90f6dddf --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack.snap @@ -0,0 +1,53 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 132 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 99, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 0, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_both_are_stacked.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_both_are_stacked.snap new file mode 100644 index 0000000000..917aa326d7 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_both_are_stacked.snap @@ -0,0 +1,122 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 214 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 1, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 96, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 97, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked.snap new file mode 100644 index 0000000000..f3163688cd --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked.snap @@ -0,0 +1,97 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 152 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 66.6, + ), + inner: 65, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_mid_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_mid_stack.snap new file mode 100644 index 0000000000..5e877d8b20 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_mid_stack.snap @@ -0,0 +1,120 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 193 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 66.6, + ), + inner: 64, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 97, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_on_top_of_stack.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_on_top_of_stack.snap new file mode 100644 index 0000000000..70c2293c0e --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_on_top_of_stack.snap @@ -0,0 +1,97 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 172 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Percent( + 33.3, + ), + inner: 33, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: None, + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 33, + rows: Dimension { + constraint: Percent( + 66.6, + ), + inner: 65, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 98, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 1, + }, + stacked: Some( + 1, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_with_multiple_non_stacked_neighbors.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_with_multiple_non_stacked_neighbors.snap new file mode 100644 index 0000000000..0bd8229f97 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_with_multiple_non_stacked_neighbors.snap @@ -0,0 +1,122 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 241 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 1, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 2, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 3, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 96, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 2, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_with_multiple_stacked_neighbors.snap b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_with_multiple_stacked_neighbors.snap new file mode 100644 index 0000000000..72bf80cb8e --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/snapshots/zellij_server__panes__tiled_panes__stacked_panes__stacked_panes_tests__combine_vertically_aligned_panes_to_stack_with_multiple_stacked_neighbors.snap @@ -0,0 +1,145 @@ +--- +source: zellij-server/src/panes/tiled_panes/./unit/stacked_panes_tests.rs +assertion_line: 261 +expression: "format!(\"{:#?}\", pane_geoms_after)" +--- +[ + PaneGeom { + x: 0, + y: 0, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 3, + ), + is_pinned: false, + logical_position: Some( + 1, + ), + }, + PaneGeom { + x: 0, + y: 1, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 3, + ), + is_pinned: false, + logical_position: Some( + 2, + ), + }, + PaneGeom { + x: 0, + y: 2, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 3, + ), + is_pinned: false, + logical_position: Some( + 3, + ), + }, + PaneGeom { + x: 0, + y: 3, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 3, + ), + is_pinned: false, + logical_position: Some( + 4, + ), + }, + PaneGeom { + x: 0, + y: 4, + rows: Dimension { + constraint: Percent( + 100.0, + ), + inner: 95, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 3, + ), + is_pinned: false, + logical_position: Some( + 5, + ), + }, + PaneGeom { + x: 0, + y: 99, + rows: Dimension { + constraint: Fixed( + 1, + ), + inner: 1, + }, + cols: Dimension { + constraint: Percent( + 100.0, + ), + inner: 100, + }, + stacked: Some( + 3, + ), + is_pinned: false, + logical_position: Some( + 6, + ), + }, +] diff --git a/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs b/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs new file mode 100644 index 0000000000..0782cf5120 --- /dev/null +++ b/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs @@ -0,0 +1,1239 @@ +use crate::{panes::tiled_panes::StackedPanes, panes::PaneId, tab::Pane}; +use insta::assert_snapshot; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; +use zellij_utils::errors::prelude::*; +use zellij_utils::input::layout::Run; +use zellij_utils::pane_size::Offset; +use zellij_utils::pane_size::{Dimension, PaneGeom}; + +use crate::ui::pane_boundaries_frame::FrameParams; +use crate::{ + output::{CharacterChunk, SixelImageChunk}, + pty::VteBytes, + ClientId, +}; +use std::time::Instant; +use zellij_utils::data::{InputMode, PaletteColor}; + +macro_rules! mock_pane { + ($pane_id:expr, $dimension:expr, $inner:expr, $x:expr, $y:expr, $logical_position:expr, $mock_panes:expr) => { + let mut mock_pane_rows = $dimension; + mock_pane_rows.set_inner($inner); + let mut mock_pane: Box = Box::new(MockPane::new(PaneGeom { + x: $x, + y: $y, + rows: mock_pane_rows, + cols: Dimension::percent(100.0), + logical_position: $logical_position, + ..Default::default() + })); + $mock_panes.insert($pane_id, &mut mock_pane); + }; +} + +macro_rules! mock_pane_with_cols { + ($pane_id:expr, $rows_dimension:expr, $rows_inner:expr, $cols_dimension:expr, $cols_inner:expr, $x:expr, $y:expr, $logical_position:expr, $mock_panes:expr) => { + let mut mock_pane_rows = $rows_dimension; + mock_pane_rows.set_inner($rows_inner); + let mut mock_pane_cols = $cols_dimension; + mock_pane_cols.set_inner($cols_inner); + let mut mock_pane: Box = Box::new(MockPane::new(PaneGeom { + x: $x, + y: $y, + rows: mock_pane_rows, + cols: mock_pane_cols, + logical_position: $logical_position, + ..Default::default() + })); + $mock_panes.insert($pane_id, &mut mock_pane); + }; +} +macro_rules! mock_stacked_pane { + ($pane_id:expr, $dimension:expr, $inner:expr, $x:expr, $y:expr, $logical_position:expr, $mock_panes:expr) => { + let mut mock_pane_rows = $dimension; + mock_pane_rows.set_inner($inner); + let mut mock_pane: Box = Box::new(MockPane::new(PaneGeom { + x: $x, + y: $y, + rows: mock_pane_rows, + cols: Dimension::percent(100.0), + logical_position: $logical_position, + stacked: Some(0), + ..Default::default() + })); + $mock_panes.insert($pane_id, &mut mock_pane); + }; +} + +macro_rules! mock_stacked_pane_with_id { + ($pane_id:expr, $dimension:expr, $inner:expr, $x:expr, $y:expr, $logical_position:expr, $mock_panes:expr, $stack_id:expr) => { + let mut mock_pane_rows = $dimension; + mock_pane_rows.set_inner($inner); + let mut mock_pane: Box = Box::new(MockPane::new(PaneGeom { + x: $x, + y: $y, + rows: mock_pane_rows, + cols: Dimension::percent(100.0), + logical_position: $logical_position, + stacked: Some($stack_id), + ..Default::default() + })); + $mock_panes.insert($pane_id, &mut mock_pane); + }; +} + +macro_rules! mock_stacked_pane_with_cols_and_id { + ($pane_id:expr, $rows_dimension:expr, $rows_inner:expr, $cols_dimension:expr, $cols_inner:expr, $x:expr, $y:expr, $logical_position:expr, $mock_panes:expr, $stack_id:expr) => { + let mut mock_pane_rows = $rows_dimension; + mock_pane_rows.set_inner($rows_inner); + let mut mock_pane_cols = $cols_dimension; + mock_pane_cols.set_inner($cols_inner); + let mut mock_pane: Box = Box::new(MockPane::new(PaneGeom { + x: $x, + y: $y, + rows: mock_pane_rows, + cols: mock_pane_cols, + logical_position: $logical_position, + stacked: Some($stack_id), + ..Default::default() + })); + $mock_panes.insert($pane_id, &mut mock_pane); + }; +} + +#[test] +fn combine_vertically_aligned_panes_to_stack() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(50.0), + 50, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(50.0), + 50, + 0, + 50, + Some(2), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_above = PaneId::Terminal(1); + let pane_id_below = PaneId::Terminal(2); + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id_above, vec![pane_id_below]) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::percent(33.3), + 33, + 0, + 67, + Some(4), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_above = PaneId::Terminal(2); + let pane_id_below = PaneId::Terminal(4); + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id_above, vec![pane_id_below]) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_on_top_of_stack( +) { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(3), + Dimension::percent(33.3), + 33, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::fixed(1), + 1, + 0, + 99, + Some(4), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_above = PaneId::Terminal(2); + let pane_id_below = PaneId::Terminal(3); + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id_above, vec![pane_id_below]) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_vertically_aligned_panes_to_stack_when_lower_pane_is_stacked_and_flexible_pane_is_mid_stack( +) { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::percent(33.3), + 32, + 0, + 67, + Some(4), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(5), + Dimension::fixed(1), + 1, + 0, + 99, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_above = PaneId::Terminal(2); + let pane_id_below = PaneId::Terminal(4); + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id_above, vec![pane_id_below]) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_vertically_aligned_panes_to_stack_when_both_are_stacked() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_stacked_pane_with_id!( + PaneId::Terminal(1), + Dimension::percent(50.0), + 49, + 0, + 0, + Some(1), + mock_panes, + 0 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(2), + Dimension::fixed(1), + 1, + 0, + 49, + Some(2), + mock_panes, + 0 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + 0, + 50, + Some(3), + mock_panes, + 1 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(4), + Dimension::percent(50.0), + 48, + 0, + 51, + Some(4), + mock_panes, + 1 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(5), + Dimension::fixed(1), + 1, + 0, + 99, + Some(5), + mock_panes, + 1 + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_above = PaneId::Terminal(2); + let pane_id_below = PaneId::Terminal(4); + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&pane_id_above, vec![pane_id_below]) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_vertically_aligned_panes_to_stack_with_multiple_non_stacked_neighbors() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane_with_cols!( + PaneId::Terminal(1), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane_with_cols!( + PaneId::Terminal(2), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 50, + 0, + Some(2), + mock_panes + ); + + mock_stacked_pane_with_id!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + 0, + 50, + Some(3), + mock_panes, + 1 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(4), + Dimension::percent(50.0), + 48, + 0, + 51, + Some(4), + mock_panes, + 1 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(5), + Dimension::fixed(1), + 1, + 0, + 99, + Some(5), + mock_panes, + 1 + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let root_pane_id = PaneId::Terminal(4); + let pane_ids_above = vec![PaneId::Terminal(1), PaneId::Terminal(2)]; + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&root_pane_id, pane_ids_above) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_vertically_aligned_panes_to_stack_with_multiple_stacked_neighbors() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane_with_cols!( + PaneId::Terminal(1), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 0, + 0, + Some(1), + mock_panes + ); + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(2), + Dimension::percent(50.0), + 49, + Dimension::percent(50.0), + 50, + 50, + 0, + Some(2), + mock_panes, + 2 + ); + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + Dimension::percent(50.0), + 50, + 50, + 49, + Some(3), + mock_panes, + 2 + ); + + mock_stacked_pane_with_id!( + PaneId::Terminal(4), + Dimension::fixed(1), + 1, + 0, + 50, + Some(4), + mock_panes, + 1 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(5), + Dimension::percent(50.0), + 48, + 0, + 51, + Some(5), + mock_panes, + 1 + ); + mock_stacked_pane_with_id!( + PaneId::Terminal(6), + Dimension::fixed(1), + 1, + 0, + 99, + Some(6), + mock_panes, + 1 + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let root_pane_id = PaneId::Terminal(5); + let pane_ids_above = vec![PaneId::Terminal(1), PaneId::Terminal(2)]; + + StackedPanes::new(mock_panes.clone()) + .combine_vertically_aligned_panes_to_stack(&root_pane_id, pane_ids_above) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_horizontally_aligned_panes_to_stack() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane_with_cols!( + PaneId::Terminal(1), + Dimension::percent(100.0), + 100, + Dimension::percent(50.0), + 50, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane_with_cols!( + PaneId::Terminal(2), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 50, + 0, + Some(2), + mock_panes + ); + mock_pane_with_cols!( + PaneId::Terminal(3), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 50, + 50, + Some(3), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_of_main_stack = PaneId::Terminal(1); + let neighboring_pane_ids = vec![PaneId::Terminal(2), PaneId::Terminal(3)]; + + StackedPanes::new(mock_panes.clone()) + .combine_horizontally_aligned_panes_to_stack(&pane_id_of_main_stack, neighboring_pane_ids) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_horizontally_aligned_panes_to_stack_when_left_pane_is_stacked() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(1), + Dimension::percent(100.0), + 98, + Dimension::percent(50.0), + 50, + 0, + 0, + Some(1), + mock_panes, + 0 + ); + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(2), + Dimension::fixed(1), + 1, + Dimension::percent(50.0), + 50, + 0, + 98, + Some(2), + mock_panes, + 0 + ); + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + Dimension::percent(50.0), + 50, + 0, + 99, + Some(3), + mock_panes, + 0 + ); + + mock_pane_with_cols!( + PaneId::Terminal(4), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 50, + 0, + Some(4), + mock_panes + ); + mock_pane_with_cols!( + PaneId::Terminal(5), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 50, + 50, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_of_main_stack = PaneId::Terminal(1); + let neighboring_pane_ids = vec![PaneId::Terminal(4), PaneId::Terminal(5)]; + + StackedPanes::new(mock_panes.clone()) + .combine_horizontally_aligned_panes_to_stack(&pane_id_of_main_stack, neighboring_pane_ids) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn combine_horizontally_aligned_panes_to_stack_when_right_pane_is_stacked() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(1), + Dimension::percent(100.0), + 98, + Dimension::percent(50.0), + 50, + 50, + 0, + Some(1), + mock_panes, + 0 + ); + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(2), + Dimension::fixed(1), + 1, + Dimension::percent(50.0), + 50, + 50, + 98, + Some(2), + mock_panes, + 0 + ); + mock_stacked_pane_with_cols_and_id!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + Dimension::percent(50.0), + 50, + 50, + 99, + Some(3), + mock_panes, + 0 + ); + + mock_pane_with_cols!( + PaneId::Terminal(4), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 0, + 0, + Some(4), + mock_panes + ); + mock_pane_with_cols!( + PaneId::Terminal(5), + Dimension::percent(50.0), + 50, + Dimension::percent(50.0), + 50, + 0, + 50, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let pane_id_of_main_stack = PaneId::Terminal(1); + let neighboring_pane_ids = vec![PaneId::Terminal(4), PaneId::Terminal(5)]; + + StackedPanes::new(mock_panes.clone()) + .combine_horizontally_aligned_panes_to_stack(&pane_id_of_main_stack, neighboring_pane_ids) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn break_pane_out_of_stack_top() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(3), + Dimension::percent(33.3), + 1, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::fixed(1), + 32, + 0, + 67, + Some(4), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(5), + Dimension::fixed(1), + 1, + 0, + 99, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let focused_pane = PaneId::Terminal(3); + + // here the bottom pane should be broken out because the focused pane is the top one and should + // remain in the stack + StackedPanes::new(mock_panes.clone()) + .break_pane_out_of_stack(&focused_pane) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn break_pane_out_of_stack_middle() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(3), + Dimension::fixed(1), + 1, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::percent(33.3), + 32, + 0, + 67, + Some(4), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(5), + Dimension::fixed(1), + 1, + 0, + 99, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let focused_pane = PaneId::Terminal(4); + + // here the bottom pane should be broken out (default behavior) + StackedPanes::new(mock_panes.clone()) + .break_pane_out_of_stack(&focused_pane) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn break_pane_out_of_stack_bottom() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(3), + Dimension::fixed(1), + 32, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::fixed(1), + 1, + 0, + 98, + Some(4), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(5), + Dimension::percent(33.3), + 1, + 0, + 99, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let focused_pane = PaneId::Terminal(5); + + // here the top pane should be broken out, because the focused pane is the bottom one and it + // should remain in the stack + StackedPanes::new(mock_panes.clone()) + .break_pane_out_of_stack(&focused_pane) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +#[test] +fn break_next_to_last_pane_out_of_stack() { + let mut mock_panes: HashMap> = HashMap::new(); + + mock_pane!( + PaneId::Terminal(1), + Dimension::percent(33.3), + 33, + 0, + 0, + Some(1), + mock_panes + ); + mock_pane!( + PaneId::Terminal(2), + Dimension::percent(33.3), + 33, + 0, + 33, + Some(2), + mock_panes + ); + mock_pane!( + PaneId::Terminal(3), + Dimension::percent(22.1), + 22, + 0, + 66, + Some(3), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(4), + Dimension::percent(11.2), + 11, + 0, + 88, + Some(4), + mock_panes + ); + mock_stacked_pane!( + PaneId::Terminal(5), + Dimension::fixed(1), + 1, + 0, + 99, + Some(5), + mock_panes + ); + + let mock_panes = Rc::new(RefCell::new(mock_panes)); + let focused_pane = PaneId::Terminal(4); + + StackedPanes::new(mock_panes.clone()) + .break_pane_out_of_stack(&focused_pane) + .unwrap(); + let mut pane_geoms_after: Vec = mock_panes + .borrow() + .values() + .map(|p| p.current_geom()) + .collect(); + pane_geoms_after.sort_by(|a, b| a.logical_position.cmp(&b.logical_position)); + assert_snapshot!(format!("{:#?}", pane_geoms_after)); +} + +struct MockPane { + pane_geom: PaneGeom, +} + +impl MockPane { + pub fn new(pane_geom: PaneGeom) -> Self { + MockPane { pane_geom } + } +} + +impl Pane for MockPane { + fn x(&self) -> usize { + unimplemented!() + } + fn y(&self) -> usize { + unimplemented!() + } + fn rows(&self) -> usize { + unimplemented!() + } + fn cols(&self) -> usize { + unimplemented!() + } + fn get_content_x(&self) -> usize { + unimplemented!() + } + fn get_content_y(&self) -> usize { + unimplemented!() + } + fn get_content_columns(&self) -> usize { + unimplemented!() + } + fn get_content_rows(&self) -> usize { + unimplemented!() + } + fn reset_size_and_position_override(&mut self) { + unimplemented!() + } + fn set_geom(&mut self, position_and_size: PaneGeom) { + self.pane_geom = position_and_size; + } + fn set_geom_override(&mut self, _pane_geom: PaneGeom) { + unimplemented!() + } + fn handle_pty_bytes(&mut self, _bytes: VteBytes) { + unimplemented!() + } + fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) { + unimplemented!() + } + fn cursor_coordinates(&self) -> Option<(usize, usize)> { + unimplemented!() + } + + fn position_and_size(&self) -> PaneGeom { + self.pane_geom.clone() + } + fn current_geom(&self) -> PaneGeom { + self.pane_geom.clone() + } + + fn geom_override(&self) -> Option { + unimplemented!() + } + fn should_render(&self) -> bool { + unimplemented!() + } + fn set_should_render(&mut self, _should_render: bool) { + unimplemented!() + } + fn set_should_render_boundaries(&mut self, _should_render: bool) { + unimplemented!() + } + fn selectable(&self) -> bool { + unimplemented!() + } + fn set_selectable(&mut self, _selectable: bool) { + unimplemented!() + } + + fn render( + &mut self, + _client_id: Option, + ) -> Result, Option, Vec)>> { + unimplemented!() + } + fn render_frame( + &mut self, + _client_id: ClientId, + _frame_params: FrameParams, + _input_mode: InputMode, + ) -> Result, Option)>> { + unimplemented!() + } + fn render_fake_cursor( + &mut self, + _cursor_color: PaletteColor, + _text_color: PaletteColor, + ) -> Option { + unimplemented!() + } + fn render_terminal_title(&mut self, _input_mode: InputMode) -> String { + unimplemented!() + } + fn update_name(&mut self, _name: &str) { + unimplemented!() + } + fn pid(&self) -> PaneId { + unimplemented!() + } + fn reduce_height(&mut self, _percent: f64) { + unimplemented!() + } + fn increase_height(&mut self, _percent: f64) { + unimplemented!() + } + fn reduce_width(&mut self, _percent: f64) { + unimplemented!() + } + fn increase_width(&mut self, _percent: f64) { + unimplemented!() + } + fn push_down(&mut self, _count: usize) { + unimplemented!() + } + fn push_right(&mut self, _count: usize) { + unimplemented!() + } + fn pull_left(&mut self, _count: usize) { + unimplemented!() + } + fn pull_up(&mut self, _count: usize) { + unimplemented!() + } + fn clear_screen(&mut self) { + unimplemented!() + } + fn scroll_up(&mut self, _count: usize, _client_id: ClientId) { + unimplemented!() + } + fn scroll_down(&mut self, _count: usize, _client_id: ClientId) { + unimplemented!() + } + fn clear_scroll(&mut self) { + unimplemented!() + } + fn is_scrolled(&self) -> bool { + unimplemented!() + } + fn active_at(&self) -> Instant { + unimplemented!() + } + fn set_active_at(&mut self, _instant: Instant) { + unimplemented!() + } + fn set_frame(&mut self, _frame: bool) { + unimplemented!() + } + fn set_content_offset(&mut self, _offset: Offset) { + unimplemented!() + } + fn store_pane_name(&mut self) { + unimplemented!() + } + fn load_pane_name(&mut self) { + unimplemented!() + } + fn set_borderless(&mut self, _borderless: bool) { + unimplemented!() + } + fn borderless(&self) -> bool { + unimplemented!() + } + fn set_exclude_from_sync(&mut self, _exclude_from_sync: bool) { + unimplemented!() + } + fn exclude_from_sync(&self) -> bool { + unimplemented!() + } + + fn add_red_pane_frame_color_override(&mut self, _error_text: Option) { + unimplemented!() + } + fn clear_pane_frame_color_override(&mut self) { + unimplemented!() + } + fn frame_color_override(&self) -> Option { + unimplemented!() + } + fn invoked_with(&self) -> &Option { + unimplemented!() + } + fn set_title(&mut self, _title: String) { + unimplemented!() + } + fn current_title(&self) -> String { + unimplemented!() + } + fn custom_title(&self) -> Option { + unimplemented!() + } +} diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 27de5fa0d3..a535f1e1f2 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -367,6 +367,7 @@ pub enum ScreenInstruction { auto_layout: bool, rounded_corners: bool, hide_session_name: bool, + stacked_resize: bool, }, RerunCommandPane(u32), // u32 - terminal pane id ResizePaneWithId(ResizeStrategy, PaneId), @@ -655,6 +656,7 @@ pub(crate) struct Screen { size: Size, pixel_dimensions: PixelDimensions, character_cell_size: Rc>>, + stacked_resize: Rc>, sixel_image_store: Rc>, /// The overlay that is drawn on top of [`Pane`]'s', [`Tab`]'s and the [`Screen`] overlay: OverlayWindow, @@ -711,6 +713,7 @@ impl Screen { arrow_fonts: bool, layout_dir: Option, explicitly_disable_kitty_keyboard_protocol: bool, + stacked_resize: bool, ) -> Self { let session_name = mode_info.session_name.clone().unwrap_or_default(); let session_info = SessionInfo::new(session_name.clone()); @@ -723,6 +726,7 @@ impl Screen { size: client_attributes.size, pixel_dimensions: Default::default(), character_cell_size: Rc::new(RefCell::new(None)), + stacked_resize: Rc::new(RefCell::new(stacked_resize)), sixel_image_store: Rc::new(RefCell::new(SixelImageStore::default())), style: client_attributes.style, connected_clients: Rc::new(RefCell::new(HashSet::new())), @@ -1326,6 +1330,7 @@ impl Screen { tab_name, self.size, self.character_cell_size.clone(), + self.stacked_resize.clone(), self.sixel_image_store.clone(), self.bus .os_input @@ -2428,6 +2433,7 @@ impl Screen { auto_layout: bool, rounded_corners: bool, hide_session_name: bool, + stacked_resize: bool, client_id: ClientId, ) -> Result<()> { let should_support_arrow_fonts = !simplified_ui; @@ -2445,6 +2451,9 @@ impl Screen { .update_arrow_fonts(should_support_arrow_fonts); self.default_mode_info .update_hide_session_name(hide_session_name); + { + *self.stacked_resize.borrow_mut() = stacked_resize; + } if let Some(copy_to_clipboard) = copy_to_clipboard { self.copy_options.clipboard = copy_to_clipboard; } @@ -2747,6 +2756,7 @@ pub(crate) fn screen_thread_main( // explicitly_disable_kitty_keyboard_protocol is false and vice versa .unwrap_or(false); // by default, we try to support this if the terminal supports it and // the program running inside a pane requests it + let stacked_resize = config_options.stacked_resize.unwrap_or(true); let thread_senders = bus.senders.clone(); let mut screen = Screen::new( @@ -2778,6 +2788,7 @@ pub(crate) fn screen_thread_main( arrow_fonts, layout_dir, explicitly_disable_kitty_keyboard_protocol, + stacked_resize, ); let mut pending_tab_ids: HashSet = HashSet::new(); @@ -4493,6 +4504,7 @@ pub(crate) fn screen_thread_main( auto_layout, rounded_corners, hide_session_name, + stacked_resize, } => { screen .reconfigure( @@ -4508,6 +4520,7 @@ pub(crate) fn screen_thread_main( auto_layout, rounded_corners, hide_session_name, + stacked_resize, client_id, ) .non_fatal(); diff --git a/zellij-server/src/tab/layout_applier.rs b/zellij-server/src/tab/layout_applier.rs index ed80c39e7b..70da914bb7 100644 --- a/zellij-server/src/tab/layout_applier.rs +++ b/zellij-server/src/tab/layout_applier.rs @@ -1000,7 +1000,7 @@ impl<'a> PaneApplier<'a> { ) { for pane_id in remaining_pane_ids { if let Some(pane) = existing_tab_state.remove_pane(&pane_id) { - self.tiled_panes.insert_pane(pane.pid(), pane); + self.tiled_panes.insert_pane(pane.pid(), pane, None); } } } diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 489c902e2f..e785e32e84 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -157,6 +157,7 @@ pub(crate) struct Tab { viewport: Rc>, // includes all non-UI panes display_area: Rc>, // includes all panes (including eg. the status bar and tab bar in the default layout) character_cell_size: Rc>>, + stacked_resize: Rc>, sixel_image_store: Rc>, os_api: Box, pub senders: ThreadSenders, @@ -564,6 +565,7 @@ impl Tab { name: String, display_area: Size, character_cell_size: Rc>>, + stacked_resize: Rc>, sixel_image_store: Rc>, os_api: Box, senders: ThreadSenders, @@ -608,6 +610,7 @@ impl Tab { connected_clients_in_app.clone(), mode_info.clone(), character_cell_size.clone(), + stacked_resize.clone(), session_is_mirrored, draw_pane_frames, default_mode_info.clone(), @@ -647,6 +650,7 @@ impl Tab { viewport, display_area, character_cell_size, + stacked_resize, sixel_image_store, synchronize_is_active: false, os_api, @@ -4256,9 +4260,10 @@ impl Tab { if should_auto_layout { // no need to relayout here, we'll do it when reapplying the swap layout // below - self.tiled_panes.insert_pane_without_relayout(pane_id, pane); + self.tiled_panes + .insert_pane_without_relayout(pane_id, pane, client_id); } else { - self.tiled_panes.insert_pane(pane_id, pane); + self.tiled_panes.insert_pane(pane_id, pane, client_id); } self.set_should_clear_display_before_rendering(); if let Some(client_id) = client_id { @@ -4346,7 +4351,10 @@ impl Tab { self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" in case of a decrease } } else if self.tiled_panes.panes_contain(&pane_id) { - match self.tiled_panes.resize_pane_with_id(strategy, pane_id) { + match self + .tiled_panes + .resize_pane_with_id(strategy, pane_id, None) + { Ok(_) => {}, Err(err) => match err.downcast_ref::() { Some(ZellijError::CantResizeFixedPanes { pane_ids }) => { diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-10.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-10.snap new file mode 100644 index 0000000000..72e2e25e4a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-10.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 830 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-11.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-11.snap new file mode 100644 index 0000000000..bdf8aeba45 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-11.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 830 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +08 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +09 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-12.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-12.snap new file mode 100644 index 0000000000..c75888b854 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-12.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 830 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +21 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-13.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-13.snap new file mode 100644 index 0000000000..2791de733c --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-13.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 830 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +21 (C): │ ││ ││ │ +22 (C): │ ││ ││ │ +23 (C): │ ││ ││ │ +24 (C): │ ││ ││ │ +25 (C): │ ││ ││ │ +26 (C): │ ││ ││ │ +27 (C): │ ││ ││ │ +28 (C): │ ││ ││ │ +29 (C): │ ││ ││ │ +30 (C): │ ││ ││ │ +31 (C): │ ││ ││ │ +32 (C): │ ││ ││ │ +33 (C): │ ││ ││ │ +34 (C): │ ││ ││ │ +35 (C): │ ││ ││ │ +36 (C): │ ││ ││ │ +37 (C): │ ││ ││ │ +38 (C): │ ││ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-2.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-2.snap new file mode 100644 index 0000000000..63e6071839 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-2.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 815 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +08 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +09 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-3.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-3.snap new file mode 100644 index 0000000000..5ec920202a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-3.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 816 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-4.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-4.snap new file mode 100644 index 0000000000..745171194d --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-4.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 816 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-5.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-5.snap new file mode 100644 index 0000000000..41d4dd7df3 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-5.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 816 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): ┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): ┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): │ │ +20 (C): │ │ +21 (C): │ │ +22 (C): │ │ +23 (C): │ │ +24 (C): │ │ +25 (C): │ │ +26 (C): │ │ +27 (C): │ │ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-6.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-6.snap new file mode 100644 index 0000000000..5b2bd36535 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-6.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 816 +expression: snapshot +--- +00 (C): ┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): │ │ +20 (C): │ │ +21 (C): │ │ +22 (C): │ │ +23 (C): │ │ +24 (C): │ │ +25 (C): │ │ +26 (C): │ │ +27 (C): │ │ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-7.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-7.snap new file mode 100644 index 0000000000..5b2bd36535 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-7.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 816 +expression: snapshot +--- +00 (C): ┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): │ │ +20 (C): │ │ +21 (C): │ │ +22 (C): │ │ +23 (C): │ │ +24 (C): │ │ +25 (C): │ │ +26 (C): │ │ +27 (C): │ │ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-8.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-8.snap new file mode 100644 index 0000000000..cfaef23fa8 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-8.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 830 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): ┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): ┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): ┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): │ │ +20 (C): │ │ +21 (C): │ │ +22 (C): │ │ +23 (C): │ │ +24 (C): │ │ +25 (C): │ │ +26 (C): │ │ +27 (C): │ │ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-9.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-9.snap new file mode 100644 index 0000000000..44ca1ec38c --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes-9.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 830 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes.snap new file mode 100644 index 0000000000..96033b80c8 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 815 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +21 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-2.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-2.snap new file mode 100644 index 0000000000..ce7bf0a327 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-2.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 875 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-3.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-3.snap new file mode 100644 index 0000000000..dd64752539 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-3.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 889 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ │└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ +10 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-4.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-4.snap new file mode 100644 index 0000000000..09aa9d094d --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes-4.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 889 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ ││ │└────────────────────────────────────────────────┘ +10 (C): │ ││ │┌ Pane #5 ───────────────────────────────────────┐ +11 (C): │ ││ ││ │ +12 (C): │ ││ ││ │ +13 (C): │ ││ ││ │ +14 (C): │ ││ ││ │ +15 (C): │ ││ ││ │ +16 (C): │ ││ ││ │ +17 (C): │ ││ ││ │ +18 (C): │ ││ ││ │ +19 (C): │ │└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes.snap new file mode 100644 index 0000000000..52c18be6fb --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 875 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ │└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ +10 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-2.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-2.snap new file mode 100644 index 0000000000..a1f5898b6f --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-2.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 7598 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-3.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-3.snap new file mode 100644 index 0000000000..f8b8e4b3cd --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-3.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 7598 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +21 (C): │ ││ ││ │ +22 (C): │ ││ ││ │ +23 (C): │ ││ ││ │ +24 (C): │ ││ ││ │ +25 (C): │ ││ ││ │ +26 (C): │ ││ ││ │ +27 (C): │ ││ ││ │ +28 (C): │ ││ ││ │ +29 (C): │ ││ ││ │ +30 (C): │ ││ ││ │ +31 (C): │ ││ ││ │ +32 (C): │ ││ ││ │ +33 (C): │ ││ ││ │ +34 (C): │ ││ ││ │ +35 (C): │ ││ ││ │ +36 (C): │ ││ ││ │ +37 (C): │ ││ ││ │ +38 (C): │ ││ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-4.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-4.snap new file mode 100644 index 0000000000..b375c4cdfb --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes-4.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 7598 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #3 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +21 (C): │ ││ │┌ Pane #5 ───────────────────────────────────────┐ +22 (C): │ ││ ││ │ +23 (C): │ ││ ││ │ +24 (C): │ ││ ││ │ +25 (C): │ ││ ││ │ +26 (C): │ ││ ││ │ +27 (C): │ ││ ││ │ +28 (C): │ ││ ││ │ +29 (C): │ ││ ││ │ +30 (C): │ ││ ││ │ +31 (C): │ ││ ││ │ +32 (C): │ ││ ││ │ +33 (C): │ ││ ││ │ +34 (C): │ ││ ││ │ +35 (C): │ ││ ││ │ +36 (C): │ ││ ││ │ +37 (C): │ ││ ││ │ +38 (C): │ ││ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes.snap new file mode 100644 index 0000000000..970d1f91e7 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_stacked_resizes.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 7598 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__split_stack_horizontally.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__split_stack_horizontally.snap new file mode 100644 index 0000000000..fb172dc52d --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__split_stack_horizontally.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 970 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ │└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +20 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────────────────────────────────────────────┐ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): │ ││ │ +28 (C): │ ││ │ +29 (C): │ ││ │ +30 (C): │ ││ │ +31 (C): │ ││ │ +32 (C): │ ││ │ +33 (C): │ ││ │ +34 (C): │ ││ │ +35 (C): │ ││ │ +36 (C): │ ││ │ +37 (C): │ ││ │ +38 (C): │ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__split_stack_vertically.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__split_stack_vertically.snap new file mode 100644 index 0000000000..f4826470bc --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__split_stack_vertically.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 931 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ───────────────────────────────────────┐│ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ ││ ││ │ +10 (C): │ ││ ││ │ +11 (C): │ ││ ││ │ +12 (C): │ ││ ││ │ +13 (C): │ ││ ││ │ +14 (C): │ ││ ││ │ +15 (C): │ ││ ││ │ +16 (C): │ ││ ││ │ +17 (C): │ ││ ││ │ +18 (C): │ ││ ││ │ +19 (C): │ ││ ││ │ +20 (C): │ ││ ││ │ +21 (C): │ ││ ││ │ +22 (C): │ ││ ││ │ +23 (C): │ ││ ││ │ +24 (C): │ ││ ││ │ +25 (C): │ ││ ││ │ +26 (C): │ ││ ││ │ +27 (C): │ ││ ││ │ +28 (C): │ ││ ││ │ +29 (C): │ ││ ││ │ +30 (C): │ ││ ││ │ +31 (C): │ ││ ││ │ +32 (C): │ ││ ││ │ +33 (C): │ ││ ││ │ +34 (C): │ ││ ││ │ +35 (C): │ ││ ││ │ +36 (C): │ ││ ││ │ +37 (C): │ ││ ││ │ +38 (C): │ ││ ││ │ +39 (C): └──────────────────────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 74c2d4d99a..472a8b686c 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -220,6 +220,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(true)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); @@ -234,6 +235,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -279,6 +281,7 @@ fn create_new_tab_with_swap_layouts( HashMap>, )>, draw_pane_frames: bool, + stacked_resize: bool, ) -> Tab { set_session_name("test".into()); let index = 0; @@ -299,6 +302,7 @@ fn create_new_tab_with_swap_layouts( connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(stacked_resize)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); @@ -313,6 +317,7 @@ fn create_new_tab_with_swap_layouts( name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -380,6 +385,7 @@ fn create_new_tab_with_os_api( connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(true)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); @@ -394,6 +400,7 @@ fn create_new_tab_with_os_api( name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -445,6 +452,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(true)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); @@ -461,6 +469,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -528,6 +537,7 @@ fn create_new_tab_with_mock_pty_writer( connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(true)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); @@ -542,6 +552,7 @@ fn create_new_tab_with_mock_pty_writer( name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -601,6 +612,7 @@ fn create_new_tab_with_sixel_support( width: 8, height: 21, }))); + let stacked_resize = Rc::new(RefCell::new(true)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); @@ -614,6 +626,7 @@ fn create_new_tab_with_sixel_support( name, size, character_cell_size, + stacked_resize, sixel_image_store, os_api, senders, @@ -765,6 +778,202 @@ fn take_snapshot_and_cursor_position( (format!("{:?}", grid), grid.cursor_coordinates()) } +#[test] +fn increase_tiled_pane_sizes_with_stacked_resizes() { + // this is the default resizing algorithm + let size = Size { + cols: 200, + rows: 40, + }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + let mut output = Output::default(); + for i in 2..5 { + let new_pane_id_1 = PaneId::Terminal(i); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + } + + // first we increase until fullscreen and once more to make sure we don't increase beyond it + for _ in 0..=6 { + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.render(&mut output).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); + } + + // then we decrease until the original position + for _ in 0..=5 { + tab.resize(client_id, ResizeStrategy::new(Resize::Decrease, None)) + .unwrap(); + tab.render(&mut output).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); + } +} + +#[test] +fn increase_tiled_pane_sizes_with_stacked_resizes_into_uneven_panes() { + // this is the default resizing algorithm + let size = Size { + cols: 200, + rows: 40, + }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + let mut output = Output::default(); + for i in 2..4 { + let new_pane_id_1 = PaneId::Terminal(i); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + } + tab.move_focus_up(client_id).unwrap(); + tab.vertical_split(PaneId::Terminal(4), None, client_id) + .unwrap(); + tab.move_focus_right(client_id).unwrap(); + tab.horizontal_split(PaneId::Terminal(5), None, client_id) + .unwrap(); + tab.move_focus_down(client_id).unwrap(); + + // increase twice, once to add the short pane into the stack and shorten the larger one, and + // once to add the remaining two panes (the one we shortened and the extra one near it) + for _ in 0..2 { + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.render(&mut output).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + eprintln!("increase: \n{}", snapshot); + assert_snapshot!(snapshot); + } + + // then we decrease until the original position + for _ in 0..2 { + tab.resize(client_id, ResizeStrategy::new(Resize::Decrease, None)) + .unwrap(); + tab.render(&mut output).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + eprintln!("decrease: \n{}", snapshot); + assert_snapshot!(snapshot); + } +} + +#[test] +fn split_stack_vertically() { + let size = Size { + cols: 200, + rows: 40, + }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + let mut output = Output::default(); + for i in 2..4 { + let new_pane_id_1 = PaneId::Terminal(i); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + } + // the below resizes will end up stacking the panes + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.vertical_split(PaneId::Terminal(4), None, client_id) + .unwrap(); + + tab.render(&mut output).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn split_stack_horizontally() { + let size = Size { + cols: 200, + rows: 40, + }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + let mut output = Output::default(); + for i in 2..4 { + let new_pane_id_1 = PaneId::Terminal(i); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + } + // the below resizes will end up stacking the panes + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.horizontal_split(PaneId::Terminal(4), None, client_id) + .unwrap(); + + tab.render(&mut output).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + #[test] fn dump_screen() { let size = Size { @@ -4039,12 +4248,14 @@ fn can_swap_tiled_layout_at_runtime() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); @@ -4100,12 +4311,14 @@ fn can_swap_floating_layout_at_runtime() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4171,12 +4384,14 @@ fn swapping_layouts_after_resize_snaps_to_current_layout() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); @@ -4227,12 +4442,14 @@ fn swap_tiled_layout_with_stacked_children() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4298,12 +4515,14 @@ fn swap_tiled_layout_with_only_stacked_children() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4372,12 +4591,14 @@ fn swap_tiled_layout_with_stacked_children_and_no_pane_frames() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, false, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4446,12 +4667,14 @@ fn move_focus_up_with_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4522,12 +4745,14 @@ fn move_focus_down_with_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4602,12 +4827,14 @@ fn move_focus_right_into_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); for i in 0..12 { let new_pane_id = i + 2; @@ -4671,12 +4898,14 @@ fn move_focus_left_into_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); for i in 0..13 { let new_pane_id = i + 2; @@ -4742,12 +4971,14 @@ fn move_focus_up_into_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); for i in 0..4 { let new_pane_id = i + 3; @@ -4814,12 +5045,14 @@ fn move_focus_down_into_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); for i in 0..4 { let new_pane_id = i + 3; @@ -4880,12 +5113,14 @@ fn close_main_stacked_pane() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -4955,12 +5190,14 @@ fn close_main_stacked_pane_in_mid_stack() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5055,12 +5292,14 @@ fn close_one_liner_stacked_pane_below_main_pane() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5156,12 +5395,14 @@ fn close_one_liner_stacked_pane_above_main_pane() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5256,12 +5497,14 @@ fn can_increase_size_of_main_pane_in_stack_horizontally() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5360,12 +5603,14 @@ fn can_increase_size_of_main_pane_in_stack_vertically() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5464,12 +5709,14 @@ fn can_increase_size_of_main_pane_in_stack_non_directionally() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = false; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5563,12 +5810,14 @@ fn can_increase_size_into_pane_stack_horizontally() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5666,12 +5915,14 @@ fn can_increase_size_into_pane_stack_vertically() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5748,6 +5999,7 @@ fn can_increase_size_into_pane_stack_vertically() { #[test] fn can_increase_size_into_pane_stack_non_directionally() { + // note - this is not the default behavior, by default stacked_resize is enabled (set to true) let size = Size { cols: 121, rows: 40, @@ -5771,12 +6023,14 @@ fn can_increase_size_into_pane_stack_non_directionally() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = false; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5869,12 +6123,14 @@ fn decreasing_size_of_whole_tab_treats_stacked_panes_properly() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -5969,12 +6225,14 @@ fn increasing_size_of_whole_tab_treats_stacked_panes_properly() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6074,12 +6332,14 @@ fn cannot_decrease_stack_size_beyond_minimum_height() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6179,12 +6439,14 @@ fn focus_stacked_pane_over_flexible_pane_with_the_mouse() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6281,12 +6543,14 @@ fn focus_stacked_pane_under_flexible_pane_with_the_mouse() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6388,12 +6652,14 @@ fn close_stacked_pane_with_previously_focused_other_pane() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6501,12 +6767,14 @@ fn close_pane_near_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6605,12 +6873,14 @@ fn focus_next_pane_expands_stacked_panes() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6705,12 +6975,14 @@ fn stacked_panes_can_become_fullscreen() { let layout = Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), (swap_tiled_layouts, swap_floating_layouts), None, true, + stacked_resize, ); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -6852,6 +7124,7 @@ fn layout_with_plugins_and_commands_swaped_properly() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -6864,6 +7137,7 @@ fn layout_with_plugins_and_commands_swaped_properly() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -6948,6 +7222,7 @@ fn base_layout_is_included_in_swap_layouts() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -6960,6 +7235,7 @@ fn base_layout_is_included_in_swap_layouts() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -7041,6 +7317,7 @@ fn swap_layouts_including_command_panes_absent_from_existing_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7053,6 +7330,7 @@ fn swap_layouts_including_command_panes_absent_from_existing_layout() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -7137,6 +7415,7 @@ fn swap_layouts_not_including_command_panes_present_in_existing_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7149,6 +7428,7 @@ fn swap_layouts_not_including_command_panes_present_in_existing_layout() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -7217,6 +7497,7 @@ fn swap_layouts_including_plugin_panes_absent_from_existing_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7229,6 +7510,7 @@ fn swap_layouts_including_plugin_panes_absent_from_existing_layout() { new_plugin_ids, )), true, + stacked_resize, ); tab.next_swap_layout().unwrap(); tab.render(&mut output).unwrap(); @@ -7307,6 +7589,7 @@ fn swap_layouts_not_including_plugin_panes_present_in_existing_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7319,6 +7602,7 @@ fn swap_layouts_not_including_plugin_panes_present_in_existing_layout() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -7377,6 +7661,7 @@ fn new_pane_in_auto_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7389,6 +7674,7 @@ fn new_pane_in_auto_layout() { new_plugin_ids, )), true, + stacked_resize, ); let mut expected_cursor_coordinates = vec![ @@ -7430,6 +7716,77 @@ fn new_pane_in_auto_layout() { } } +#[test] +fn new_pane_in_stacked_resizes() { + let size = Size { + cols: 200, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, Some("file_name.kdl".into()), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_tiled_layouts = vec![]; + let swap_floating_layouts = vec![]; + let stacked_resize = true; + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + stacked_resize, + ); + + let mut expected_cursor_coordinates = vec![(101, 1), (101, 21), (151, 21), (151, 22)]; + for i in 0..4 { + let new_pane_id = i + 2; + tab.new_pane( + PaneId::Terminal(new_pane_id), + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.render(&mut output).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + eprintln!("{}", snapshot); + let (expected_x, expected_y) = expected_cursor_coordinates.remove(0); + assert_eq!( + cursor_coordinates, + Some((expected_x, expected_y)), + "cursor coordinates moved to the new pane", + ); + assert_snapshot!(snapshot); + } +} + #[test] fn when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged() { let size = Size { @@ -7472,6 +7829,7 @@ fn when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unch Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7484,6 +7842,7 @@ fn when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unch new_plugin_ids, )), true, + stacked_resize, ); tab.move_focus_down(client_id); tab.resize( @@ -7551,6 +7910,7 @@ fn when_swapping_tiled_layouts_in_an_undamaged_state_pane_focuses_on_focused_nod Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7563,6 +7923,7 @@ fn when_swapping_tiled_layouts_in_an_undamaged_state_pane_focuses_on_focused_nod new_plugin_ids, )), true, + stacked_resize, ); tab.move_focus_down(client_id); tab.next_swap_layout().unwrap(); @@ -7626,6 +7987,7 @@ fn when_swapping_tiled_layouts_in_an_undamaged_state_with_no_focus_node_pane_foc Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7638,6 +8000,7 @@ fn when_swapping_tiled_layouts_in_an_undamaged_state_with_no_focus_node_pane_foc new_plugin_ids, )), true, + stacked_resize, ); tab.move_focus_down(client_id); tab.move_focus_down(client_id); @@ -7701,6 +8064,7 @@ fn when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7713,6 +8077,7 @@ fn when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.move_focus_down(client_id); let _ = tab.move_focus_down(client_id); @@ -7800,6 +8165,7 @@ fn floating_layout_with_plugins_and_commands_swaped_properly() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7812,6 +8178,7 @@ fn floating_layout_with_plugins_and_commands_swaped_properly() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -7894,6 +8261,7 @@ fn base_floating_layout_is_included_in_swap_layouts() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7906,6 +8274,7 @@ fn base_floating_layout_is_included_in_swap_layouts() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -7987,6 +8356,7 @@ fn swap_floating_layouts_including_command_panes_absent_from_existing_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -7999,6 +8369,7 @@ fn swap_floating_layouts_including_command_panes_absent_from_existing_layout() { new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -8083,6 +8454,7 @@ fn swap_floating_layouts_not_including_command_panes_present_in_existing_layout( Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8095,6 +8467,7 @@ fn swap_floating_layouts_not_including_command_panes_present_in_existing_layout( new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -8156,6 +8529,7 @@ fn swap_floating_layouts_including_plugin_panes_absent_from_existing_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8168,6 +8542,7 @@ fn swap_floating_layouts_including_plugin_panes_absent_from_existing_layout() { new_plugin_ids, )), true, + stacked_resize, ); tab.next_swap_layout().unwrap(); tab.render(&mut output).unwrap(); @@ -8242,6 +8617,7 @@ fn swap_floating_layouts_not_including_plugin_panes_present_in_existing_layout() Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8254,6 +8630,7 @@ fn swap_floating_layouts_not_including_plugin_panes_present_in_existing_layout() new_plugin_ids, )), true, + stacked_resize, ); let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); @@ -8312,6 +8689,7 @@ fn new_floating_pane_in_auto_layout() { Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8324,6 +8702,7 @@ fn new_floating_pane_in_auto_layout() { new_plugin_ids, )), true, + stacked_resize, ); let mut expected_cursor_coordinates = vec![(62, 11), (62, 6), (31, 12)]; @@ -8400,6 +8779,7 @@ fn when_swapping_floating_layouts_in_a_damaged_state_layout_and_pane_focus_are_u Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8412,6 +8792,7 @@ fn when_swapping_floating_layouts_in_a_damaged_state_layout_and_pane_focus_are_u new_plugin_ids, )), true, + stacked_resize, ); tab.resize( client_id, @@ -8478,6 +8859,7 @@ fn when_swapping_floating_layouts_in_an_undamaged_state_pane_focuses_on_focused_ Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8490,6 +8872,7 @@ fn when_swapping_floating_layouts_in_an_undamaged_state_pane_focuses_on_focused_ new_plugin_ids, )), true, + stacked_resize, ); tab.next_swap_layout().unwrap(); tab.render(&mut output).unwrap(); @@ -8552,6 +8935,7 @@ fn when_swapping_floating_layouts_in_an_undamaged_state_with_no_focus_node_pane_ Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8564,6 +8948,7 @@ fn when_swapping_floating_layouts_in_an_undamaged_state_with_no_focus_node_pane_ new_plugin_ids, )), true, + stacked_resize, ); tab.next_swap_layout().unwrap(); tab.render(&mut output).unwrap(); @@ -8625,6 +9010,7 @@ fn when_closing_a_floating_pane_in_auto_layout_the_focus_goes_to_last_focused_pa Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8637,6 +9023,7 @@ fn when_closing_a_floating_pane_in_auto_layout_the_focus_goes_to_last_focused_pa new_plugin_ids, )), true, + stacked_resize, ); tab.move_focus_up(client_id); tab.move_focus_up(client_id); @@ -8691,6 +9078,7 @@ fn when_resizing_whole_tab_with_auto_layout_and_floating_panes_the_layout_is_mai Layout::from_kdl(swap_layouts, Some("file_name.kdl".into()), None, None).unwrap(); let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let stacked_resize = true; let mut tab = create_new_tab_with_swap_layouts( size, ModeInfo::default(), @@ -8703,6 +9091,7 @@ fn when_resizing_whole_tab_with_auto_layout_and_floating_panes_the_layout_is_mai new_plugin_ids, )), true, + stacked_resize, ); let new_size = Size { cols: 150, diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 438046fdeb..883029694f 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -143,7 +143,7 @@ fn tab_resize_right(tab: &mut Tab, id: ClientId) { .unwrap(); } -fn create_new_tab(size: Size) -> Tab { +fn create_new_tab(size: Size, stacked_resize: bool) -> Tab { let index = 0; let position = 0; let name = String::new(); @@ -158,6 +158,7 @@ fn create_new_tab(size: Size) -> Tab { let session_is_mirrored = true; let mut connected_clients = HashSet::new(); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(stacked_resize)); connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); @@ -174,6 +175,7 @@ fn create_new_tab(size: Size) -> Tab { name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -222,6 +224,7 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab { let session_is_mirrored = true; let mut connected_clients = HashSet::new(); let character_cell_info = Rc::new(RefCell::new(None)); + let stacked_resize = Rc::new(RefCell::new(true)); connected_clients.insert(client_id); let connected_clients = Rc::new(RefCell::new(connected_clients)); let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); @@ -238,6 +241,7 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab { name, size, character_cell_info, + stacked_resize, sixel_image_store, os_api, senders, @@ -302,12 +306,14 @@ fn create_new_tab_with_cell_size( let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let stacked_resize = Rc::new(RefCell::new(true)); let mut tab = Tab::new( index, position, name, size, character_cell_size, + stacked_resize, sixel_image_store, os_api, senders, @@ -347,7 +353,8 @@ fn write_to_suppressed_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); // Suppress pane 2 and remove it from active panes @@ -374,7 +381,8 @@ fn split_panes_vertically() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "The tab has two panes"); @@ -470,7 +478,8 @@ fn split_panes_horizontally() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "The tab has two panes"); @@ -568,7 +577,8 @@ fn split_largest_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) @@ -752,7 +762,8 @@ fn split_largest_pane() { #[test] pub fn cannot_split_panes_vertically_when_active_pane_is_too_small() { let size = Size { cols: 8, rows: 20 }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); assert_eq!( tab.tiled_panes.panes.len(), @@ -764,7 +775,8 @@ pub fn cannot_split_panes_vertically_when_active_pane_is_too_small() { #[test] pub fn cannot_split_panes_horizontally_when_active_pane_is_too_small() { let size = Size { cols: 121, rows: 4 }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); assert_eq!( tab.tiled_panes.panes.len(), @@ -776,7 +788,8 @@ pub fn cannot_split_panes_horizontally_when_active_pane_is_too_small() { #[test] pub fn cannot_split_largest_pane_when_there_is_no_room() { let size = Size { cols: 8, rows: 4 }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); assert_eq!( @@ -818,7 +831,8 @@ pub fn toggle_focused_pane_fullscreen() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) @@ -886,6 +900,82 @@ pub fn toggle_focused_pane_fullscreen() { // function and we already test that in the e2e tests } +#[test] +pub fn toggle_focused_pane_fullscreen_with_stacked_resizes() { + // note - this is the default + let size = Size { + cols: 121, + rows: 20, + }; + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); + for i in 2..5 { + let new_pane_id = PaneId::Terminal(i); + tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) + .unwrap(); + } + tab.toggle_active_pane_fullscreen(1); + assert_eq!( + tab.tiled_panes.panes.get(&PaneId::Terminal(4)).unwrap().x(), + 0, + "Pane x is on screen edge" + ); + assert_eq!( + tab.tiled_panes.panes.get(&PaneId::Terminal(4)).unwrap().y(), + 0, + "Pane y is on screen edge" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(4)) + .unwrap() + .cols(), + 121, + "Pane cols match fullscreen cols" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(4)) + .unwrap() + .rows(), + 20, + "Pane rows match fullscreen rows" + ); + tab.toggle_active_pane_fullscreen(1); + assert_eq!( + tab.tiled_panes.panes.get(&PaneId::Terminal(4)).unwrap().x(), + 61, + "Pane x is back to its original position" + ); + assert_eq!( + tab.tiled_panes.panes.get(&PaneId::Terminal(4)).unwrap().y(), + 2, + "Pane y is back to its original position" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(4)) + .unwrap() + .cols(), + 60, + "Pane cols are back at their original position" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(4)) + .unwrap() + .rows(), + 18, + "Pane rows are back at their original position" + ); + // we don't test if all other panes are hidden because this logic is done in the render + // function and we already test that in the e2e tests +} + #[test] fn switch_to_next_pane_fullscreen() { let size = Size { @@ -893,7 +983,8 @@ fn switch_to_next_pane_fullscreen() { rows: 20, }; - let mut active_tab = create_new_tab(size); + let stacked_resize = true; + let mut active_tab = create_new_tab(size, stacked_resize); active_tab .new_pane(PaneId::Terminal(1), None, None, None, None, false, Some(1)) @@ -931,7 +1022,8 @@ fn switch_to_prev_pane_fullscreen() { cols: 121, rows: 20, }; - let mut active_tab = create_new_tab(size); + let stacked_resize = true; + let mut active_tab = create_new_tab(size, stacked_resize); //testing four consecutive switches in fullscreen mode @@ -979,7 +1071,8 @@ pub fn close_pane_with_another_pane_above_it() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); tab.close_focused_pane(1).unwrap(); @@ -1044,7 +1137,8 @@ pub fn close_pane_with_another_pane_below_it() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -1107,7 +1201,8 @@ pub fn close_pane_with_another_pane_to_the_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); tab.close_focused_pane(1).unwrap(); @@ -1169,7 +1264,8 @@ pub fn close_pane_with_another_pane_to_the_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); tab.move_focus_left(1).unwrap(); @@ -1234,7 +1330,8 @@ pub fn close_pane_with_multiple_panes_above_it() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); tab.horizontal_split(new_pane_id_1, None, 1).unwrap(); @@ -1345,7 +1442,8 @@ pub fn close_pane_with_multiple_panes_below_it() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); tab.horizontal_split(new_pane_id_1, None, 1).unwrap(); @@ -1455,7 +1553,8 @@ pub fn close_pane_with_multiple_panes_to_the_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); tab.vertical_split(new_pane_id_1, None, 1).unwrap(); @@ -1566,7 +1665,8 @@ pub fn close_pane_with_multiple_panes_to_the_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); tab.vertical_split(new_pane_id_1, None, 1).unwrap(); @@ -1676,7 +1776,8 @@ pub fn close_pane_with_multiple_panes_above_it_away_from_screen_edges() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -1976,7 +2077,8 @@ pub fn close_pane_with_multiple_panes_below_it_away_from_screen_edges() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -2277,7 +2379,8 @@ pub fn close_pane_with_multiple_panes_to_the_left_away_from_screen_edges() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -2581,7 +2684,8 @@ pub fn close_pane_with_multiple_panes_to_the_right_away_from_screen_edges() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -2873,7 +2977,8 @@ pub fn move_focus_down() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); @@ -2893,7 +2998,8 @@ pub fn move_focus_down_to_the_most_recently_used_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -2922,7 +3028,8 @@ pub fn move_focus_up() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); @@ -2941,7 +3048,8 @@ pub fn move_focus_up_to_the_most_recently_used_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -2971,7 +3079,8 @@ pub fn move_focus_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); @@ -2990,7 +3099,8 @@ pub fn move_focus_left_to_the_most_recently_used_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -3020,7 +3130,8 @@ pub fn move_focus_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); @@ -3040,7 +3151,8 @@ pub fn move_focus_right_to_the_most_recently_used_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -3069,7 +3181,8 @@ pub fn move_active_pane_down() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); @@ -3094,7 +3207,8 @@ pub fn move_active_pane_down_to_the_most_recently_used_position() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -3128,7 +3242,8 @@ pub fn move_active_pane_up() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); @@ -3152,7 +3267,8 @@ pub fn move_active_pane_up_to_the_most_recently_used_position() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -3188,7 +3304,8 @@ pub fn move_active_pane_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); @@ -3212,7 +3329,8 @@ pub fn move_active_pane_left_to_the_most_recently_used_position() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -3248,7 +3366,8 @@ pub fn move_active_pane_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.vertical_split(new_pane_id, None, 1).unwrap(); @@ -3273,7 +3392,8 @@ pub fn move_active_pane_right_to_the_most_recently_used_position() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); @@ -3316,7 +3436,8 @@ pub fn resize_down_with_pane_above() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); tab_resize_down(&mut tab, 1); @@ -3422,7 +3543,8 @@ pub fn resize_down_with_pane_below() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -3533,7 +3655,8 @@ pub fn resize_down_with_panes_above_and_below() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let first_pane_id = PaneId::Terminal(1); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -3686,7 +3809,8 @@ pub fn resize_down_with_multiple_panes_above() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let first_pane_id = PaneId::Terminal(1); let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); @@ -3840,7 +3964,8 @@ pub fn resize_down_with_panes_above_aligned_left_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let pane_above_and_left = PaneId::Terminal(1); let pane_to_the_left = PaneId::Terminal(2); let focused_pane = PaneId::Terminal(3); @@ -4040,7 +4165,8 @@ pub fn resize_down_with_panes_below_aligned_left_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let pane_to_the_left = PaneId::Terminal(1); let pane_below_and_left = PaneId::Terminal(2); let pane_below = PaneId::Terminal(3); @@ -4239,7 +4365,8 @@ pub fn resize_down_with_panes_above_aligned_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let pane_above = PaneId::Terminal(1); let focused_pane = PaneId::Terminal(2); let pane_to_the_right = PaneId::Terminal(3); @@ -4440,7 +4567,8 @@ pub fn resize_down_with_panes_below_aligned_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); let focused_pane = PaneId::Terminal(1); let pane_below = PaneId::Terminal(2); let pane_below_and_right = PaneId::Terminal(3); @@ -4640,7 +4768,8 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -4925,7 +5054,8 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -5209,7 +5339,8 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_panes_to_the_lef cols: 122, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -5583,7 +5714,8 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_to_the_left_and_ cols: 122, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -5957,7 +6089,8 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() { cols: 121, rows: 10, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab_resize_down(&mut tab, 1); @@ -6137,7 +6270,8 @@ pub fn resize_left_with_pane_to_the_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab_resize_left(&mut tab, 1); @@ -6240,7 +6374,8 @@ pub fn resize_left_with_pane_to_the_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab_resize_left(&mut tab, 1); @@ -6345,7 +6480,8 @@ pub fn resize_left_with_panes_to_the_left_and_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); @@ -6493,7 +6629,8 @@ pub fn resize_left_with_multiple_panes_to_the_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -6643,7 +6780,8 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -6837,7 +6975,8 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -7032,7 +7171,8 @@ pub fn resize_left_with_panes_to_the_left_aligned_bottom_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -7225,7 +7365,8 @@ pub fn resize_left_with_panes_to_the_right_aligned_bottom_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -7421,7 +7562,8 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_current_pa cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -7706,7 +7848,8 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_current_p cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -7992,7 +8135,8 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abov cols: 121, rows: 70, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -8367,7 +8511,8 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_panes_abo cols: 121, rows: 70, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -8738,7 +8883,8 @@ pub fn cannot_resize_left_when_pane_to_the_left_is_at_minimum_width() { // █ == focused pane let size = Size { cols: 10, rows: 20 }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab_resize_left(&mut tab, 1); @@ -8779,7 +8925,8 @@ pub fn resize_right_with_pane_to_the_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab_resize_right(&mut tab, 1); @@ -8883,7 +9030,8 @@ pub fn resize_right_with_pane_to_the_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab_resize_right(&mut tab, 1); @@ -8988,7 +9136,8 @@ pub fn resize_right_with_panes_to_the_left_and_right() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); @@ -9137,7 +9286,8 @@ pub fn resize_right_with_multiple_panes_to_the_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -9287,7 +9437,8 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_with_current_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -9480,7 +9631,8 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_with_current_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -9675,7 +9827,8 @@ pub fn resize_right_with_panes_to_the_left_aligned_bottom_with_current_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -9870,7 +10023,8 @@ pub fn resize_right_with_panes_to_the_right_aligned_bottom_with_current_pane() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -10068,7 +10222,8 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_current_p cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -10352,7 +10507,8 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_current_ cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -10637,7 +10793,8 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abo cols: 121, rows: 70, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -11011,7 +11168,8 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_panes_ab cols: 121, rows: 70, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -11381,7 +11539,8 @@ pub fn cannot_resize_right_when_pane_to_the_left_is_at_minimum_width() { // └─┴─┘ └─┴─┘ // █ == focused pane let size = Size { cols: 10, rows: 20 }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab_resize_right(&mut tab, 1); @@ -11469,7 +11628,8 @@ pub fn resize_up_with_pane_above() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab_resize_up(&mut tab, 1); @@ -11574,7 +11734,8 @@ pub fn resize_up_with_pane_below() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab_resize_up(&mut tab, 1); @@ -11683,7 +11844,8 @@ pub fn resize_up_with_panes_above_and_below() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); @@ -11832,7 +11994,8 @@ pub fn resize_up_with_multiple_panes_above() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -11981,7 +12144,8 @@ pub fn resize_up_with_panes_above_aligned_left_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -12176,7 +12340,8 @@ pub fn resize_up_with_panes_below_aligned_left_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -12372,7 +12537,8 @@ pub fn resize_up_with_panes_above_aligned_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -12568,7 +12734,8 @@ pub fn resize_up_with_panes_below_aligned_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -12765,7 +12932,8 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -13048,7 +13216,8 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_current_pane() { cols: 121, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); @@ -13332,7 +13501,8 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_panes_to_the_left_ cols: 122, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -13705,7 +13875,8 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_to_the_left_and_ri cols: 122, rows: 30, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -14078,7 +14249,8 @@ pub fn cannot_resize_up_when_pane_above_is_at_minimum_height() { cols: 121, rows: 10, }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab_resize_down(&mut tab, 1); @@ -14112,7 +14284,31 @@ pub fn nondirectional_resize_increase_with_1_pane() { cols: 121, rows: 10, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); + tab_resize_increase(&mut tab, 1); + + assert_eq!( + tab.get_active_pane(1).unwrap().position_and_size().y, + 0, + "There is only 1 pane so both coordinates should be 0" + ); + + assert_eq!( + tab.get_active_pane(1).unwrap().position_and_size().x, + 0, + "There is only 1 pane so both coordinates should be 0" + ); +} + +#[test] +pub fn nondirectional_resize_increase_with_1_pane_with_stacked_resizes() { + let size = Size { + cols: 121, + rows: 10, + }; + let stacked_resize = true; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); tab_resize_increase(&mut tab, 1); assert_eq!( @@ -14134,7 +14330,8 @@ pub fn nondirectional_resize_increase_with_1_pane_to_left() { cols: 121, rows: 10, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); let new_pane_id_1 = PaneId::Terminal(2); tab.vertical_split(new_pane_id_1, None, 1).unwrap(); tab_resize_increase(&mut tab, 1); @@ -14168,7 +14365,8 @@ pub fn nondirectional_resize_increase_with_2_panes_to_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -14226,7 +14424,8 @@ pub fn nondirectional_resize_increase_with_1_pane_to_right_1_pane_above() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); @@ -14282,7 +14481,8 @@ pub fn nondirectional_resize_increase_with_1_pane_to_right_1_pane_to_left() { cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); @@ -14338,7 +14538,8 @@ pub fn nondirectional_resize_increase_with_pane_above_aligned_right_with_current cols: 121, rows: 20, }; - let mut tab = create_new_tab(size); + let stacked_resize = false; // note - this is not the default + let mut tab = create_new_tab(size, stacked_resize); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1).unwrap(); @@ -14419,7 +14620,8 @@ fn correctly_resize_frameless_panes_on_pane_close() { let cols = 60; let rows = 20; let size = Size { cols, rows }; - let mut tab = create_new_tab(size); + let stacked_resize = true; + let mut tab = create_new_tab(size, stacked_resize); tab.set_pane_frames(false); // a single frameless pane should take up all available space diff --git a/zellij-server/src/ui/boundaries.rs b/zellij-server/src/ui/boundaries.rs index 0df0e4eeae..7f70b1c726 100644 --- a/zellij-server/src/ui/boundaries.rs +++ b/zellij-server/src/ui/boundaries.rs @@ -445,7 +445,7 @@ impl Boundaries { } } pub fn add_rect(&mut self, rect: &dyn Pane, color: Option) { - let pane_is_stacked = rect.current_geom().is_stacked; + let pane_is_stacked = rect.current_geom().is_stacked(); if !self.is_fully_inside_screen(rect) { return; } @@ -576,7 +576,7 @@ impl Boundaries { rect.y() + rect.rows() < self.viewport.y + self.viewport.rows } fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize { - let pane_is_stacked = rect.current_geom().is_stacked; + let pane_is_stacked = rect.current_geom().is_stacked(); let horizontal_frame_offset = if pane_is_stacked { 0 } else { 1 }; if rect.y() > self.viewport.y { rect.y() - horizontal_frame_offset diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index d4b9e91681..9679bc2f49 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -271,6 +271,7 @@ fn create_new_screen(size: Size) -> Screen { let styled_underlines = true; let arrow_fonts = true; let explicitly_disable_kitty_keyboard_protocol = false; + let stacked_resize = true; let screen = Screen::new( bus, &client_attributes, @@ -291,6 +292,7 @@ fn create_new_screen(size: Size) -> Screen { arrow_fonts, layout_dir, explicitly_disable_kitty_keyboard_protocol, + stacked_resize, ); screen } diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 15f1fd54a5..2a4b3f9cdd 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -408,3 +408,8 @@ load_plugins { // Default: true (if the host terminal supports it) // // support_kitty_keyboard_protocol false + +// Whether to stack panes when resizing beyond a certain size +// Default: true +// +// stacked_resize false diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 0201eed446..f69975fc06 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -914,9 +914,15 @@ impl TiledPaneLayout { layout_to_split.focus_deepest_pane(); } - split_space(space, &layout_to_split, space, ignore_percent_split_sizes)? + split_space( + space, + &layout_to_split, + space, + ignore_percent_split_sizes, + 0, + )? }, - None => split_space(space, self, space, ignore_percent_split_sizes)?, + None => split_space(space, self, space, ignore_percent_split_sizes, 0)?, }; for (_pane_layout, pane_geom) in layouts.iter() { if !pane_geom.is_at_least_minimum_size() { @@ -1611,6 +1617,7 @@ fn split_space( layout: &TiledPaneLayout, total_space_to_split: &PaneGeom, ignore_percent_split_sizes: bool, + mut next_stack_id: usize, ) -> Result, &'static str> { let sizes: Vec> = if layout.children_are_stacked { let index_of_expanded_pane = layout.children.iter().position(|p| p.is_expanded_in_stack); @@ -1675,6 +1682,13 @@ fn split_space( acc } }); + let stacked = if layout.children_are_stacked { + let stack_id = next_stack_id; + next_stack_id += 1; + Some(stack_id) + } else { + None + }; let mut total_pane_size = 0; for (&size, _part) in sizes.iter().zip(&*layout.children) { @@ -1710,7 +1724,7 @@ fn split_space( y: space_to_split.y, cols: split_dimension, rows: inherited_dimension, - is_stacked: layout.children_are_stacked, + stacked, is_pinned: false, logical_position: None, }, @@ -1719,7 +1733,7 @@ fn split_space( y: current_position, cols: inherited_dimension, rows: split_dimension, - is_stacked: layout.children_are_stacked, + stacked, is_pinned: false, logical_position: None, }, @@ -1743,6 +1757,7 @@ fn split_space( part, total_space_to_split, ignore_percent_split_sizes, + next_stack_id, )?; // add the only first child to pane_positions only adding the others after all the // childfree panes have been added so that the returned vec will be sorted breadth-first diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index c571bcae62..5ece3c581a 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -161,6 +161,12 @@ pub struct Options { #[clap(long, value_parser)] #[serde(default)] pub support_kitty_keyboard_protocol: Option, + + /// Whether to stack panes when resizing beyond a certain size + /// default is true + #[clap(long, value_parser)] + #[serde(default)] + pub stacked_resize: Option, } #[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] @@ -239,6 +245,7 @@ impl Options { let support_kitty_keyboard_protocol = other .support_kitty_keyboard_protocol .or(self.support_kitty_keyboard_protocol); + let stacked_resize = other.stacked_resize.or(self.stacked_resize); Options { simplified_ui, @@ -268,6 +275,7 @@ impl Options { serialization_interval, disable_session_metadata, support_kitty_keyboard_protocol, + stacked_resize, } } @@ -326,6 +334,7 @@ impl Options { let support_kitty_keyboard_protocol = other .support_kitty_keyboard_protocol .or(self.support_kitty_keyboard_protocol); + let stacked_resize = other.stacked_resize.or(self.stacked_resize); Options { simplified_ui, @@ -355,6 +364,7 @@ impl Options { serialization_interval, disable_session_metadata, support_kitty_keyboard_protocol, + stacked_resize, } } @@ -420,6 +430,7 @@ impl From for Options { styled_underlines: opts.styled_underlines, serialization_interval: opts.serialization_interval, support_kitty_keyboard_protocol: opts.support_kitty_keyboard_protocol, + stacked_resize: opts.stacked_resize, ..Default::default() } } diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 37f36fd8a5..009ad48a13 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -2241,6 +2241,8 @@ impl Options { "support_kitty_keyboard_protocol" ) .map(|(v, _)| v); + let stacked_resize = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "stacked_resize").map(|(v, _)| v); Ok(Options { simplified_ui, theme, @@ -2269,6 +2271,7 @@ impl Options { serialization_interval, disable_session_metadata, support_kitty_keyboard_protocol, + stacked_resize, }) } pub fn from_string(stringified_keybindings: &String) -> Result { @@ -3068,6 +3071,34 @@ impl Options { None } } + fn stacked_resize_to_kdl(&self, add_comments: bool) -> Option { + let comment_text = format!( + "{}\n{}\n{}\n{}", + " ", + "// Whether to stack panes when resizing beyond a certain size", + "// Default: true", + "// ", + ); + + let create_node = |node_value: bool| -> KdlNode { + let mut node = KdlNode::new("stacked_resize"); + node.push(KdlValue::Bool(node_value)); + node + }; + if let Some(stacked_resize) = self.stacked_resize { + let mut node = create_node(stacked_resize); + if add_comments { + node.set_leading(format!("{}\n", comment_text)); + } + Some(node) + } else if add_comments { + let mut node = create_node(false); + node.set_leading(format!("{}\n// ", comment_text)); + Some(node) + } else { + None + } + } pub fn to_kdl(&self, add_comments: bool) -> Vec { let mut nodes = vec![]; if let Some(simplified_ui_node) = self.simplified_ui_to_kdl(add_comments) { @@ -3155,6 +3186,9 @@ impl Options { { nodes.push(support_kitty_keyboard_protocol); } + if let Some(stacked_resize) = self.stacked_resize_to_kdl(add_comments) { + nodes.push(stacked_resize); + } nodes } } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap index f31c73d3c4..f7b1c82192 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5563 +assertion_line: 5598 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -432,4 +432,9 @@ load_plugins { // Default: true (if the host terminal supports it) // // support_kitty_keyboard_protocol false + +// Whether to stack panes when resizing beyond a certain size +// Default: true +// +// stacked_resize false diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap index 00fd6320c6..61361362b7 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5380 +assertion_line: 5537 expression: fake_document.to_string() --- @@ -180,4 +180,9 @@ disable_session_metadata true // Default: true (if the host terminal supports it) // support_kitty_keyboard_protocol false + +// Whether to stack panes when resizing beyond a certain size +// Default: true +// +// stacked_resize false diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 7de00ee5c7..d337e9ac7a 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -16,7 +16,7 @@ pub struct PaneGeom { pub y: usize, pub rows: Dimension, pub cols: Dimension, - pub is_stacked: bool, + pub stacked: Option, // usize - stack id pub is_pinned: bool, // only relevant to floating panes pub logical_position: Option, // relevant when placing this pane in a layout } @@ -29,7 +29,7 @@ impl PartialEq for PaneGeom { && self.y == other.y && self.rows == other.rows && self.cols == other.cols - && self.is_stacked == other.is_stacked + && self.stacked == other.stacked } } @@ -40,7 +40,7 @@ impl std::hash::Hash for PaneGeom { self.y.hash(state); self.rows.hash(state); self.cols.hash(state); - self.is_stacked.hash(state); + self.stacked.hash(state); } } @@ -170,6 +170,35 @@ impl Dimension { }, } } + pub fn split_out(&mut self, by: f64) -> Self { + match self.constraint { + Constraint::Percent(percent) => { + let split_out_value = percent / by; + let split_out_inner_value = self.inner / by as usize; + self.constraint = Constraint::Percent(percent - split_out_value); + self.inner = self.inner.saturating_sub(split_out_inner_value); + let mut split_out_dimension = Self::percent(split_out_value); + split_out_dimension.inner = split_out_inner_value; + split_out_dimension + }, + Constraint::Fixed(fixed) => { + let split_out_value = fixed / by as usize; + self.constraint = Constraint::Fixed(fixed - split_out_value); + Self::fixed(split_out_value) + }, + } + } + pub fn reduce_by(&mut self, by: f64, by_inner: usize) { + match self.constraint { + Constraint::Percent(percent) => { + self.constraint = Constraint::Percent(percent - by); + self.inner = self.inner.saturating_sub(by_inner); + }, + Constraint::Fixed(_fixed) => { + log::error!("Cannot reduce_by fixed dimensions"); + }, + } + } } #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] @@ -257,6 +286,85 @@ impl PaneGeom { self.rows.set_inner(new_rows); } } + pub fn combine_vertically_with(&self, geom_below: &PaneGeom) -> Option { + match (self.rows.constraint, geom_below.rows.constraint) { + (Constraint::Percent(self_percent), Constraint::Percent(geom_below_percent)) => { + let mut combined = self.clone(); + combined.rows = Dimension::percent(self_percent + geom_below_percent); + combined.rows.inner = self.rows.inner + geom_below.rows.inner; + Some(combined) + }, + _ => { + log::error!("Can't combine fixed panes"); + None + }, + } + } + pub fn combine_horizontally_with(&self, geom_to_the_right: &PaneGeom) -> Option { + match (self.cols.constraint, geom_to_the_right.cols.constraint) { + (Constraint::Percent(self_percent), Constraint::Percent(geom_to_the_right_percent)) => { + let mut combined = self.clone(); + combined.cols = Dimension::percent(self_percent + geom_to_the_right_percent); + combined.cols.inner = self.cols.inner + geom_to_the_right.cols.inner; + Some(combined) + }, + _ => { + log::error!("Can't combine fixed panes"); + None + }, + } + } + pub fn combine_vertically_with_many(&self, geoms_below: &Vec) -> Option { + // here we expect the geoms to be sorted by their y and be contiguous (i.e. same x and + // width, no overlaps) and be below self + let mut combined = self.clone(); + for geom_below in geoms_below { + match (combined.rows.constraint, geom_below.rows.constraint) { + ( + Constraint::Percent(combined_percent), + Constraint::Percent(geom_below_percent), + ) => { + let new_rows_inner = combined.rows.inner + geom_below.rows.inner; + combined.rows = Dimension::percent(combined_percent + geom_below_percent); + combined.rows.inner = new_rows_inner; + }, + _ => { + log::error!("Can't combine fixed panes"); + return None; + }, + } + } + Some(combined) + } + pub fn combine_horizontally_with_many( + &self, + geoms_to_the_right: &Vec, + ) -> Option { + // here we expect the geoms to be sorted by their x and be contiguous (i.e. same x and + // width, no overlaps) and be right of self + let mut combined = self.clone(); + for geom_to_the_right in geoms_to_the_right { + match (combined.cols.constraint, geom_to_the_right.cols.constraint) { + ( + Constraint::Percent(combined_percent), + Constraint::Percent(geom_to_the_right_percent), + ) => { + let new_cols = combined.cols.inner + geom_to_the_right.cols.inner; + combined.cols = + Dimension::percent(combined_percent + geom_to_the_right_percent); + combined.cols.inner = new_cols; + }, + _ => { + log::error!("Can't combine fixed panes"); + return None; + }, + } + } + Some(combined) + } + pub fn is_stacked(&self) -> bool { + self.stacked.is_some() + } } impl Display for PaneGeom { @@ -266,7 +374,8 @@ impl Display for PaneGeom { write!(f, r#""y": {},"#, self.y)?; write!(f, r#""cols": {},"#, self.cols.constraint)?; write!(f, r#""rows": {},"#, self.rows.constraint)?; - write!(f, r#""stacked": {}"#, self.is_stacked)?; + write!(f, r#""stacked": {:?}"#, self.stacked)?; + write!(f, r#""logical_position": {:?}"#, self.logical_position)?; write!(f, " }}")?; Ok(()) diff --git a/zellij-utils/src/session_serialization.rs b/zellij-utils/src/session_serialization.rs index ae25295299..9ad23a5c64 100644 --- a/zellij-utils/src/session_serialization.rs +++ b/zellij-utils/src/session_serialization.rs @@ -682,7 +682,7 @@ fn tiled_pane_layout_from_manifest( ( run, g.is_borderless, - g.geom.is_stacked && g.geom.rows.inner > 1, + g.geom.is_stacked() && g.geom.rows.inner > 1, g.title.clone(), Some(g.is_focused), g.pane_contents.clone(), @@ -756,7 +756,7 @@ fn get_tiled_panes_layout_from_panegeoms( let children_are_stacked = children_split_direction == SplitDirection::Horizontal && new_geoms .iter() - .all(|c| c.iter().all(|c| c.geom.is_stacked)); + .all(|c| c.iter().all(|c| c.geom.is_stacked())); Some(TiledPaneLayout { children_split_direction, split_size, @@ -1284,7 +1284,7 @@ mod tests { y: 0, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1297,7 +1297,7 @@ mod tests { y: 10, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1314,7 +1314,7 @@ mod tests { y: 20, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1330,7 +1330,7 @@ mod tests { y: 30, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1351,7 +1351,7 @@ mod tests { y: 40, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1366,7 +1366,7 @@ mod tests { y: 50, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1383,7 +1383,7 @@ mod tests { y: 60, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1396,7 +1396,7 @@ mod tests { y: 70, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1411,7 +1411,7 @@ mod tests { y: 80, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1442,7 +1442,7 @@ mod tests { y: 0, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1455,7 +1455,7 @@ mod tests { y: 10, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1472,7 +1472,7 @@ mod tests { y: 20, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1488,7 +1488,7 @@ mod tests { y: 30, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1509,7 +1509,7 @@ mod tests { y: 40, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1524,7 +1524,7 @@ mod tests { y: 50, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1541,7 +1541,7 @@ mod tests { y: 60, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1556,7 +1556,7 @@ mod tests { y: 70, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1571,7 +1571,7 @@ mod tests { y: 80, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1600,7 +1600,7 @@ mod tests { y: 0, rows: Dimension::fixed(1), cols: Dimension::fixed(10), - is_stacked: true, + stacked: Some(0), is_pinned: false, logical_position: None, }, @@ -1612,7 +1612,7 @@ mod tests { y: 1, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: true, + stacked: Some(0), is_pinned: false, logical_position: None, }, @@ -1624,7 +1624,7 @@ mod tests { y: 11, rows: Dimension::fixed(1), cols: Dimension::fixed(10), - is_stacked: true, + stacked: Some(0), is_pinned: false, logical_position: None, }, @@ -1649,7 +1649,7 @@ mod tests { y: 0, rows: Dimension::percent(100.0), cols: Dimension::percent(100.0), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1665,7 +1665,7 @@ mod tests { y: 0, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1677,7 +1677,7 @@ mod tests { y: 0, rows: Dimension::fixed(10), cols: Dimension::fixed(10), - is_stacked: false, + stacked: None, is_pinned: false, logical_position: None, }, @@ -1810,7 +1810,7 @@ mod tests { y: data["y"].to_string().parse().unwrap(), rows: get_dim(&data["rows"]), cols: get_dim(&data["cols"]), - is_stacked: data["is_stacked"].to_string().parse().unwrap(), + stacked: None, is_pinned: false, logical_position: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap index 155ffb811d..4d2c2feec2 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 740 +assertion_line: 770 expression: "format!(\"{:#?}\", options)" --- Options { @@ -33,4 +33,5 @@ Options { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap index 682e645915..db16d8ce4d 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 768 +assertion_line: 798 expression: "format!(\"{:#?}\", options)" --- Options { @@ -33,4 +33,5 @@ Options { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap index 127a7d704d..621bc4a7d3 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 727 +assertion_line: 757 expression: "format!(\"{:#?}\", options)" --- Options { @@ -31,4 +31,5 @@ Options { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 754375a862..138f4bec30 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -5610,6 +5610,7 @@ Config { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 4e79b783f5..0b0294596d 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -5610,6 +5610,7 @@ Config { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap index 72b3df8ea5..009d6715f1 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 854 +assertion_line: 855 expression: "format!(\"{:#?}\", config)" --- Config { @@ -118,6 +118,7 @@ Config { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap index 604752e8c3..46d265d1d7 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 750 +assertion_line: 780 expression: "format!(\"{:#?}\", options)" --- Options { @@ -33,4 +33,5 @@ Options { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index 6c435fa0d1..05e35cb29f 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -5610,6 +5610,7 @@ Config { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, }, themes: { "other-theme-from-config": Theme { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index 5712f84705..a372158222 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -5610,6 +5610,7 @@ Config { serialization_interval: None, disable_session_metadata: None, support_kitty_keyboard_protocol: None, + stacked_resize: None, }, themes: {}, plugins: PluginAliases {