From 1b5f3c52a48feb584228f326c20829c83c21cc47 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 19 Dec 2022 12:48:43 +0100 Subject: fix(panes): show visual error when failing to resize panes (#2036) * fix(panes): show visual error when failing to resize pane vertically/horizontally * fix(resize): retry pane resize on rounding errors * fix(resize): proper error when resizing other panes into fixed panes * style(fmt): rustfmt --- zellij-server/src/background_jobs.rs | 11 +- zellij-server/src/panes/plugin_pane.rs | 10 +- zellij-server/src/panes/terminal_pane.rs | 10 +- zellij-server/src/panes/tiled_panes/mod.rs | 33 +++- .../src/panes/tiled_panes/pane_resizer.rs | 21 ++- .../src/panes/tiled_panes/tiled_pane_grid.rs | 146 ++++++++++++--- zellij-server/src/screen.rs | 28 +-- zellij-server/src/tab/mod.rs | 30 +++- zellij-server/src/tab/unit/tab_tests.rs | 195 ++++++++++++++++++++- 9 files changed, 415 insertions(+), 69 deletions(-) (limited to 'zellij-server') diff --git a/zellij-server/src/background_jobs.rs b/zellij-server/src/background_jobs.rs index 3c89ed064..c612c9d57 100644 --- a/zellij-server/src/background_jobs.rs +++ b/zellij-server/src/background_jobs.rs @@ -10,7 +10,7 @@ use crate::thread_bus::Bus; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum BackgroundJob { - DisplayPaneError(PaneId, String), + DisplayPaneError(Vec, String), Exit, } @@ -34,7 +34,7 @@ pub(crate) fn background_jobs_main(bus: Bus) -> Result<()> { err_ctx.add_call(ContextType::BackgroundJob((&event).into())); let job = event.clone(); match event { - BackgroundJob::DisplayPaneError(pane_id, text) => { + BackgroundJob::DisplayPaneError(pane_ids, text) => { if job_already_running(job, &mut running_jobs) { continue; } @@ -42,11 +42,14 @@ pub(crate) fn background_jobs_main(bus: Bus) -> Result<()> { let senders = bus.senders.clone(); async move { let _ = senders.send_to_screen( - ScreenInstruction::AddRedPaneFrameColorOverride(pane_id, Some(text)), + ScreenInstruction::AddRedPaneFrameColorOverride( + pane_ids.clone(), + Some(text), + ), ); task::sleep(std::time::Duration::from_millis(FLASH_DURATION_MS)).await; let _ = senders.send_to_screen( - ScreenInstruction::ClearPaneFrameColorOverride(pane_id), + ScreenInstruction::ClearPaneFrameColorOverride(pane_ids), ); } }); diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index 3a0ec9c83..babb66b67 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -16,7 +16,7 @@ use zellij_utils::{ channels::SenderWithContext, data::{Event, InputMode, Mouse, Palette, PaletteColor, Style}, errors::prelude::*, - pane_size::{Dimension, PaneGeom}, + pane_size::PaneGeom, shared::make_terminal_title, vte, }; @@ -341,28 +341,28 @@ impl Pane for PluginPane { } fn reduce_height(&mut self, percent: f64) { if let Some(p) = self.geom.rows.as_percent() { - self.geom.rows = Dimension::percent(p - percent); + self.geom.rows.set_percent(p - percent); self.resize_grids(); self.set_should_render(true); } } fn increase_height(&mut self, percent: f64) { if let Some(p) = self.geom.rows.as_percent() { - self.geom.rows = Dimension::percent(p + percent); + self.geom.rows.set_percent(p + percent); self.resize_grids(); self.set_should_render(true); } } fn reduce_width(&mut self, percent: f64) { if let Some(p) = self.geom.cols.as_percent() { - self.geom.cols = Dimension::percent(p - percent); + self.geom.cols.set_percent(p - percent); self.resize_grids(); self.set_should_render(true); } } fn increase_width(&mut self, percent: f64) { if let Some(p) = self.geom.cols.as_percent() { - self.geom.cols = Dimension::percent(p + percent); + self.geom.cols.set_percent(p + percent); self.resize_grids(); self.set_should_render(true); } diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 4d5ed840c..d206245ad 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -18,8 +18,8 @@ use zellij_utils::pane_size::Offset; use zellij_utils::{ data::{InputMode, Palette, PaletteColor, Style}, errors::prelude::*, + pane_size::PaneGeom, pane_size::SizeInPixels, - pane_size::{Dimension, PaneGeom}, position::Position, shared::make_terminal_title, vte, @@ -446,25 +446,25 @@ impl Pane for TerminalPane { } fn reduce_height(&mut self, percent: f64) { if let Some(p) = self.geom.rows.as_percent() { - self.geom.rows = Dimension::percent(p - percent); + self.geom.rows.set_percent(p - percent); self.set_should_render(true); } } fn increase_height(&mut self, percent: f64) { if let Some(p) = self.geom.rows.as_percent() { - self.geom.rows = Dimension::percent(p + percent); + self.geom.rows.set_percent(p + percent); self.set_should_render(true); } } fn reduce_width(&mut self, percent: f64) { if let Some(p) = self.geom.cols.as_percent() { - self.geom.cols = Dimension::percent(p - percent); + self.geom.cols.set_percent(p - percent); self.set_should_render(true); } } fn increase_width(&mut self, percent: f64) { if let Some(p) = self.geom.cols.as_percent() { - self.geom.cols = Dimension::percent(p + percent); + self.geom.cols.set_percent(p + percent); self.set_should_render(true); } } diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index ee752ea15..726627f4b 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -577,9 +577,38 @@ impl TiledPanes { *self.viewport.borrow(), ); - pane_grid + match pane_grid .change_pane_size(&active_pane_id, strategy, (RESIZE_PERCENT, RESIZE_PERCENT)) - .with_context(err_context)?; + .with_context(err_context) + { + Ok(_) => {}, + Err(err) => match err.downcast_ref::() { + Some(ZellijError::PaneSizeUnchanged) => { + // try once more with double the resize percent, but let's keep it at that + match pane_grid + .change_pane_size( + &active_pane_id, + strategy, + (RESIZE_PERCENT * 2.0, RESIZE_PERCENT * 2.0), + ) + .with_context(err_context) + { + Ok(_) => {}, + Err(err) => match err.downcast_ref::() { + Some(ZellijError::PaneSizeUnchanged) => { + Err::<(), _>(err).non_fatal() + }, + _ => { + return Err(err); + }, + }, + } + }, + _ => { + return Err(err); + }, + }, + } for pane in self.panes.values_mut() { resize_pty!(pane, self.os_api, self.senders).unwrap(); diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index 37eb6c4c6..c01e42e4b 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -53,7 +53,7 @@ impl<'a> PaneResizer<'a> { let spans = self .discretize_spans(grid, space) .map_err(|err| anyhow!("{}", err))?; - self.apply_spans(spans); + self.apply_spans(spans)?; Ok(()) } @@ -127,10 +127,13 @@ impl<'a> PaneResizer<'a> { Ok(grid.into_iter().flatten().collect()) } - fn apply_spans(&mut self, spans: Vec) { + fn apply_spans(&mut self, spans: Vec) -> Result<()> { + let err_context = || format!("Failed to apply spans"); let mut panes = self.panes.borrow_mut(); + let mut geoms_changed = false; for span in spans { let pane = panes.get_mut(&span.pid).unwrap(); + let current_geom = pane.position_and_size(); let new_geom = match span.direction { SplitDirection::Horizontal => PaneGeom { x: span.pos, @@ -143,12 +146,26 @@ impl<'a> PaneResizer<'a> { ..pane.current_geom() }, }; + if new_geom.rows.as_usize() != current_geom.rows.as_usize() + || new_geom.cols.as_usize() != current_geom.cols.as_usize() + { + geoms_changed = true; + } if pane.geom_override().is_some() { pane.set_geom_override(new_geom); } else { pane.set_geom(new_geom); } } + if geoms_changed { + Ok(()) + } else { + // probably a rounding issue - this might be considered an error depending on who + // called us - if it's an explicit resize operation, it's clearly an error (the user + // wanted to resize and doesn't care about percentage rounding), if it's resizing the + // terminal window as a whole, it might not be + Err(ZellijError::PaneSizeUnchanged).with_context(err_context) + } } // FIXME: Functions like this should have unit 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 fdb2f74ad..e34164f9e 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -88,6 +88,56 @@ impl<'a> TiledPaneGrid<'a> { } .is_fixed()) } + fn neighbor_pane_ids(&self, pane_id: &PaneId, direction: Direction) -> Result> { + let err_context = || format!("Failed to get neighboring panes"); + // Shorthand + use Direction as Dir; + let mut neighbor_terminals = self + .pane_ids_directly_next_to(pane_id, &direction) + .with_context(err_context)?; + + let neighbor_terminal_borders: HashSet<_> = if direction.is_horizontal() { + neighbor_terminals + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().y()) + .collect() + } else { + neighbor_terminals + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().x()) + .collect() + }; + + // Only return those neighbors that are aligned and between pane borders + let (some_direction, other_direction) = match direction { + Dir::Left | Dir::Right => (Dir::Up, Dir::Down), + Dir::Down | Dir::Up => (Dir::Left, Dir::Right), + }; + let (some_borders, _some_terminals) = self + .contiguous_panes_with_alignment( + pane_id, + &neighbor_terminal_borders, + &direction, + &some_direction, + ) + .with_context(err_context)?; + let (other_borders, _other_terminals) = self + .contiguous_panes_with_alignment( + pane_id, + &neighbor_terminal_borders, + &direction, + &other_direction, + ) + .with_context(err_context)?; + neighbor_terminals.retain(|t| { + if direction.is_horizontal() { + self.pane_is_between_horizontal_borders(t, some_borders, other_borders) + } else { + self.pane_is_between_vertical_borders(t, some_borders, other_borders) + } + }); + Ok(neighbor_terminals) + } // Check if panes in the desired direction can be resized. Returns the maximum resize that's // possible (at most `change_by`). @@ -100,12 +150,39 @@ impl<'a> TiledPaneGrid<'a> { let err_context = || format!("failed to determine if pane {pane_id:?} can {strategy}"); if let Some(direction) = strategy.direction { - let mut pane_ids = self - .pane_ids_directly_next_to(pane_id, &direction) + if !self + .pane_is_flexible(direction.into(), pane_id) + .unwrap_or(false) + { + let pane_ids = match pane_id { + PaneId::Terminal(id) => vec![(*id, true)], + PaneId::Plugin(id) => vec![(*id, false)], + }; + return Err(ZellijError::CantResizeFixedPanes { pane_ids }) + .with_context(err_context); + } + let pane_ids = self + .neighbor_pane_ids(pane_id, direction) .with_context(err_context)?; - pane_ids.retain(|id| self.pane_is_flexible(direction.into(), id).unwrap_or(false)); + let fixed_panes: Vec = pane_ids + .iter() + .filter(|p| !self.pane_is_flexible(direction.into(), p).unwrap_or(false)) + .copied() + .collect(); + if !fixed_panes.is_empty() { + let mut pane_ids = vec![]; + for fixed_pane in fixed_panes { + match fixed_pane { + PaneId::Terminal(id) => pane_ids.push((id, true)), + PaneId::Plugin(id) => pane_ids.push((id, false)), + }; + } + return Err(ZellijError::CantResizeFixedPanes { pane_ids }) + .with_context(err_context); + } if pane_ids.is_empty() { + // TODO: proper error return Ok(false); } @@ -160,37 +237,56 @@ impl<'a> TiledPaneGrid<'a> { change_by: (f64, f64), ) -> Result { let err_context = || format!("failed to {strategy} by {change_by:?} for pane {pane_id:?}"); - // Shorthand use Direction as Dir; + let mut fixed_panes_blocking_resize = vec![]; + // Default behavior is to only increase pane size, unless the direction being resized to is // a boundary. In this case, decrease size from the other side (invert strategy)! - let strategy = if strategy.resize_increase() // Only invert when increasing + let can_invert_strategy_if_needed = strategy.resize_increase() // Only invert when increasing && strategy.invert_on_boundaries // Only invert if configured to do so - && strategy.direction.is_some() // Only invert if there's a direction - && strategy - .direction - .and_then(|direction| { - // Only invert if there are no neighbor IDs in the given direction - let mut neighbors = self.pane_ids_directly_next_to(pane_id, &direction) - .unwrap_or_default(); - neighbors.retain(|pane_id| self.pane_is_flexible(direction.into(), pane_id).unwrap_or(false)); - neighbors.is_empty().then_some(true) + && strategy.direction.is_some(); // Only invert if there's a direction + + let can_change_pane_size_in_main_direction = self + .can_change_pane_size(pane_id, &strategy, change_by) + .unwrap_or_else(|err| { + if let Some(ZellijError::CantResizeFixedPanes { pane_ids }) = + err.downcast_ref::() + { + fixed_panes_blocking_resize.append(&mut pane_ids.clone()); + } + false + }); + let can_change_pane_size_in_inverted_direction = if can_invert_strategy_if_needed { + let strategy = strategy.invert(); + self.can_change_pane_size(pane_id, &strategy, change_by) + .unwrap_or_else(|err| { + if let Some(ZellijError::CantResizeFixedPanes { pane_ids }) = + err.downcast_ref::() + { + fixed_panes_blocking_resize.append(&mut pane_ids.clone()); + } + false }) - .unwrap_or(false) - { - strategy.invert() } else { - *strategy + false }; - - if !self - .can_change_pane_size(pane_id, &strategy, change_by) - .with_context(err_context)? + if strategy.direction.is_some() + && !can_change_pane_size_in_main_direction + && !can_change_pane_size_in_inverted_direction { - // Resize not possible, quit - return Ok(false); + // we can't resize in any direction, not playing the blame game, but I'm looking at + // you: fixed_panes_blocking_resize + return Err(ZellijError::CantResizeFixedPanes { + pane_ids: fixed_panes_blocking_resize, + }) + .with_context(err_context); } + let strategy = if can_change_pane_size_in_main_direction { + *strategy + } else { + strategy.invert() + }; if let Some(direction) = strategy.direction { let mut neighbor_terminals = self @@ -407,7 +503,7 @@ impl<'a> TiledPaneGrid<'a> { }; if self .change_pane_size(pane_id, &new_strategy, change_by) - .with_context(err_context)? + .unwrap_or(false) { return Ok(true); } diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 9ed803bbd..a862cb405 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -224,8 +224,8 @@ pub enum ScreenInstruction { SearchToggleCaseSensitivity(ClientId), SearchToggleWholeWord(ClientId), SearchToggleWrap(ClientId), - AddRedPaneFrameColorOverride(PaneId, Option), // Option => optional error text - ClearPaneFrameColorOverride(PaneId), + AddRedPaneFrameColorOverride(Vec, Option), // Option => optional error text + ClearPaneFrameColorOverride(Vec), } impl From<&ScreenInstruction> for ScreenContext { @@ -2120,22 +2120,26 @@ pub(crate) fn screen_thread_main( screen.render()?; screen.unblock_input()?; }, - ScreenInstruction::AddRedPaneFrameColorOverride(pane_id, error_text) => { + ScreenInstruction::AddRedPaneFrameColorOverride(pane_ids, error_text) => { let all_tabs = screen.get_tabs_mut(); - for tab in all_tabs.values_mut() { - if tab.has_pane_with_pid(&pane_id) { - tab.add_red_pane_frame_color_override(pane_id, error_text); - break; + for pane_id in pane_ids { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + tab.add_red_pane_frame_color_override(pane_id, error_text.clone()); + break; + } } } screen.render()?; }, - ScreenInstruction::ClearPaneFrameColorOverride(pane_id) => { + ScreenInstruction::ClearPaneFrameColorOverride(pane_ids) => { let all_tabs = screen.get_tabs_mut(); - for tab in all_tabs.values_mut() { - if tab.has_pane_with_pid(&pane_id) { - tab.clear_pane_frame_color_override(pane_id); - break; + for pane_id in pane_ids { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + tab.clear_pane_frame_color_override(pane_id); + break; + } } } screen.render()?; diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index a841b39ae..c2b26ecf8 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -1048,7 +1048,7 @@ impl Tab { if let Some(active_pane_id) = self.tiled_panes.get_active_pane_id(client_id) { self.senders .send_to_background_jobs(BackgroundJob::DisplayPaneError( - active_pane_id, + vec![active_pane_id], "TOO SMALL!".into(), )) .with_context(err_context)?; @@ -1102,7 +1102,7 @@ impl Tab { if let Some(active_pane_id) = self.tiled_panes.get_active_pane_id(client_id) { self.senders .send_to_background_jobs(BackgroundJob::DisplayPaneError( - active_pane_id, + vec![active_pane_id], "TOO SMALL!".into(), )) .with_context(err_context)?; @@ -1630,6 +1630,7 @@ impl Tab { self.should_clear_display_before_rendering = true; } pub fn resize(&mut self, client_id: ClientId, strategy: ResizeStrategy) -> Result<()> { + let err_context = || format!("unable to resize pane"); if self.floating_panes.panes_are_visible() { let successfully_resized = self .floating_panes @@ -1639,9 +1640,28 @@ 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 { - self.tiled_panes - .resize_active_pane(client_id, &strategy) - .unwrap(); + match self.tiled_panes.resize_active_pane(client_id, &strategy) { + Ok(_) => {}, + Err(err) => match err.downcast_ref::() { + Some(ZellijError::CantResizeFixedPanes { pane_ids }) => { + let mut pane_ids_to_error = vec![]; + for (id, is_terminal) in pane_ids { + if *is_terminal { + pane_ids_to_error.push(PaneId::Terminal(*id)); + } else { + pane_ids_to_error.push(PaneId::Plugin(*id)); + }; + } + self.senders + .send_to_background_jobs(BackgroundJob::DisplayPaneError( + pane_ids_to_error, + "FIXED!".into(), + )) + .with_context(err_context)?; + }, + _ => Err::<(), _>(err).fatal(), + }, + } } Ok(()) } diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 192c55e3e..86848045a 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -231,15 +231,8 @@ fn create_new_tab_with_layout(size: Size, layout: PaneLayout) -> Tab { for i in 0..layout.extract_run_instructions().len() { new_terminal_ids.push((i as u32, None)); } - tab.apply_layout( - layout, - // vec![(1, None), (2, None)], - new_terminal_ids, - HashMap::new(), - index, - client_id, - ) - .unwrap(); + tab.apply_layout(layout, new_terminal_ids, HashMap::new(), index, client_id) + .unwrap(); tab } @@ -5932,6 +5925,144 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() { ); } +#[test] +pub fn cannot_resize_down_when_pane_has_fixed_rows() { + // ┌───────────┐ ┌───────────┐ + // │███████████│ │███████████│ + // ├───────────┤ ==resize=down==> ├───────────┤ + // │ │ │ │ + // └───────────┘ └───────────┘ + // █ == focused pane + + let size = Size { + cols: 121, + rows: 20, + }; + + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Horizontal; + let mut fixed_child = PaneLayout::default(); + fixed_child.split_size = Some(SplitSize::Fixed(10)); + initial_layout.children = vec![fixed_child, PaneLayout::default()]; + let mut tab = create_new_tab_with_layout(size, initial_layout); + tab_resize_down(&mut tab, 1); + + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(0)) + .unwrap() + .position_and_size() + .rows + .as_usize(), + 10, + "pane 1 height stayed the same" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(1)) + .unwrap() + .position_and_size() + .rows + .as_usize(), + 10, + "pane 2 height stayed the same" + ); +} + +#[test] +pub fn cannot_resize_down_when_pane_below_has_fixed_rows() { + // ┌───────────┐ ┌───────────┐ + // │███████████│ │███████████│ + // ├───────────┤ ==resize=down==> ├───────────┤ + // │ │ │ │ + // └───────────┘ └───────────┘ + // █ == focused pane + + let size = Size { + cols: 121, + rows: 20, + }; + + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Horizontal; + let mut fixed_child = PaneLayout::default(); + fixed_child.split_size = Some(SplitSize::Fixed(10)); + initial_layout.children = vec![PaneLayout::default(), fixed_child]; + let mut tab = create_new_tab_with_layout(size, initial_layout); + tab_resize_down(&mut tab, 1); + + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(0)) + .unwrap() + .position_and_size() + .rows + .as_usize(), + 10, + "pane 1 height stayed the same" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(1)) + .unwrap() + .position_and_size() + .rows + .as_usize(), + 10, + "pane 2 height stayed the same" + ); +} + +#[test] +pub fn cannot_resize_up_when_pane_below_has_fixed_rows() { + // ┌───────────┐ ┌───────────┐ + // │███████████│ │███████████│ + // ├───────────┤ ==resize=down==> ├───────────┤ + // │ │ │ │ + // └───────────┘ └───────────┘ + // █ == focused pane + + let size = Size { + cols: 121, + rows: 20, + }; + + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Horizontal; + let mut fixed_child = PaneLayout::default(); + fixed_child.split_size = Some(SplitSize::Fixed(10)); + initial_layout.children = vec![PaneLayout::default(), fixed_child]; + let mut tab = create_new_tab_with_layout(size, initial_layout); + tab_resize_up(&mut tab, 1); + + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(0)) + .unwrap() + .position_and_size() + .rows + .as_usize(), + 10, + "pane 1 height stayed the same" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(1)) + .unwrap() + .position_and_size() + .rows + .as_usize(), + 10, + "pane 2 height stayed the same" + ); +} + #[test] pub fn resize_left_with_pane_to_the_left() { // ┌─────┬─────┐ ┌───┬───────┐ @@ -11217,6 +11348,52 @@ pub fn cannot_resize_right_when_pane_to_the_left_is_at_minimum_width() { ); } +#[test] +pub fn cannot_resize_right_when_pane_has_fixed_columns() { + // ┌──┬──┐ ┌──┬──┐ + // │██│ │ │██│ │ + // │██│ │ ==resize=right==> │██│ │ + // │██│ │ │██│ │ + // └──┴──┘ └──┴──┘ + // █ == focused pane + + let size = Size { + cols: 121, + rows: 20, + }; + + let mut initial_layout = PaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + let mut fixed_child = PaneLayout::default(); + fixed_child.split_size = Some(SplitSize::Fixed(60)); + initial_layout.children = vec![fixed_child, PaneLayout::default()]; + let mut tab = create_new_tab_with_layout(size, initial_layout); + tab_resize_down(&mut tab, 1); + + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(0)) + .unwrap() + .position_and_size() + .cols + .as_usize(), + 60, + "pane 1 height stayed the same" + ); + assert_eq!( + tab.tiled_panes + .panes + .get(&PaneId::Terminal(1)) + .unwrap() + .position_and_size() + .cols + .as_usize(), + 61, + "pane 2 height stayed the same" + ); +} + #[test] pub fn resize_up_with_pane_above() { // ┌───────────┐ ┌───────────┐ -- cgit v1.2.3