diff options
author | Aram Drevekenin <aram@poor.dev> | 2022-12-19 12:48:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-19 12:48:43 +0100 |
commit | 1b5f3c52a48feb584228f326c20829c83c21cc47 (patch) | |
tree | f4a76f6ecb2e3fca72c83899ba2c1cd83acbfe02 /zellij-server/src/panes | |
parent | d1f50150f6f7525f93ccb9ed94f75ce6bfb5c60b (diff) |
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
Diffstat (limited to 'zellij-server/src/panes')
-rw-r--r-- | zellij-server/src/panes/plugin_pane.rs | 10 | ||||
-rw-r--r-- | zellij-server/src/panes/terminal_pane.rs | 10 | ||||
-rw-r--r-- | zellij-server/src/panes/tiled_panes/mod.rs | 33 | ||||
-rw-r--r-- | zellij-server/src/panes/tiled_panes/pane_resizer.rs | 21 | ||||
-rw-r--r-- | zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs | 146 |
5 files changed, 181 insertions, 39 deletions
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::<ZellijError>() { + 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::<ZellijError>() { + 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<Span>) { + fn apply_spans(&mut self, spans: Vec<Span>) -> 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<Vec<PaneId>> { + 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<PaneId> = 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<bool> { 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::<ZellijError>() + { + 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::<ZellijError>() + { + 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); } |