diff --git a/CHANGELOG.md b/CHANGELOG.md index da66bf9733..f87e572f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * refactor(messaging): reduce extraneous cross-thread messaging (https://github.com/zellij-org/zellij/pull/1996) * errors: preserve caller location in `to_log` (https://github.com/zellij-org/zellij/pull/1994) * feat: show loading screen on startup (https://github.com/zellij-org/zellij/pull/1997) +* feat: Allow "reducing" resizes, refactor resizing code (https://github.com/zellij-org/zellij/pull/1990) ## [0.33.0] - 2022-11-10 diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 97f2bd2c68..027425a3ae 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -367,6 +367,7 @@ pub fn action_key_group(keymap: &[(Key, Vec)], actions: &[&[Action]]) -> /// separator between them: /// /// - "hjkl" +/// - "HJKL" /// - "←↓↑→" /// - "←→" /// - "↓↑" @@ -423,6 +424,7 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec "", "hjkl" => "", "←↓↑→" => "", "←→" => "", diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs index ccab42dff8..4422105da5 100644 --- a/default-plugins/status-bar/src/second_line.rs +++ b/default-plugins/status-bar/src/second_line.rs @@ -109,8 +109,7 @@ fn full_shortcut_list_nonstandard_mode(help: &ModeInfo) -> LinePart { fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { use Action as A; use InputMode as IM; - use actions::Direction as Dir; - use actions::ResizeDirection as RDir; + use Direction as Dir; use actions::SearchDirection as SDir; use actions::SearchOption as SOpt; @@ -188,11 +187,23 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { (s("Toggle"), s("Toggle"), action_key(&km, &[A::ToggleTab])), (s("Select pane"), s("Select"), to_normal_key), ]} else if mi.mode == IM::Resize { vec![ - (s("Resize"), s("Resize"), action_key_group(&km, &[ - &[A::Resize(RDir::Left)], &[A::Resize(RDir::Down)], - &[A::Resize(RDir::Up)], &[A::Resize(RDir::Right)]])), + (s("Increase to"), s("Increase"), action_key_group(&km, &[ + &[A::Resize(Resize::Increase, Some(Dir::Left))], + &[A::Resize(Resize::Increase, Some(Dir::Down))], + &[A::Resize(Resize::Increase, Some(Dir::Up))], + &[A::Resize(Resize::Increase, Some(Dir::Right))] + ])), + (s("Decrease from"), s("Decrease"), action_key_group(&km, &[ + &[A::Resize(Resize::Decrease, Some(Dir::Left))], + &[A::Resize(Resize::Decrease, Some(Dir::Down))], + &[A::Resize(Resize::Decrease, Some(Dir::Up))], + &[A::Resize(Resize::Decrease, Some(Dir::Right))] + ])), (s("Increase/Decrease size"), s("Increase/Decrease"), - action_key_group(&km, &[&[A::Resize(RDir::Increase)], &[A::Resize(RDir::Decrease)]])), + action_key_group(&km, &[ + &[A::Resize(Resize::Increase, None)], + &[A::Resize(Resize::Decrease, None)] + ])), (s("Select pane"), s("Select"), to_normal_key), ]} else if mi.mode == IM::Move { vec![ (s("Move"), s("Move"), action_key_group(&km, &[ @@ -666,13 +677,10 @@ mod tests { keybinds: vec![( InputMode::Pane, vec![ - (Key::Left, vec![Action::MoveFocus(actions::Direction::Left)]), - (Key::Down, vec![Action::MoveFocus(actions::Direction::Down)]), - (Key::Up, vec![Action::MoveFocus(actions::Direction::Up)]), - ( - Key::Right, - vec![Action::MoveFocus(actions::Direction::Right)], - ), + (Key::Left, vec![Action::MoveFocus(Direction::Left)]), + (Key::Down, vec![Action::MoveFocus(Direction::Down)]), + (Key::Up, vec![Action::MoveFocus(Direction::Up)]), + (Key::Right, vec![Action::MoveFocus(Direction::Right)]), (Key::Char('n'), vec![Action::NewPane(None, None), TO_NORMAL]), (Key::Char('x'), vec![Action::CloseFocus, TO_NORMAL]), ( @@ -701,13 +709,10 @@ mod tests { keybinds: vec![( InputMode::Pane, vec![ - (Key::Left, vec![Action::MoveFocus(actions::Direction::Left)]), - (Key::Down, vec![Action::MoveFocus(actions::Direction::Down)]), - (Key::Up, vec![Action::MoveFocus(actions::Direction::Up)]), - ( - Key::Right, - vec![Action::MoveFocus(actions::Direction::Right)], - ), + (Key::Left, vec![Action::MoveFocus(Direction::Left)]), + (Key::Down, vec![Action::MoveFocus(Direction::Down)]), + (Key::Up, vec![Action::MoveFocus(Direction::Up)]), + (Key::Right, vec![Action::MoveFocus(Direction::Right)]), (Key::Char('n'), vec![Action::NewPane(None, None), TO_NORMAL]), (Key::Char('x'), vec![Action::CloseFocus, TO_NORMAL]), ( @@ -732,22 +737,10 @@ mod tests { keybinds: vec![( InputMode::Pane, vec![ - ( - Key::Ctrl('a'), - vec![Action::MoveFocus(actions::Direction::Left)], - ), - ( - Key::Ctrl('\n'), - vec![Action::MoveFocus(actions::Direction::Down)], - ), - ( - Key::Ctrl('1'), - vec![Action::MoveFocus(actions::Direction::Up)], - ), - ( - Key::Ctrl(' '), - vec![Action::MoveFocus(actions::Direction::Right)], - ), + (Key::Ctrl('a'), vec![Action::MoveFocus(Direction::Left)]), + (Key::Ctrl('\n'), vec![Action::MoveFocus(Direction::Down)]), + (Key::Ctrl('1'), vec![Action::MoveFocus(Direction::Up)]), + (Key::Ctrl(' '), vec![Action::MoveFocus(Direction::Right)]), (Key::Backspace, vec![Action::NewPane(None, None), TO_NORMAL]), (Key::Esc, vec![Action::CloseFocus, TO_NORMAL]), (Key::End, vec![Action::ToggleFocusFullscreen, TO_NORMAL]), diff --git a/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs b/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs index b7a62ab8b3..67940e491d 100644 --- a/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs +++ b/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs @@ -1,10 +1,7 @@ use ansi_term::{unstyled_len, ANSIString, ANSIStrings, Style}; use crate::{action_key_group, style_key_with_modifier, LinePart}; -use zellij_tile::prelude::{ - actions::{Action, Direction}, - *, -}; +use zellij_tile::prelude::{actions::Action, *}; macro_rules! strings { ($ANSIStrings:expr) => {{ diff --git a/default-plugins/status-bar/src/tip/data/quicknav.rs b/default-plugins/status-bar/src/tip/data/quicknav.rs index 2e7cbec327..b8077e459d 100644 --- a/default-plugins/status-bar/src/tip/data/quicknav.rs +++ b/default-plugins/status-bar/src/tip/data/quicknav.rs @@ -1,10 +1,7 @@ use ansi_term::{unstyled_len, ANSIString, ANSIStrings, Style}; use crate::{action_key, action_key_group, style_key_with_modifier, LinePart}; -use zellij_tile::prelude::{ - actions::{Action, Direction, ResizeDirection}, - *, -}; +use zellij_tile::prelude::{actions::Action, *}; macro_rules! strings { ($ANSIStrings:expr) => {{ @@ -75,8 +72,8 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups { let mut resize_keys = action_key_group( &normal_keymap, &[ - &[Action::Resize(ResizeDirection::Increase)], - &[Action::Resize(ResizeDirection::Decrease)], + &[Action::Resize(Resize::Increase, None)], + &[Action::Resize(Resize::Decrease, None)], ], ); if resize_keys.contains(&Key::Alt(CharOrArrow::Char('='))) diff --git a/zellij-client/src/unit/stdin_tests.rs b/zellij-client/src/unit/stdin_tests.rs index aa1bd59ce7..c55283964c 100644 --- a/zellij-client/src/unit/stdin_tests.rs +++ b/zellij-client/src/unit/stdin_tests.rs @@ -2,8 +2,8 @@ use super::input_loop; use crate::stdin_ansi_parser::StdinAnsiParser; use crate::stdin_loop; use zellij_utils::anyhow::Result; -use zellij_utils::data::{InputMode, Palette}; -use zellij_utils::input::actions::{Action, Direction}; +use zellij_utils::data::{Direction, InputMode, Palette}; +use zellij_utils::input::actions::Action; use zellij_utils::input::config::Config; use zellij_utils::input::options::Options; use zellij_utils::nix; 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 83724887f8..2245ee16aa 100644 --- a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs +++ b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs @@ -2,18 +2,23 @@ use crate::tab::{MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH}; use crate::{panes::PaneId, tab::Pane}; use std::cmp::Ordering; use std::collections::HashMap; +use zellij_utils::data::ResizeStrategy; +use zellij_utils::errors::prelude::*; use zellij_utils::pane_size::{Dimension, PaneGeom, Size, Viewport}; use std::cell::RefCell; use std::rc::Rc; -const RESIZE_INCREMENT_WIDTH: usize = 5; -const RESIZE_INCREMENT_HEIGHT: usize = 2; const MOVE_INCREMENT_HORIZONTAL: usize = 10; const MOVE_INCREMENT_VERTICAL: usize = 5; const MAX_PANES: usize = 100; +// For error reporting +fn no_pane_id(pane_id: &PaneId) -> String { + format!("no floating pane with ID {:?} found", pane_id) +} + pub struct FloatingPaneGrid<'a> { panes: Rc>>>, desired_pane_positions: Rc>>, @@ -36,14 +41,17 @@ impl<'a> FloatingPaneGrid<'a> { viewport, } } - pub fn move_pane_by(&mut self, pane_id: PaneId, x: isize, y: isize) { + pub fn move_pane_by(&mut self, pane_id: PaneId, x: isize, y: isize) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} by ({x}, {y})"); + // true => succeeded to move, false => failed to move let new_pane_position = { let mut panes = self.panes.borrow_mut(); let pane = panes .iter_mut() .find(|(p_id, _p)| **p_id == pane_id) - .unwrap() + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)? .1; let mut new_pane_position = pane.position_and_size(); let min_x = self.viewport.x as isize; @@ -60,20 +68,39 @@ impl<'a> FloatingPaneGrid<'a> { new_pane_position.y = new_y as usize; new_pane_position }; - self.set_pane_geom(pane_id, new_pane_position); + self.set_pane_geom(pane_id, new_pane_position) + .with_context(err_context) } - fn set_pane_geom(&mut self, pane_id: PaneId, new_pane_geom: PaneGeom) { + + fn set_pane_geom(&mut self, pane_id: PaneId, new_pane_geom: PaneGeom) -> Result<()> { + let err_context = || { + format!( + "failed to set pane {pane_id:?} geometry to {:?}", + new_pane_geom + ) + }; + let mut panes = self.panes.borrow_mut(); let pane = panes .iter_mut() .find(|(p_id, _p)| **p_id == pane_id) - .unwrap() + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)? .1; pane.set_geom(new_pane_geom); let mut desired_pane_positions = self.desired_pane_positions.borrow_mut(); desired_pane_positions.insert(pane_id, new_pane_geom); + Ok(()) } - pub fn resize(&mut self, space: Size) { + + pub fn resize(&mut self, space: Size) -> Result<()> { + let err_context = || { + format!( + "failed to resize from {:?} to {:?}", + self.display_area, space + ) + }; + let mut panes = self.panes.borrow_mut(); let desired_pane_positions = self.desired_pane_positions.borrow(); @@ -89,7 +116,15 @@ impl<'a> FloatingPaneGrid<'a> { for (pane_id, pane) in panes.iter_mut() { let mut new_pane_geom = pane.current_geom(); - let desired_pane_geom = desired_pane_positions.get(pane_id).unwrap(); + let desired_pane_geom = desired_pane_positions + .get(pane_id) + .with_context(|| { + format!( + "failed to acquire desired pane geometry for pane {:?}", + pane_id + ) + }) + .with_context(err_context)?; let desired_pane_geom_is_inside_viewport = pane_geom_is_inside_viewport(&new_viewport, desired_pane_geom); let pane_is_in_desired_position = new_pane_geom == *desired_pane_geom; @@ -230,436 +265,340 @@ impl<'a> FloatingPaneGrid<'a> { pane.set_geom(new_pane_geom); } } + Ok(()) } - pub fn move_pane_left(&mut self, pane_id: &PaneId) { - if let Some(move_by) = self.can_move_pane_left(pane_id, MOVE_INCREMENT_HORIZONTAL) { - self.move_pane_position_left(pane_id, move_by); + + pub fn move_pane_left(&mut self, pane_id: &PaneId) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} left"); + + if let Some(move_by) = self + .can_move_pane_left(pane_id, MOVE_INCREMENT_HORIZONTAL) + .with_context(err_context)? + { + self.move_pane_position_left(pane_id, move_by) + .with_context(err_context)?; } + Ok(()) } - pub fn move_pane_right(&mut self, pane_id: &PaneId) { - if let Some(move_by) = self.can_move_pane_right(pane_id, MOVE_INCREMENT_HORIZONTAL) { - self.move_pane_position_right(pane_id, move_by); + + pub fn move_pane_right(&mut self, pane_id: &PaneId) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} right"); + + if let Some(move_by) = self + .can_move_pane_right(pane_id, MOVE_INCREMENT_HORIZONTAL) + .with_context(err_context)? + { + self.move_pane_position_right(pane_id, move_by) + .with_context(err_context)?; } + Ok(()) } - pub fn move_pane_down(&mut self, pane_id: &PaneId) { - if let Some(move_by) = self.can_move_pane_down(pane_id, MOVE_INCREMENT_VERTICAL) { - self.move_pane_position_down(pane_id, move_by); + + pub fn move_pane_down(&mut self, pane_id: &PaneId) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} down"); + + if let Some(move_by) = self + .can_move_pane_down(pane_id, MOVE_INCREMENT_VERTICAL) + .with_context(err_context)? + { + self.move_pane_position_down(pane_id, move_by) + .with_context(err_context)?; } + Ok(()) } - pub fn move_pane_up(&mut self, pane_id: &PaneId) { - if let Some(move_by) = self.can_move_pane_up(pane_id, MOVE_INCREMENT_VERTICAL) { - self.move_pane_position_up(pane_id, move_by); + + pub fn move_pane_up(&mut self, pane_id: &PaneId) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} up"); + + if let Some(move_by) = self + .can_move_pane_up(pane_id, MOVE_INCREMENT_VERTICAL) + .with_context(err_context)? + { + self.move_pane_position_up(pane_id, move_by) + .with_context(err_context)?; } + Ok(()) } - fn can_move_pane_left(&self, pane_id: &PaneId, move_by: usize) -> Option { + + fn can_move_pane_left(&self, pane_id: &PaneId, move_by: usize) -> Result> { + let err_context = || { + format!( + "failed to determine if pane {pane_id:?} can be moved left by {move_by} columns" + ) + }; + let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); + let pane = panes + .get(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let space_until_left_screen_edge = pane.x().saturating_sub(self.viewport.x); - if space_until_left_screen_edge >= move_by { + + Ok(if space_until_left_screen_edge >= move_by { Some(move_by) } else if space_until_left_screen_edge > 0 { Some(space_until_left_screen_edge) } else { None - } + }) } - fn can_move_pane_right(&self, pane_id: &PaneId, move_by: usize) -> Option { + + fn can_move_pane_right(&self, pane_id: &PaneId, move_by: usize) -> Result> { + let err_context = || { + format!( + "failed to determine if pane {pane_id:?} can be moved right by {move_by} columns" + ) + }; + let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); + let pane = panes + .get(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let space_until_right_screen_edge = (self.viewport.x + self.viewport.cols).saturating_sub(pane.x() + pane.cols()); - if space_until_right_screen_edge >= move_by { + + Ok(if space_until_right_screen_edge >= move_by { Some(move_by) } else if space_until_right_screen_edge > 0 { Some(space_until_right_screen_edge) } else { None - } + }) } - fn can_move_pane_up(&self, pane_id: &PaneId, move_by: usize) -> Option { + + fn can_move_pane_up(&self, pane_id: &PaneId, move_by: usize) -> Result> { + let err_context = + || format!("failed to determine if pane {pane_id:?} can be moved up by {move_by} rows"); + let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); + let pane = panes + .get(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let space_until_top_screen_edge = pane.y().saturating_sub(self.viewport.y); - if space_until_top_screen_edge >= move_by { + + Ok(if space_until_top_screen_edge >= move_by { Some(move_by) } else if space_until_top_screen_edge > 0 { Some(space_until_top_screen_edge) } else { None - } + }) } - fn can_move_pane_down(&self, pane_id: &PaneId, move_by: usize) -> Option { + + fn can_move_pane_down(&self, pane_id: &PaneId, move_by: usize) -> Result> { + let err_context = || { + format!("failed to determine if pane {pane_id:?} can be moved down by {move_by} rows") + }; + let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); + let pane = panes + .get(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let space_until_bottom_screen_edge = (self.viewport.y + self.viewport.rows).saturating_sub(pane.y() + pane.rows()); - if space_until_bottom_screen_edge >= move_by { + + Ok(if space_until_bottom_screen_edge >= move_by { Some(move_by) } else if space_until_bottom_screen_edge > 0 { Some(space_until_bottom_screen_edge) } else { None - } + }) } - fn move_pane_position_left(&mut self, pane_id: &PaneId, move_by: usize) { + + fn move_pane_position_left(&mut self, pane_id: &PaneId, move_by: usize) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} left by {move_by}"); + let new_pane_geom = { let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(pane_id).unwrap(); + let pane = panes + .get_mut(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let mut current_geom = pane.position_and_size(); current_geom.x -= move_by; current_geom }; - self.set_pane_geom(*pane_id, new_pane_geom); + self.set_pane_geom(*pane_id, new_pane_geom) + .with_context(err_context) } - fn move_pane_position_right(&mut self, pane_id: &PaneId, move_by: usize) { + + fn move_pane_position_right(&mut self, pane_id: &PaneId, move_by: usize) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} right by {move_by}"); + let new_pane_geom = { let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(pane_id).unwrap(); + let pane = panes + .get_mut(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let mut current_geom = pane.position_and_size(); current_geom.x += move_by; current_geom }; - self.set_pane_geom(*pane_id, new_pane_geom); + self.set_pane_geom(*pane_id, new_pane_geom) + .with_context(err_context) } - fn move_pane_position_down(&mut self, pane_id: &PaneId, move_by: usize) { + + fn move_pane_position_down(&mut self, pane_id: &PaneId, move_by: usize) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} down by {move_by}"); + let new_pane_geom = { let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(pane_id).unwrap(); + let pane = panes + .get_mut(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let mut current_geom = pane.position_and_size(); current_geom.y += move_by; current_geom }; - self.set_pane_geom(*pane_id, new_pane_geom); + self.set_pane_geom(*pane_id, new_pane_geom) + .with_context(err_context) } - fn move_pane_position_up(&mut self, pane_id: &PaneId, move_by: usize) { + + fn move_pane_position_up(&mut self, pane_id: &PaneId, move_by: usize) -> Result<()> { + let err_context = || format!("failed to move pane {pane_id:?} up by {move_by}"); + let new_pane_geom = { let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(pane_id).unwrap(); + let pane = panes + .get_mut(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)?; let mut current_geom = pane.position_and_size(); current_geom.y -= move_by; current_geom }; - self.set_pane_geom(*pane_id, new_pane_geom); - } - pub fn resize_pane_left(&'a mut self, pane_id: &PaneId) { - if let Some(increase_by) = self.can_increase_pane_size_left(pane_id, RESIZE_INCREMENT_WIDTH) - { - self.increase_pane_size_left(pane_id, increase_by); - } else if let Some(decrease_by) = - self.can_decrease_pane_size_left(pane_id, RESIZE_INCREMENT_WIDTH) - { - self.decrease_pane_size_left(pane_id, decrease_by); - } - } - pub fn resize_pane_right(&mut self, pane_id: &PaneId) { - if let Some(increase_by) = - self.can_increase_pane_size_right(pane_id, RESIZE_INCREMENT_WIDTH) - { - self.increase_pane_size_right(pane_id, increase_by); - } else if let Some(decrease_by) = - self.can_decrease_pane_size_right(pane_id, RESIZE_INCREMENT_WIDTH) - { - self.decrease_pane_size_right(pane_id, decrease_by); - } - } - pub fn resize_pane_down(&mut self, pane_id: &PaneId) { - if let Some(increase_by) = - self.can_increase_pane_size_down(pane_id, RESIZE_INCREMENT_HEIGHT) - { - self.increase_pane_size_down(pane_id, increase_by); - } else if let Some(decrease_by) = - self.can_decrease_pane_size_down(pane_id, RESIZE_INCREMENT_HEIGHT) - { - self.decrease_pane_size_down(pane_id, decrease_by); - } - } - pub fn resize_pane_up(&mut self, pane_id: &PaneId) { - if let Some(increase_by) = self.can_increase_pane_size_up(pane_id, RESIZE_INCREMENT_HEIGHT) - { - self.increase_pane_size_up(pane_id, increase_by); - } else if let Some(decrease_by) = - self.can_decrease_pane_size_up(pane_id, RESIZE_INCREMENT_HEIGHT) - { - self.decrease_pane_size_up(pane_id, decrease_by); - } - } - pub fn resize_increase(&mut self, pane_id: &PaneId) { - if let Some(increase_by) = - self.can_increase_pane_size_left(pane_id, RESIZE_INCREMENT_WIDTH / 2) - { - self.increase_pane_size_left(pane_id, increase_by); - } - if let Some(increase_by) = - self.can_increase_pane_size_right(pane_id, RESIZE_INCREMENT_WIDTH / 2) - { - self.increase_pane_size_right(pane_id, increase_by); - } - if let Some(increase_by) = - self.can_increase_pane_size_down(pane_id, RESIZE_INCREMENT_HEIGHT / 2) - { - self.increase_pane_size_down(pane_id, increase_by); - } - if let Some(increase_by) = - self.can_increase_pane_size_up(pane_id, RESIZE_INCREMENT_HEIGHT / 2) - { - self.increase_pane_size_up(pane_id, increase_by); - } + self.set_pane_geom(*pane_id, new_pane_geom) + .with_context(err_context) } - pub fn resize_decrease(&mut self, pane_id: &PaneId) { - if let Some(decrease_by) = - self.can_decrease_pane_size_left(pane_id, RESIZE_INCREMENT_WIDTH / 2) - { - self.decrease_pane_size_left(pane_id, decrease_by); - } - if let Some(decrease_by) = - self.can_decrease_pane_size_right(pane_id, RESIZE_INCREMENT_WIDTH / 2) - { - self.decrease_pane_size_right(pane_id, decrease_by); - } - if let Some(decrease_by) = - self.can_decrease_pane_size_down(pane_id, RESIZE_INCREMENT_HEIGHT / 2) - { - self.decrease_pane_size_down(pane_id, decrease_by); - } - if let Some(decrease_by) = - self.can_decrease_pane_size_up(pane_id, RESIZE_INCREMENT_HEIGHT / 2) - { - self.decrease_pane_size_up(pane_id, decrease_by); - } - } - fn can_increase_pane_size_left( - &self, - pane_id: &PaneId, - max_increase_by: usize, - ) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let distance_to_left_edge = pane.x().saturating_sub(self.viewport.x); - if distance_to_left_edge.saturating_sub(max_increase_by) > 0 { - Some(max_increase_by) - } else if distance_to_left_edge > 0 { - Some(distance_to_left_edge) - } else { - None - } - } - fn can_decrease_pane_size_left( - &self, - pane_id: &PaneId, - max_decrease_by: usize, - ) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let space_left_to_decrease = pane.cols().saturating_sub(MIN_TERMINAL_WIDTH); - if space_left_to_decrease.saturating_sub(max_decrease_by) > 0 { - Some(max_decrease_by) - } else if space_left_to_decrease > 0 { - Some(space_left_to_decrease) - } else { - None - } - } - fn can_increase_pane_size_right( - &self, - pane_id: &PaneId, - max_increase_by: usize, - ) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let distance_to_right_edge = - (self.viewport.x + self.viewport.cols).saturating_sub(pane.x() + pane.cols()); - if pane.x() + pane.cols() + max_increase_by < self.viewport.cols { - Some(max_increase_by) - } else if distance_to_right_edge > 0 { - Some(distance_to_right_edge) - } else { - None - } - } - fn can_decrease_pane_size_right( - &self, - pane_id: &PaneId, - max_decrease_by: usize, - ) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let space_left_to_decrease = pane.cols().saturating_sub(MIN_TERMINAL_WIDTH); - let pane_right_edge = pane.x() + pane.cols(); - if space_left_to_decrease.saturating_sub(max_decrease_by) > 0 - && pane.x() + max_decrease_by <= pane_right_edge + MIN_TERMINAL_WIDTH - { - Some(max_decrease_by) - } else if space_left_to_decrease > 0 - && pane.x() + max_decrease_by <= pane_right_edge + MIN_TERMINAL_WIDTH - { - Some(space_left_to_decrease) - } else { - None - } - } - fn can_increase_pane_size_down( - &self, - pane_id: &PaneId, - max_increase_by: usize, - ) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let distance_to_bottom_edge = - (self.viewport.y + self.viewport.rows).saturating_sub(pane.y() + pane.rows()); - if pane.y() + pane.rows() + max_increase_by < self.viewport.rows { - Some(max_increase_by) - } else if distance_to_bottom_edge > 0 { - Some(distance_to_bottom_edge) - } else { - None - } - } - fn can_decrease_pane_size_down( - &self, + + pub fn change_pane_size( + &mut self, pane_id: &PaneId, - max_decrease_by: usize, - ) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let space_left_to_decrease = pane.rows().saturating_sub(MIN_TERMINAL_HEIGHT); - let pane_bottom_edge = pane.y() + pane.rows(); - if space_left_to_decrease.saturating_sub(max_decrease_by) > 0 - && pane.y() + max_decrease_by <= pane_bottom_edge + MIN_TERMINAL_HEIGHT - { - Some(max_decrease_by) - } else if space_left_to_decrease > 0 - && pane.y() + max_decrease_by <= pane_bottom_edge + MIN_TERMINAL_HEIGHT - { - Some(space_left_to_decrease) - } else { - None - } - } - fn can_increase_pane_size_up(&self, pane_id: &PaneId, max_increase_by: usize) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let distance_to_top_edge = pane.y().saturating_sub(self.viewport.y); - if distance_to_top_edge.saturating_sub(max_increase_by) > 0 { - Some(max_increase_by) - } else if distance_to_top_edge > 0 { - Some(distance_to_top_edge) - } else { - None - } - } - fn can_decrease_pane_size_up(&self, pane_id: &PaneId, max_decrease_by: usize) -> Option { - let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); - let space_left_to_decrease = pane.rows().saturating_sub(MIN_TERMINAL_HEIGHT); - if space_left_to_decrease.saturating_sub(max_decrease_by) > 0 { - Some(max_decrease_by) - } else if space_left_to_decrease > 0 { - Some(space_left_to_decrease) + strategy: &ResizeStrategy, + change_by: (usize, usize), // (x, y) + ) -> Result<()> { + let err_context = || format!("failed to {strategy} for pane {pane_id:?}"); + + let mut geometry = self + .panes + .borrow() + .get(pane_id) + .with_context(|| no_pane_id(&pane_id)) + .with_context(err_context)? + .position_and_size(); + + let change_by = if strategy.direction.is_none() { + (change_by.0 / 2, change_by.1 / 2) } else { - None - } - } - fn increase_pane_size_left(&mut self, id: &PaneId, increase_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom.x -= increase_by; - current_geom - .cols - .set_inner(current_geom.cols.as_usize() + increase_by); - current_geom + change_by }; - self.set_pane_geom(*id, new_pane_geom); - } - fn decrease_pane_size_left(&mut self, id: &PaneId, decrease_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom + + // Move left border + if strategy.move_left_border_left() || strategy.move_all_borders_out() { + let increment = std::cmp::min(geometry.x.saturating_sub(self.viewport.x), change_by.0); + // Invert if on boundary already + if increment == 0 && strategy.direction.is_some() { + return self.change_pane_size(pane_id, &strategy.invert(), change_by); + } + + geometry.x -= increment; + geometry .cols - .set_inner(current_geom.cols.as_usize() - decrease_by); - current_geom - }; - self.set_pane_geom(*id, new_pane_geom); - } - fn increase_pane_size_right(&mut self, id: &PaneId, increase_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom + .set_inner(geometry.cols.as_usize() + increment); + } else if strategy.move_left_border_right() || strategy.move_all_borders_in() { + let increment = std::cmp::min( + geometry.cols.as_usize().saturating_sub(MIN_TERMINAL_WIDTH), + change_by.0, + ); + geometry.x += increment; + geometry .cols - .set_inner(current_geom.cols.as_usize() + increase_by); - current_geom + .set_inner(geometry.cols.as_usize() - increment); }; - self.set_pane_geom(*id, new_pane_geom); - } - fn decrease_pane_size_right(&mut self, id: &PaneId, decrease_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom.x += decrease_by; - current_geom + + // Move right border + if strategy.move_right_border_right() || strategy.move_all_borders_out() { + let increment = std::cmp::min( + (self.viewport.x + self.viewport.cols) + .saturating_sub(geometry.x + geometry.cols.as_usize()), + change_by.0, + ); + // Invert if on boundary already + if increment == 0 && strategy.direction.is_some() { + return self.change_pane_size(pane_id, &strategy.invert(), change_by); + } + + geometry .cols - .set_inner(current_geom.cols.as_usize() - decrease_by); - current_geom + .set_inner(geometry.cols.as_usize() + increment); + } else if strategy.move_right_border_left() || strategy.move_all_borders_in() { + let increment = std::cmp::min( + geometry.cols.as_usize().saturating_sub(MIN_TERMINAL_WIDTH), + change_by.0, + ); + geometry + .cols + .set_inner(geometry.cols.as_usize() - increment); }; - self.set_pane_geom(*id, new_pane_geom); - } - fn increase_pane_size_down(&mut self, id: &PaneId, increase_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom + + // Move upper border + if strategy.move_upper_border_up() || strategy.move_all_borders_out() { + let increment = std::cmp::min(geometry.y.saturating_sub(self.viewport.y), change_by.1); + // Invert if on boundary already + if increment == 0 && strategy.direction.is_some() { + return self.change_pane_size(pane_id, &strategy.invert(), change_by); + } + + geometry.y -= increment; + geometry .rows - .set_inner(current_geom.rows.as_usize() + increase_by); - current_geom - }; - self.set_pane_geom(*id, new_pane_geom); - } - fn decrease_pane_size_down(&mut self, id: &PaneId, decrease_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom.y += decrease_by; - current_geom + .set_inner(geometry.rows.as_usize() + increment); + } else if strategy.move_upper_border_down() || strategy.move_all_borders_in() { + let increment = std::cmp::min( + geometry.rows.as_usize().saturating_sub(MIN_TERMINAL_HEIGHT), + change_by.1, + ); + geometry.y += increment; + geometry .rows - .set_inner(current_geom.rows.as_usize() - decrease_by); - current_geom - }; - self.set_pane_geom(*id, new_pane_geom); - } - fn increase_pane_size_up(&mut self, id: &PaneId, increase_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom.y -= increase_by; - current_geom + .set_inner(geometry.rows.as_usize() - increment); + } + + // Move lower border + if strategy.move_lower_border_down() || strategy.move_all_borders_out() { + let increment = std::cmp::min( + (self.viewport.y + self.viewport.rows) + .saturating_sub(geometry.y + geometry.rows.as_usize()), + change_by.1, + ); + // Invert if on boundary already + if increment == 0 && strategy.direction.is_some() { + return self.change_pane_size(pane_id, &strategy.invert(), change_by); + } + + geometry .rows - .set_inner(current_geom.rows.as_usize() + increase_by); - pane.set_geom(current_geom); - current_geom - }; - self.set_pane_geom(*id, new_pane_geom); - } - fn decrease_pane_size_up(&mut self, id: &PaneId, decrease_by: usize) { - let new_pane_geom = { - let mut panes = self.panes.borrow_mut(); - let pane = panes.get_mut(id).unwrap(); - let mut current_geom = pane.position_and_size(); - current_geom + .set_inner(geometry.rows.as_usize() + increment); + } else if strategy.move_lower_border_up() || strategy.move_all_borders_in() { + let increment = std::cmp::min( + geometry.rows.as_usize().saturating_sub(MIN_TERMINAL_HEIGHT), + change_by.1, + ); + geometry .rows - .set_inner(current_geom.rows.as_usize() - decrease_by); - current_geom - }; - self.set_pane_geom(*id, new_pane_geom); + .set_inner(geometry.rows.as_usize() - increment); + } + + self.set_pane_geom(*pane_id, geometry) + .with_context(err_context) } + pub fn next_selectable_pane_id_to_the_left(&self, current_pane_id: &PaneId) -> Option { let panes = self.panes.borrow(); let current_pane = panes.get(current_pane_id)?; diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index bb446e8dc8..fe98ee63d5 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -1,5 +1,8 @@ mod floating_pane_grid; -use zellij_utils::position::Position; +use zellij_utils::{ + data::{Direction, ResizeStrategy}, + position::Position, +}; use crate::resize_pty; use crate::tab::Pane; @@ -25,6 +28,9 @@ use zellij_utils::{ pane_size::{Offset, PaneGeom, Size, Viewport}, }; +const RESIZE_INCREMENT_WIDTH: usize = 5; +const RESIZE_INCREMENT_HEIGHT: usize = 2; + pub struct FloatingPanes { panes: BTreeMap>, display_area: Rc>, @@ -101,34 +107,43 @@ impl FloatingPanes { &mut self, pane: Box, client_id: ClientId, - ) -> Option> { + ) -> Result> { self.active_panes .get(&client_id) + .with_context(|| format!("failed to determine active pane for client {client_id}")) .copied() .and_then(|active_pane_id| self.replace_pane(active_pane_id, pane)) + .with_context(|| format!("failed to replace active pane for client {client_id}")) } pub fn replace_pane( &mut self, pane_id: PaneId, mut with_pane: Box, - ) -> Option> { + ) -> Result> { + let err_context = || format!("failed to replace pane {pane_id:?} with pane"); + let with_pane_id = with_pane.pid(); with_pane.set_content_offset(Offset::frame(1)); - let removed_pane = self.panes.remove(&pane_id).map(|removed_pane| { - let removed_pane_id = removed_pane.pid(); - let with_pane_id = with_pane.pid(); - let removed_pane_geom = removed_pane.current_geom(); - with_pane.set_geom(removed_pane_geom); - self.panes.insert(with_pane_id, with_pane); - let z_index = self - .z_indices - .iter() - .position(|pane_id| pane_id == &removed_pane_id) - .unwrap(); - self.z_indices.remove(z_index); - self.z_indices.insert(z_index, with_pane_id); - removed_pane - }); + let removed_pane = self + .panes + .remove(&pane_id) + .with_context(|| format!("failed to remove unknown pane with ID {pane_id:?}")) + .and_then(|removed_pane| { + let removed_pane_id = removed_pane.pid(); + let with_pane_id = with_pane.pid(); + let removed_pane_geom = removed_pane.current_geom(); + with_pane.set_geom(removed_pane_geom); + self.panes.insert(with_pane_id, with_pane); + let z_index = self + .z_indices + .iter() + .position(|pane_id| pane_id == &removed_pane_id) + .context("no z-index found for pane to be removed with ID {removed_pane_id:?}") + .with_context(err_context)?; + self.z_indices.remove(z_index); + self.z_indices.insert(z_index, with_pane_id); + Ok(removed_pane) + }); // update the desired_pane_positions to relate to the new pane if let Some(desired_pane_position) = self.desired_pane_positions.remove(&pane_id) { @@ -222,7 +237,10 @@ impl FloatingPanes { pane.render_full_viewport(); } } - pub fn set_pane_frames(&mut self, os_api: &mut Box) { + pub fn set_pane_frames(&mut self, os_api: &mut Box) -> Result<()> { + let err_context = + |pane_id: &PaneId| format!("failed to activate frame on pane {pane_id:?}"); + for pane in self.panes.values_mut() { // floating panes should always have a frame unless explicitly set otherwise if !pane.borderless() { @@ -231,8 +249,9 @@ impl FloatingPanes { } else { pane.set_content_offset(Offset::default()); } - resize_pty!(pane, os_api, self.senders).unwrap(); + resize_pty!(pane, os_api, self.senders).with_context(|| err_context(&pane.pid()))?; } + Ok(()) } pub fn render(&mut self, output: &mut Output) -> Result<()> { let err_context = || "failed to render output"; @@ -297,6 +316,7 @@ impl FloatingPanes { } Ok(()) } + pub fn resize(&mut self, new_screen_size: Size) { let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); @@ -306,140 +326,28 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.resize(new_screen_size); + floating_pane_grid.resize(new_screen_size).unwrap(); self.set_force_render(); } - pub fn resize_pty_all_panes(&mut self, os_api: &mut Box) { + + pub fn resize_pty_all_panes(&mut self, os_api: &mut Box) -> Result<()> { for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); - } - } - pub fn resize_active_pane_left( - &mut self, - client_id: ClientId, - os_api: &mut Box, - ) -> bool { - // true => successfully resized - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) { - let mut floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - floating_pane_grid.resize_pane_left(active_floating_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); - } - self.set_force_render(); - return true; - } - false - } - pub fn resize_active_pane_right( - &mut self, - client_id: ClientId, - os_api: &mut Box, - ) -> bool { - // true => successfully resized - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) { - let mut floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - floating_pane_grid.resize_pane_right(active_floating_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); - } - self.set_force_render(); - return true; - } - false - } - pub fn resize_active_pane_down( - &mut self, - client_id: ClientId, - os_api: &mut Box, - ) -> bool { - // true => successfully resized - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) { - let mut floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - floating_pane_grid.resize_pane_down(active_floating_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); - } - self.set_force_render(); - return true; - } - false - } - pub fn resize_active_pane_up( - &mut self, - client_id: ClientId, - os_api: &mut Box, - ) -> bool { - // true => successfully resized - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) { - let mut floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - floating_pane_grid.resize_pane_up(active_floating_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); - } - self.set_force_render(); - return true; - } - false - } - pub fn resize_active_pane_increase( - &mut self, - client_id: ClientId, - os_api: &mut Box, - ) -> bool { - // true => successfully resized - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) { - let mut floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - floating_pane_grid.resize_increase(active_floating_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); - } - self.set_force_render(); - return true; + resize_pty!(pane, os_api, self.senders) + .with_context(|| format!("failed to resize PTY in pane {:?}", pane.pid()))?; } - false + Ok(()) } - pub fn resize_active_pane_decrease( + + pub fn resize_active_pane( &mut self, client_id: ClientId, os_api: &mut Box, - ) -> bool { + strategy: &ResizeStrategy, + ) -> Result { // true => successfully resized + let err_context = + || format!("failed to {strategy} for active floating pane for client {client_id}"); + let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) { @@ -449,238 +357,40 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.resize_decrease(active_floating_pane_id); + floating_pane_grid + .change_pane_size( + active_floating_pane_id, + strategy, + (RESIZE_INCREMENT_WIDTH, RESIZE_INCREMENT_HEIGHT), + ) + .with_context(err_context)?; + for pane in self.panes.values_mut() { - resize_pty!(pane, os_api, self.senders).unwrap(); + resize_pty!(pane, os_api, self.senders).with_context(err_context)?; } self.set_force_render(); - return true; + return Ok(true); } - false + Ok(false) } + fn set_pane_active_at(&mut self, pane_id: PaneId) { if let Some(pane) = self.panes.get_mut(&pane_id) { pane.set_active_at(Instant::now()); } } - pub fn move_focus_left( - &mut self, - client_id: ClientId, - connected_clients: &HashSet, - ) -> bool { - // true => successfully moved - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - let active_pane_id = self.active_panes.get(&client_id).copied(); - let updated_active_pane = if let Some(active_pane_id) = active_pane_id { - let floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - let next_index = - floating_pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id); - match next_index { - Some(p) => { - // render previously active pane so that its frame does not remain actively - // colored - let previously_active_pane = self - .panes - .get_mut(self.active_panes.get(&client_id).unwrap()) - .unwrap(); - - 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) - previously_active_pane.render_full_viewport(); - let next_active_pane = self.panes.get_mut(&p).unwrap(); - 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) - next_active_pane.render_full_viewport(); - - // move all clients - let connected_clients: Vec = - connected_clients.iter().copied().collect(); - for client_id in connected_clients { - self.focus_pane(p, client_id); - } - self.set_pane_active_at(p); - - self.set_force_render(); - return true; - }, - None => Some(active_pane_id), - } - } else { - active_pane_id - }; - match updated_active_pane { - Some(updated_active_pane) => { - let connected_clients: Vec = connected_clients.iter().copied().collect(); - for client_id in connected_clients { - self.focus_pane(updated_active_pane, client_id); - } - self.set_pane_active_at(updated_active_pane); - self.set_force_render(); - }, - None => { - // TODO: can this happen? - self.active_panes.clear(&mut self.panes); - self.z_indices.clear(); - }, - } - false - } - pub fn move_focus_right( + pub fn move_focus( &mut self, client_id: ClientId, connected_clients: &HashSet, - ) -> bool { + direction: &Direction, + ) -> Result { // true => successfully moved - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - let active_pane_id = self.active_panes.get(&client_id).copied(); - let updated_active_pane = if let Some(active_pane_id) = active_pane_id { - let floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - let next_index = - floating_pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id); - match next_index { - Some(p) => { - // render previously active pane so that its frame does not remain actively - // colored - let previously_active_pane = self - .panes - .get_mut(self.active_panes.get(&client_id).unwrap()) - .unwrap(); - - 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) - previously_active_pane.render_full_viewport(); - - let next_active_pane = self.panes.get_mut(&p).unwrap(); - 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) - next_active_pane.render_full_viewport(); - - // move all clients - let connected_clients: Vec = - connected_clients.iter().copied().collect(); - for client_id in connected_clients { - self.focus_pane(p, client_id); - } - - self.set_pane_active_at(p); - self.set_force_render(); - return true; - }, - None => Some(active_pane_id), - } - } else { - active_pane_id + let _err_context = || { + format!("failed to move focus of floating pane {direction:?} for client {client_id}") }; - match updated_active_pane { - Some(updated_active_pane) => { - let connected_clients: Vec = connected_clients.iter().copied().collect(); - for client_id in connected_clients { - self.focus_pane(updated_active_pane, client_id); - } - self.set_pane_active_at(updated_active_pane); - self.set_force_render(); - }, - None => { - // TODO: can this happen? - self.active_panes.clear(&mut self.panes); - self.z_indices.clear(); - }, - } - false - } - pub fn move_focus_up( - &mut self, - client_id: ClientId, - connected_clients: &HashSet, - ) -> bool { - // true => successfully moved - let display_area = *self.display_area.borrow(); - let viewport = *self.viewport.borrow(); - let active_pane_id = self.active_panes.get(&client_id).copied(); - let updated_active_pane = if let Some(active_pane_id) = active_pane_id { - let floating_pane_grid = FloatingPaneGrid::new( - &mut self.panes, - &mut self.desired_pane_positions, - display_area, - viewport, - ); - let next_index = floating_pane_grid.next_selectable_pane_id_above(&active_pane_id); - match next_index { - Some(p) => { - // render previously active pane so that its frame does not remain actively - // colored - let previously_active_pane = self - .panes - .get_mut(self.active_panes.get(&client_id).unwrap()) - .unwrap(); - - 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) - previously_active_pane.render_full_viewport(); - let next_active_pane = self.panes.get_mut(&p).unwrap(); - 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) - next_active_pane.render_full_viewport(); - - // move all clients - let connected_clients: Vec = - connected_clients.iter().copied().collect(); - for client_id in connected_clients { - self.focus_pane(p, client_id); - } - - self.set_force_render(); - self.set_pane_active_at(p); - return true; - }, - None => Some(active_pane_id), - } - } else { - active_pane_id - }; - match updated_active_pane { - Some(updated_active_pane) => { - let connected_clients: Vec = connected_clients.iter().copied().collect(); - for client_id in connected_clients { - self.focus_pane(updated_active_pane, client_id); - } - self.set_pane_active_at(updated_active_pane); - self.set_force_render(); - }, - None => { - // TODO: can this happen? - self.active_panes.clear(&mut self.panes); - self.z_indices.clear(); - }, - } - false - } - pub fn move_focus_down( - &mut self, - client_id: ClientId, - connected_clients: &HashSet, - ) -> bool { - // true => successfully moved let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); let active_pane_id = self.active_panes.get(&client_id).copied(); @@ -691,7 +401,18 @@ impl FloatingPanes { display_area, viewport, ); - let next_index = floating_pane_grid.next_selectable_pane_id_below(&active_pane_id); + let next_index = match direction { + Direction::Left => { + floating_pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id) + }, + Direction::Down => { + floating_pane_grid.next_selectable_pane_id_below(&active_pane_id) + }, + Direction::Up => floating_pane_grid.next_selectable_pane_id_above(&active_pane_id), + Direction::Right => { + floating_pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id) + }, + }; match next_index { Some(p) => { // render previously active pane so that its frame does not remain actively @@ -721,7 +442,7 @@ impl FloatingPanes { self.set_pane_active_at(p); self.set_force_render(); - return true; + return Ok(true); }, None => Some(active_pane_id), } @@ -743,8 +464,9 @@ impl FloatingPanes { self.z_indices.clear(); }, } - false + Ok(false) } + pub fn move_active_pane_down(&mut self, client_id: ClientId) { let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); @@ -755,7 +477,7 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.move_pane_down(active_pane_id); + floating_pane_grid.move_pane_down(active_pane_id).unwrap(); self.set_force_render(); } } @@ -769,7 +491,7 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.move_pane_up(active_pane_id); + floating_pane_grid.move_pane_up(active_pane_id).unwrap(); self.set_force_render(); } } @@ -783,7 +505,7 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.move_pane_left(active_pane_id); + floating_pane_grid.move_pane_left(active_pane_id).unwrap(); self.set_force_render(); } } @@ -797,7 +519,7 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.move_pane_right(active_pane_id); + floating_pane_grid.move_pane_right(active_pane_id).unwrap(); self.set_force_render(); } } @@ -869,36 +591,30 @@ impl FloatingPanes { pub fn get_pane_mut(&mut self, pane_id: PaneId) -> Option<&mut Box> { self.panes.get_mut(&pane_id) } - pub fn get_pane_id_at(&self, point: &Position, search_selectable: bool) -> Option { - if search_selectable { - // TODO: better - loop through z-indices and check each one if it contains the point - let mut selectable_panes: Vec<_> = - self.panes.iter().filter(|(_, p)| p.selectable()).collect(); - selectable_panes.sort_by(|(a_id, _a_pane), (b_id, _b_pane)| { - self.z_indices - .iter() - .position(|id| id == *b_id) - .unwrap() - .cmp(&self.z_indices.iter().position(|id| id == *a_id).unwrap()) - }); - selectable_panes - .iter() - .find(|(_, p)| p.contains(point)) - .map(|(&id, _)| id) + pub fn get_pane_id_at( + &self, + point: &Position, + search_selectable: bool, + ) -> Result> { + let _err_context = || format!("failed to determine floating pane at point {point:?}"); + + // TODO: better - loop through z-indices and check each one if it contains the point + let mut panes: Vec<_> = if search_selectable { + self.panes.iter().filter(|(_, p)| p.selectable()).collect() } else { - let mut panes: Vec<_> = self.panes.iter().collect(); - panes.sort_by(|(a_id, _a_pane), (b_id, _b_pane)| { - self.z_indices - .iter() - .position(|id| id == *b_id) - .unwrap() - .cmp(&self.z_indices.iter().position(|id| id == *a_id).unwrap()) - }); - panes - .iter() - .find(|(_, p)| p.contains(point)) - .map(|(&id, _)| id) - } + self.panes.iter().collect() + }; + panes.sort_by(|(a_id, _a_pane), (b_id, _b_pane)| { + // TODO: continue + Ord::cmp( + &self.z_indices.iter().position(|id| id == *b_id).unwrap(), + &self.z_indices.iter().position(|id| id == *a_id).unwrap(), + ) + }); + Ok(panes + .iter() + .find(|(_, p)| p.contains(point)) + .map(|(&id, _)| id)) } pub fn get_pane_at_mut( &mut self, @@ -906,6 +622,7 @@ impl FloatingPanes { search_selectable: bool, ) -> Option<&mut Box> { self.get_pane_id_at(position, search_selectable) + .unwrap() .and_then(|pane_id| self.panes.get_mut(&pane_id)) } pub fn set_pane_being_moved_with_mouse(&mut self, pane_id: PaneId, position: Position) { @@ -930,7 +647,9 @@ impl FloatingPanes { display_area, viewport, ); - floating_pane_grid.move_pane_by(pane_id, move_x_by, move_y_by); + floating_pane_grid + .move_pane_by(pane_id, move_x_by, move_y_by) + .unwrap(); self.set_pane_being_moved_with_mouse(pane_id, *click_position); self.set_force_render(); true diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index e49c865437..b5caa59169 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -2,7 +2,7 @@ mod pane_resizer; mod tiled_pane_grid; use crate::resize_pty; -use tiled_pane_grid::{split, TiledPaneGrid}; +use tiled_pane_grid::{split, TiledPaneGrid, RESIZE_PERCENT}; use crate::{ os_input_output::ServerOsApi, @@ -16,7 +16,7 @@ use crate::{ ClientId, }; use zellij_utils::{ - data::{ModeInfo, Style}, + data::{ModeInfo, ResizeStrategy, Style}, errors::prelude::*, input::{command::RunCommand, layout::SplitDirection}, pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, @@ -521,82 +521,15 @@ impl TiledPanes { } self.set_pane_frames(self.draw_pane_frames); } - pub fn resize_active_pane_left(&mut self, client_id: ClientId) { - if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let mut pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - pane_grid.resize_pane_left(&active_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api, self.senders).unwrap(); - } - self.reset_boundaries(); - } - } - pub fn resize_active_pane_right(&mut self, client_id: ClientId) { - if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let mut pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - pane_grid.resize_pane_right(&active_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api, self.senders).unwrap(); - } - self.reset_boundaries(); - } - } - pub fn resize_active_pane_up(&mut self, client_id: ClientId) { - if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let mut pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - pane_grid.resize_pane_up(&active_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api, self.senders).unwrap(); - } - self.reset_boundaries(); - } - } - pub fn resize_active_pane_down(&mut self, client_id: ClientId) { - if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let mut pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - pane_grid.resize_pane_down(&active_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api, self.senders).unwrap(); - } - self.reset_boundaries(); - } - } - pub fn resize_active_pane_increase(&mut self, client_id: ClientId) { - if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let mut pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - pane_grid.resize_increase(&active_pane_id); - for pane in self.panes.values_mut() { - resize_pty!(pane, self.os_api, self.senders).unwrap(); - } - self.reset_boundaries(); - } - } - pub fn resize_active_pane_decrease(&mut self, client_id: ClientId) { + + pub fn resize_active_pane( + &mut self, + client_id: ClientId, + strategy: &ResizeStrategy, + ) -> Result<()> { + let err_context = + || format!("failed to {strategy} for active tiled pane for client {client_id}"); + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { let mut pane_grid = TiledPaneGrid::new( &mut self.panes, @@ -604,13 +537,20 @@ impl TiledPanes { *self.display_area.borrow(), *self.viewport.borrow(), ); - pane_grid.resize_decrease(&active_pane_id); + + pane_grid + .change_pane_size(&active_pane_id, strategy, (RESIZE_PERCENT, RESIZE_PERCENT)) + .with_context(err_context)?; + for pane in self.panes.values_mut() { resize_pty!(pane, self.os_api, self.senders).unwrap(); } self.reset_boundaries(); } + + Ok(()) } + pub fn focus_next_pane(&mut self, client_id: ClientId) { let connected_clients: Vec = { self.connected_clients.borrow().iter().copied().collect() }; diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index 8667e2f6f8..37eb6c4c66 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -8,6 +8,7 @@ use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::rc::Rc; use zellij_utils::{ + errors::prelude::*, input::layout::SplitDirection, pane_size::{Constraint, Dimension, PaneGeom}, }; @@ -44,10 +45,14 @@ impl<'a> PaneResizer<'a> { } } - pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<(), String> { + pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<()> { self.solver.reset(); - let grid = self.solve(direction, space)?; - let spans = self.discretize_spans(grid, space)?; + let grid = self + .solve(direction, space) + .map_err(|err| anyhow!("{}", err))?; + let spans = self + .discretize_spans(grid, space) + .map_err(|err| anyhow!("{}", err))?; self.apply_spans(spans); Ok(()) } 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 2851564a18..fc53defc1d 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -4,7 +4,9 @@ use crate::tab::{MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH}; use crate::{panes::PaneId, tab::Pane}; use std::cmp::Reverse; use std::collections::{HashMap, HashSet}; +use zellij_utils::data::{Direction, ResizeStrategy}; use zellij_utils::{ + errors::prelude::*, input::layout::SplitDirection, pane_size::{Dimension, PaneGeom, Size, Viewport}, }; @@ -12,11 +14,16 @@ use zellij_utils::{ use std::cell::RefCell; use std::rc::Rc; -const RESIZE_PERCENT: f64 = 5.0; +pub const RESIZE_PERCENT: f64 = 5.0; const DEFAULT_CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; type BorderAndPaneIds = (usize, Vec); +// For error reporting +fn no_pane_id(pane_id: &PaneId) -> String { + format!("no floating pane with ID {:?} found", pane_id) +} + pub struct TiledPaneGrid<'a> { panes: Rc>>>, display_area: Size, // includes all panes (including eg. the status bar and tab bar in the default layout) @@ -42,189 +49,441 @@ impl<'a> TiledPaneGrid<'a> { } } - pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<(), String> { - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - pane_resizer.layout(direction, space) - } - pub fn resize_pane_left(&mut self, pane_id: &PaneId) { - // TODO: find out by how much we actually reduced and only reduce by that much - if self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { - return; - } - self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - } - pub fn resize_pane_right(&mut self, pane_id: &PaneId) { - // TODO: find out by how much we actually reduced and only reduce by that much - if self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { - return; - } - self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - } - pub fn resize_pane_down(&mut self, pane_id: &PaneId) { - // TODO: find out by how much we actually reduced and only reduce by that much - if self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT) { - return; - } - self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - } - pub fn resize_pane_up(&mut self, pane_id: &PaneId) { - // TODO: find out by how much we actually reduced and only reduce by that much - if self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT) { - return; + /// Calculates an area for each pane and sums them all. + /// + /// Returns the product of "rows * columns", summed across all panes. + #[cfg(debug_assertions)] + fn total_panes_area(&self) -> f64 { + let mut summed_area: f64 = 0.0; + + for pane in self.panes.clone().borrow().values() { + if let PaneId::Terminal(_id) = pane.pid() { + let geom = pane.current_geom(); + summed_area += match (geom.rows.as_percent(), geom.cols.as_percent()) { + (Some(rows), Some(cols)) => rows * cols, + _ => continue, + }; + } else { + continue; + } } - self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + + summed_area / (100.0 * 100.0) } - pub fn resize_increase(&mut self, pane_id: &PaneId) { - if self.try_increase_pane_and_surroundings_right_and_down(pane_id) { - return; - } - if self.try_increase_pane_and_surroundings_left_and_down(pane_id) { - return; - } - if self.try_increase_pane_and_surroundings_right_and_up(pane_id) { - return; - } - if self.try_increase_pane_and_surroundings_left_and_up(pane_id) { - return; - } - if self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { - return; - } - if self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT) { - return; - } - if self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { - return; - } - self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<()> { + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + pane_resizer.layout(direction, space) } - pub fn resize_decrease(&mut self, pane_id: &PaneId) { - if self.try_reduce_pane_and_surroundings_left_and_up(pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_right_and_up(pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_right_and_down(pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_left_and_down(pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { - return; - } - if self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { - return; - } - if self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT) { - return; + + fn pane_is_flexible(&self, direction: SplitDirection, pane_id: &PaneId) -> Result { + let err_context = + || format!("failed to determine if pane {pane_id:?} is flexible in {direction:?}"); + + let panes = self.panes.borrow(); + let pane_to_check = panes + .get(pane_id) + .with_context(|| no_pane_id(pane_id)) + .with_context(err_context)?; + let geom = pane_to_check.current_geom(); + Ok(!match direction { + SplitDirection::Vertical => geom.rows, + SplitDirection::Horizontal => geom.cols, } - self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + .is_fixed()) } - fn can_increase_pane_and_surroundings_right(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(pane_id) { - panes_to_the_right - .iter() - .all(|id| self.can_reduce_pane_width(id, increase_by)) + + // Check if panes in the desired direction can be resized. Returns the maximum resize that's + // possible (at most `change_by`). + pub fn can_change_pane_size( + &self, + pane_id: &PaneId, + strategy: &ResizeStrategy, + change_by: (f64, f64), + ) -> Result { + let err_context = || format!("failed to determine if pane {pane_id:?} can {strategy}"); + + let pane_ids = if let Some(direction) = strategy.direction { + let mut vec = self + .pane_ids_directly_next_to(pane_id, &direction) + .with_context(err_context)?; + vec.retain(|id| self.pane_is_flexible(direction.into(), id).unwrap_or(false)); + vec } else { - false - } - } - fn can_increase_pane_and_surroundings_left(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(pane_id) { - panes_to_the_left - .iter() - .all(|id| self.can_reduce_pane_width(id, increase_by)) + return Ok(true); + }; + + use zellij_utils::data::Resize::Decrease as Dec; + use zellij_utils::data::Resize::Increase as Inc; + + if !pane_ids.is_empty() { + if strategy.direction_horizontal() { + match strategy.resize { + Inc => { + for id in pane_ids { + if !self + .can_reduce_pane_width(&id, change_by.0 as f64) + .with_context(err_context)? + { + return Ok(false); + } + } + Ok(true) + }, + Dec => self + .can_reduce_pane_width(pane_id, change_by.0 as f64) + .with_context(err_context), + } + } else if strategy.direction_vertical() { + match strategy.resize { + Inc => { + for id in pane_ids { + if !self + .can_reduce_pane_height(&id, change_by.1 as f64) + .with_context(err_context)? + { + return Ok(false); + } + } + Ok(true) + }, + Dec => self + .can_reduce_pane_height(pane_id, change_by.1 as f64) + .with_context(err_context), + } + } else { + unimplemented!(); + } } else { - false + Ok(false) } } - fn can_increase_pane_and_surroundings_down(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_below) = self.pane_ids_directly_below(pane_id) { - panes_below - .iter() - .all(|id| self.can_reduce_pane_height(id, increase_by)) + + /// Change a tiled panes size based on the given strategy. + /// + /// Returns true upon successful resize, false otherwise. + pub fn change_pane_size( + &mut self, + pane_id: &PaneId, + strategy: &ResizeStrategy, + change_by: (f64, f64), + ) -> Result { + let err_context = || format!("failed to {strategy} by {change_by:?} for pane {pane_id:?}"); + + // Shorthand + use Direction as Dir; + // 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 + && 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 + self.pane_ids_directly_next_to(pane_id, &direction) + .unwrap_or_default() + .is_empty() + .then_some(true) + }) + .unwrap_or(false) + { + strategy.invert() } else { - false + *strategy + }; + + if !self + .can_change_pane_size(pane_id, &strategy, change_by) + .with_context(err_context)? + { + // Resize not possible, quit + return Ok(false); } - } - fn can_increase_pane_and_surroundings_up(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_above) = self.pane_ids_directly_above(pane_id) { - panes_above - .iter() - .all(|id| self.can_reduce_pane_height(id, increase_by)) + + if let Some(direction) = strategy.direction { + let mut neighbor_terminals = self + .pane_ids_directly_next_to(pane_id, &direction) + .with_context(err_context)?; + if neighbor_terminals.is_empty() { + // Nothing to do. + return Ok(false); + } + + 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 resize 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) + } + }); + + // Perform the resize + let change_by = match direction { + Dir::Left | Dir::Right => change_by.0, + Dir::Down | Dir::Up => change_by.1, + }; + + if strategy.resize_increase() && direction.is_horizontal() { + [*pane_id] + .iter() + .chain(&some_terminals) + .chain(&other_terminals) + .for_each(|pane| self.increase_pane_width(pane, change_by)); + neighbor_terminals + .iter() + .for_each(|pane| self.reduce_pane_width(pane, change_by)); + } else if strategy.resize_increase() && direction.is_vertical() { + [*pane_id] + .iter() + .chain(&some_terminals) + .chain(&other_terminals) + .for_each(|pane| self.increase_pane_height(pane, change_by)); + neighbor_terminals + .iter() + .for_each(|pane| self.reduce_pane_height(pane, change_by)); + } else if strategy.resize_decrease() && direction.is_horizontal() { + [*pane_id] + .iter() + .chain(&some_terminals) + .chain(&other_terminals) + .for_each(|pane| self.reduce_pane_width(pane, change_by)); + neighbor_terminals + .iter() + .for_each(|pane| self.increase_pane_width(pane, change_by)); + } else if strategy.resize_decrease() && direction.is_vertical() { + [*pane_id] + .iter() + .chain(&some_terminals) + .chain(&other_terminals) + .for_each(|pane| self.reduce_pane_height(pane, change_by)); + neighbor_terminals + .iter() + .for_each(|pane| self.increase_pane_height(pane, change_by)); + } else { + return Err(anyhow!( + "Don't know how to perform resize operation: '{strategy}'" + )) + .with_context(err_context); + } + + // Update grid + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + if direction.is_horizontal() { + pane_resizer + .layout(SplitDirection::Horizontal, self.display_area.cols) + .with_context(err_context)?; + } else { + pane_resizer + .layout(SplitDirection::Vertical, self.display_area.rows) + .with_context(err_context)?; + } } else { - false + // Get panes aligned at corners, so we can change their sizes manually afterwards + let mut aligned_panes = [ + None, // right, below + None, // left, below + None, // right, above + None, // left, above + ]; + // For the borrow checker + { + let panes = self.panes.borrow(); + let active_pane = panes + .get(pane_id) + .with_context(|| no_pane_id(pane_id)) + .with_context(err_context)?; + + for p_id in self.viewport_pane_ids_directly_below(pane_id) { + let pane = panes + .get(&p_id) + .with_context(|| no_pane_id(&p_id)) + .with_context(err_context)?; + if active_pane.x() + active_pane.cols() == pane.x() { + // right aligned + aligned_panes[0] = Some(p_id); + } else if active_pane.x() == pane.x() + pane.cols() { + // left aligned + aligned_panes[1] = Some(p_id); + } + } + for p_id in self.viewport_pane_ids_directly_above(pane_id) { + let pane = panes + .get(&p_id) + .with_context(|| no_pane_id(&p_id)) + .with_context(err_context)?; + if active_pane.x() + active_pane.cols() == pane.x() { + // right aligned + aligned_panes[2] = Some(p_id); + } else if active_pane.x() == pane.x() + pane.cols() { + // left aligned + aligned_panes[3] = Some(p_id); + } + } + } + + // Resize pane in every direction that fits + let options = [ + (Dir::Right, Some(Dir::Down), Some(Dir::Left), 0), + (Dir::Left, Some(Dir::Down), Some(Dir::Right), 1), + (Dir::Right, Some(Dir::Up), Some(Dir::Left), 2), + (Dir::Left, Some(Dir::Up), Some(Dir::Right), 3), + (Dir::Right, None, None, 0), + (Dir::Down, None, None, 0), + (Dir::Left, None, None, 0), + (Dir::Up, None, None, 0), + ]; + + for (main_dir, sub_dir, adjust_dir, adjust_pane) in options { + if let Some(sub_dir) = sub_dir { + let main_strategy = ResizeStrategy { + direction: Some(main_dir), + invert_on_boundaries: false, + ..strategy + }; + let sub_strategy = ResizeStrategy { + direction: Some(sub_dir), + invert_on_boundaries: false, + ..strategy + }; + + if self.can_change_pane_size(pane_id, &main_strategy, change_by)? + && self.can_change_pane_size(pane_id, &sub_strategy, change_by)? + { + let result = self + .change_pane_size(pane_id, &main_strategy, change_by) + .and_then(|ret| { + Ok(ret + && self.change_pane_size(pane_id, &sub_strategy, change_by)?) + }) + .and_then(|ret| { + if let Some(aligned_pane) = aligned_panes[adjust_pane] { + Ok(ret + && self.change_pane_size( + &aligned_pane, + &ResizeStrategy { + direction: adjust_dir, + invert_on_boundaries: false, + resize: strategy.resize.invert(), + }, + change_by, + )?) + } else { + Ok(ret) + } + }) + .with_context(err_context)?; + return Ok(result); + } + } else { + let new_strategy = ResizeStrategy { + direction: Some(main_dir), + invert_on_boundaries: false, + ..strategy + }; + if self + .change_pane_size(pane_id, &new_strategy, change_by) + .with_context(err_context)? + { + return Ok(true); + } + } + } + return Ok(false); } + + #[cfg(debug_assertions)] + { + let area = self.total_panes_area() * 100.0; + debug_assert!( + f64::abs(area - 100.0) < 1.0, // Tolerate a little rounding error + "area consumed by panes doesn't fill the viewport! Total area is {area} % + During operation: '{strategy}', on pane {pane_id:?}", + ); + } + + Ok(true) } - fn can_reduce_pane_width(&self, pane_id: &PaneId, reduce_by: f64) -> bool { + + fn can_reduce_pane_width(&self, pane_id: &PaneId, reduce_by: f64) -> Result { + let err_context = + || format!("failed to determine if pane {pane_id:?} can reduce width by {reduce_by} %"); + let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); + let pane = panes + .get(pane_id) + .with_context(|| no_pane_id(pane_id)) + .with_context(err_context)?; let current_fixed_cols = pane.position_and_size().cols.as_usize(); let will_reduce_by = ((self.display_area.cols as f64 / 100.0) * reduce_by) as usize; if current_fixed_cols.saturating_sub(will_reduce_by) < pane.min_width() { - false + Ok(false) } else if let Some(cols) = pane.position_and_size().cols.as_percent() { - cols - reduce_by >= RESIZE_PERCENT + Ok(cols - reduce_by >= RESIZE_PERCENT) } else { - false + Ok(false) } } - fn can_reduce_pane_height(&self, pane_id: &PaneId, reduce_by: f64) -> bool { + fn can_reduce_pane_height(&self, pane_id: &PaneId, reduce_by: f64) -> Result { + let err_context = || { + format!("failed to determine if pane {pane_id:?} can reduce height by {reduce_by} %") + }; + let panes = self.panes.borrow(); - let pane = panes.get(pane_id).unwrap(); + let pane = panes + .get(pane_id) + .with_context(|| no_pane_id(pane_id)) + .with_context(err_context)?; let current_fixed_rows = pane.position_and_size().rows.as_usize(); let will_reduce_by = ((self.display_area.rows as f64 / 100.0) * reduce_by) as usize; if current_fixed_rows.saturating_sub(will_reduce_by) < pane.min_height() { - false + Ok(false) } else if let Some(rows) = pane.position_and_size().rows.as_percent() { - rows - reduce_by >= RESIZE_PERCENT + Ok(rows - reduce_by >= RESIZE_PERCENT) } else { - false - } - } - fn can_reduce_pane_and_surroundings_right(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let ids_left = self.pane_ids_directly_left_of(pane_id); - let flexible_left = self.ids_are_flexible(SplitDirection::Horizontal, ids_left); - if flexible_left { - self.can_reduce_pane_width(pane_id, reduce_by) - } else { - false - } - } - fn can_reduce_pane_and_surroundings_left(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let ids_right = self.pane_ids_directly_right_of(pane_id); - let flexible_right = self.ids_are_flexible(SplitDirection::Horizontal, ids_right); - if flexible_right { - self.can_reduce_pane_width(pane_id, reduce_by) - } else { - false - } - } - fn can_reduce_pane_and_surroundings_down(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let ids_above = self.pane_ids_directly_above(pane_id); - let flexible_above = self.ids_are_flexible(SplitDirection::Vertical, ids_above); - if flexible_above { - self.can_reduce_pane_height(pane_id, reduce_by) - } else { - false - } - } - fn can_reduce_pane_and_surroundings_up(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let ids_below = self.pane_ids_directly_below(pane_id); - let flexible_below = self.ids_are_flexible(SplitDirection::Vertical, ids_below); - if flexible_below { - self.can_reduce_pane_height(pane_id, reduce_by) - } else { - false + Ok(false) } } + fn reduce_pane_height(&mut self, id: &PaneId, percent: f64) { - let mut panes = self.panes.borrow_mut(); - let terminal = panes.get_mut(id).unwrap(); - terminal.reduce_height(percent); + if self.can_reduce_pane_height(id, percent).unwrap() { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.reduce_height(percent); + } } fn increase_pane_height(&mut self, id: &PaneId, percent: f64) { let mut panes = self.panes.borrow_mut(); @@ -237,708 +496,192 @@ impl<'a> TiledPaneGrid<'a> { terminal.increase_width(percent); } fn reduce_pane_width(&mut self, id: &PaneId, percent: f64) { - let mut panes = self.panes.borrow_mut(); - let terminal = panes.get_mut(id).unwrap(); - terminal.reduce_width(percent); - } - fn increase_pane_and_surroundings_up(&mut self, id: &PaneId, percent: f64) { - let mut terminals_above = self - .pane_ids_directly_above(id) - .expect("can't increase pane size up if there are no terminals above"); - let terminal_borders_above: HashSet = terminals_above - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.top_aligned_contiguous_panes_to_the_left(id, &terminal_borders_above); - let (right_resize_border, terminals_to_the_right) = - self.top_aligned_contiguous_panes_to_the_right(id, &terminal_borders_above); - terminals_above.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - self.increase_pane_height(id, percent); - for terminal_id in terminals_above { - self.reduce_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.increase_pane_height(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_down(&mut self, id: &PaneId, percent: f64) { - let mut terminals_below = self - .pane_ids_directly_below(id) - .expect("can't increase pane size down if there are no terminals below"); - let terminal_borders_below: HashSet = terminals_below - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.bottom_aligned_contiguous_panes_to_the_left(id, &terminal_borders_below); - let (right_resize_border, terminals_to_the_right) = - self.bottom_aligned_contiguous_panes_to_the_right(id, &terminal_borders_below); - terminals_below.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - self.increase_pane_height(id, percent); - for terminal_id in terminals_below { - self.reduce_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.increase_pane_height(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_right(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_right = self - .pane_ids_directly_right_of(id) - .expect("can't increase pane size right if there are no terminals to the right"); - let terminal_borders_to_the_right: HashSet = terminals_to_the_right - .iter() - .map(|t| { - return self.panes.borrow().get(t).unwrap().y(); - }) - .collect(); - let (top_resize_border, terminals_above) = - self.right_aligned_contiguous_panes_above(id, &terminal_borders_to_the_right); - let (bottom_resize_border, terminals_below) = - self.right_aligned_contiguous_panes_below(id, &terminal_borders_to_the_right); - terminals_to_the_right.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - self.increase_pane_width(id, percent); - for terminal_id in terminals_to_the_right { - self.reduce_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.increase_pane_width(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_left(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_left = self - .pane_ids_directly_left_of(id) - .expect("can't increase pane size right if there are no terminals to the right"); - let terminal_borders_to_the_left: HashSet = terminals_to_the_left - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().y()) - .collect(); - let (top_resize_border, terminals_above) = - self.left_aligned_contiguous_panes_above(id, &terminal_borders_to_the_left); - let (bottom_resize_border, terminals_below) = - self.left_aligned_contiguous_panes_below(id, &terminal_borders_to_the_left); - terminals_to_the_left.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - self.increase_pane_width(id, percent); - for terminal_id in terminals_to_the_left { - self.reduce_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.increase_pane_width(terminal_id, percent); + if self.can_reduce_pane_width(id, percent).unwrap() { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.reduce_width(percent); } } - fn reduce_pane_and_surroundings_up(&mut self, id: &PaneId, percent: f64) { - let mut terminals_below = self - .pane_ids_directly_below(id) - .expect("can't reduce pane size up if there are no terminals below"); - let terminal_borders_below: HashSet = terminals_below - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.bottom_aligned_contiguous_panes_to_the_left(id, &terminal_borders_below); - let (right_resize_border, terminals_to_the_right) = - self.bottom_aligned_contiguous_panes_to_the_right(id, &terminal_borders_below); - terminals_below.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - let panes = self.panes.borrow(); - let pane = panes.get(terminal_id).unwrap(); - if pane.current_geom().rows.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_height(id, percent); - for terminal_id in terminals_below { - self.increase_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.reduce_pane_height(terminal_id, percent); - } - } - fn reduce_pane_and_surroundings_down(&mut self, id: &PaneId, percent: f64) { - let mut terminals_above = self - .pane_ids_directly_above(id) - .expect("can't reduce pane size down if there are no terminals above"); - let terminal_borders_above: HashSet = terminals_above - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.top_aligned_contiguous_panes_to_the_left(id, &terminal_borders_above); - let (right_resize_border, terminals_to_the_right) = - self.top_aligned_contiguous_panes_to_the_right(id, &terminal_borders_above); - terminals_above.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - let panes = self.panes.borrow(); - let pane = panes.get(terminal_id).unwrap(); - if pane.current_geom().rows.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } + /// Return a vector of [`PaneId`]s directly adjacent to the given [`PaneId`], if any. + /// + /// The vector is empty for example if the given pane (`id`) is at the boundary of the viewport + /// already. + fn pane_ids_directly_next_to(&self, id: &PaneId, direction: &Direction) -> Result> { + let err_context = || format!("failed to find panes {direction} from pane {id:?}"); - self.reduce_pane_height(id, percent); - for terminal_id in terminals_above { - self.increase_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.reduce_pane_height(terminal_id, percent); - } - } - fn reduce_pane_and_surroundings_right(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_left = self - .pane_ids_directly_left_of(id) - .expect("can't reduce pane size right if there are no terminals to the left"); - let terminal_borders_to_the_left: HashSet = terminals_to_the_left - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().y()) - .collect(); - let (top_resize_border, terminals_above) = - self.left_aligned_contiguous_panes_above(id, &terminal_borders_to_the_left); - let (bottom_resize_border, terminals_below) = - self.left_aligned_contiguous_panes_below(id, &terminal_borders_to_the_left); - terminals_to_the_left.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_above.iter().chain(&terminals_below) { - let panes = self.panes.borrow(); - let pane = panes.get(terminal_id).unwrap(); - if pane.current_geom().cols.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_width(id, percent); - for terminal_id in terminals_to_the_left { - self.increase_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.reduce_pane_width(terminal_id, percent); - } - } - fn reduce_pane_and_surroundings_left(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_right = self - .pane_ids_directly_right_of(id) - .expect("can't reduce pane size left if there are no terminals to the right"); - let terminal_borders_to_the_right: HashSet = terminals_to_the_right - .iter() - .map(|t| self.panes.borrow().get(t).unwrap().y()) - .collect(); - let (top_resize_border, terminals_above) = - self.right_aligned_contiguous_panes_above(id, &terminal_borders_to_the_right); - let (bottom_resize_border, terminals_below) = - self.right_aligned_contiguous_panes_below(id, &terminal_borders_to_the_right); - terminals_to_the_right.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_above.iter().chain(&terminals_below) { - let panes = self.panes.borrow(); - let pane = panes.get(terminal_id).unwrap(); - if pane.current_geom().cols.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_width(id, percent); - for terminal_id in terminals_to_the_right { - self.increase_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.reduce_pane_width(terminal_id, percent); - } - } - fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option> { let panes = self.panes.borrow(); let mut ids = vec![]; - let terminal_to_check = panes.get(id).unwrap(); - if terminal_to_check.x() == 0 { - return None; - } - // for (&pid, terminal) in self.get_panes() { - for (&pid, terminal) in panes.iter() { - if terminal.x() + terminal.cols() == terminal_to_check.x() { - ids.push(pid); - } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn pane_ids_directly_right_of(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let panes = self.panes.borrow(); - let terminal_to_check = panes.get(id).unwrap(); - // for (&pid, terminal) in self.get_panes() { - for (&pid, terminal) in panes.iter() { - if terminal.x() == terminal_to_check.x() + terminal_to_check.cols() { - ids.push(pid); - } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn pane_ids_directly_below(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let panes = self.panes.borrow(); - let terminal_to_check = panes.get(id).unwrap(); - // for (&pid, terminal) in self.get_panes() { + let terminal_to_check = panes + .get(id) + .with_context(|| no_pane_id(id)) + .with_context(err_context)?; + for (&pid, terminal) in panes.iter() { - if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() { - ids.push(pid); + // We cannot resize plugin panes, so we do not even bother trying. + if let PaneId::Plugin(_) = pid { + continue; } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn pane_ids_directly_above(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let panes = self.panes.borrow(); - let terminal_to_check = panes.get(id).unwrap(); - // for (&pid, terminal) in self.get_panes() { - for (&pid, terminal) in panes.iter() { - if terminal.y() + terminal.rows() == terminal_to_check.y() { + + if match direction { + Direction::Left => (terminal.x() + terminal.cols()) == terminal_to_check.x(), + Direction::Down => { + terminal.y() == (terminal_to_check.y() + terminal_to_check.rows()) + }, + Direction::Up => (terminal.y() + terminal.rows()) == terminal_to_check.y(), + Direction::Right => { + terminal.x() == (terminal_to_check.x() + terminal_to_check.cols()) + }, + } { ids.push(pid); } } - if ids.is_empty() { - None - } else { - Some(ids) - } + Ok(ids) } - fn pane_ids_top_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { - let panes = self.panes.borrow(); - let pane_to_check = panes.get(pane_id).unwrap(); - panes - .iter() - .filter(|(p_id, p)| *p_id != pane_id && p.y() == pane_to_check.y()) - .map(|(p_id, _p)| *p_id) - .collect() - } - fn pane_ids_bottom_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { - let panes = self.panes.borrow(); - let pane_to_check = panes.get(pane_id).unwrap(); - panes - .iter() - .filter(|(p_id, p)| { - *p_id != pane_id && p.y() + p.rows() == pane_to_check.y() + pane_to_check.rows() - }) - .map(|(p_id, _p)| *p_id) - .collect() - } - fn pane_ids_right_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { - let panes = self.panes.borrow(); - let pane_to_check = panes.get(pane_id).unwrap(); - panes - .iter() - .filter(|(p_id, p)| { - *p_id != pane_id && p.x() + p.cols() == pane_to_check.x() + pane_to_check.cols() - }) - .map(|(p_id, _p)| *p_id) - .collect() - } - fn pane_ids_left_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { - let panes = self.panes.borrow(); - let pane_to_check = panes.get(pane_id).unwrap(); - panes - .iter() - .filter(|(p_id, p)| *p_id != pane_id && p.x() == pane_to_check.x()) - .map(|(p_id, _p)| *p_id) - .collect() - } - fn right_aligned_contiguous_panes_above( - &self, - id: &PaneId, - pane_borders_to_the_right: &HashSet, - ) -> BorderAndPaneIds { - let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut right_aligned_panes: Vec<_> = self - .pane_ids_right_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - // panes that are next to each other up to current - right_aligned_panes.sort_by_key(|a| Reverse(a.y())); - for pane in right_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.y() + pane.rows() == pane_to_check.y() { - result_panes.push(pane); - } - } - // top-most border aligned with a pane border to the right - let mut top_resize_border = 0; - for pane in &result_panes { - let bottom_pane_boundary = pane.y() + pane.rows(); - if pane_borders_to_the_right - .get(&bottom_pane_boundary) - .is_some() - && top_resize_border < bottom_pane_boundary - { - top_resize_border = bottom_pane_boundary; - } - } - result_panes.retain(|pane| pane.y() >= top_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let top_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.y() - } else { - top_resize_border - }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (top_resize_border, pane_ids) - } - fn right_aligned_contiguous_panes_below( - &self, - id: &PaneId, - pane_borders_to_the_right: &HashSet, - ) -> BorderAndPaneIds { - let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut right_aligned_panes: Vec<_> = self - .pane_ids_right_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - // panes that are next to each other up to current - right_aligned_panes.sort_by_key(|a| a.y()); - for pane in right_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.y() == pane_to_check.y() + pane_to_check.rows() { - result_panes.push(pane); - } - } - // bottom-most border aligned with a pane border to the right - let mut bottom_resize_border = self.viewport.y + self.viewport.rows; - for pane in &result_panes { - let top_pane_boundary = pane.y(); - if pane_borders_to_the_right - .get(&(top_pane_boundary)) - .is_some() - && top_pane_boundary < bottom_resize_border - { - bottom_resize_border = top_pane_boundary; - } - } - result_panes.retain(|pane| pane.y() + pane.rows() <= bottom_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let bottom_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.y() + pane_to_check.rows() - } else { - bottom_resize_border - }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (bottom_resize_border, pane_ids) - } - fn left_aligned_contiguous_panes_above( - &self, - id: &PaneId, - pane_borders_to_the_left: &HashSet, - ) -> BorderAndPaneIds { - let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut left_aligned_panes: Vec<_> = self - .pane_ids_left_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - // panes that are next to each other up to current - left_aligned_panes.sort_by_key(|a| Reverse(a.y())); - for pane in left_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.y() + pane.rows() == pane_to_check.y() { - result_panes.push(pane); - } - } - // top-most border aligned with a pane border to the right - let mut top_resize_border = 0; - for pane in &result_panes { - let bottom_pane_boundary = pane.y() + pane.rows(); - if pane_borders_to_the_left - .get(&bottom_pane_boundary) - .is_some() - && top_resize_border < bottom_pane_boundary - { - top_resize_border = bottom_pane_boundary; - } - } - result_panes.retain(|pane| pane.y() >= top_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let top_resize_border = if panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.y() - } else { - top_resize_border - }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (top_resize_border, pane_ids) - } - fn left_aligned_contiguous_panes_below( + + /// Return a vector of [`PaneId`]s aligned with the given [`PaneId`] on the `direction` border. + fn pane_ids_aligned_with( &self, - id: &PaneId, - pane_borders_to_the_left: &HashSet, - ) -> BorderAndPaneIds { + pane_id: &PaneId, + direction: &Direction, + ) -> Result> { + let err_context = || format!("failed to find panes aligned {direction} with {pane_id:?}"); + let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut left_aligned_panes: Vec<_> = self - .pane_ids_left_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - // panes that are next to each other up to current - left_aligned_panes.sort_by_key(|a| a.y()); - for pane in left_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.y() == pane_to_check.y() + pane_to_check.rows() { - result_panes.push(pane); + let pane_to_check = panes + .get(pane_id) + .with_context(|| no_pane_id(pane_id)) + .with_context(err_context)?; + let mut result = vec![]; + + for (p_id, pane) in panes.iter() { + if p_id == pane_id { + continue; } - } - // bottom-most border aligned with a pane border to the left - let mut bottom_resize_border = self.viewport.y + self.viewport.rows; - for pane in &result_panes { - let top_pane_boundary = pane.y(); - if pane_borders_to_the_left.get(&(top_pane_boundary)).is_some() - && top_pane_boundary < bottom_resize_border - { - bottom_resize_border = top_pane_boundary; + + if match direction { + Direction::Left => pane.x() == pane_to_check.x(), + Direction::Down => { + (pane.y() + pane.rows()) == (pane_to_check.y() + pane_to_check.rows()) + }, + Direction::Up => pane.y() == pane_to_check.y(), + Direction::Right => { + (pane.x() + pane.cols()) == (pane_to_check.x() + pane_to_check.cols()) + }, + } { + result.push(*p_id) } } - result_panes.retain(|pane| pane.y() + pane.rows() <= bottom_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let bottom_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.y() + pane_to_check.rows() - } else { - bottom_resize_border - }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (bottom_resize_border, pane_ids) + Ok(result) } - fn top_aligned_contiguous_panes_to_the_left( + + /// Searches for contiguous panes + fn contiguous_panes_with_alignment( &self, id: &PaneId, - pane_borders_above: &HashSet, - ) -> BorderAndPaneIds { - let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut top_aligned_panes: Vec<_> = self - .pane_ids_top_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - // panes that are next to each other up to current - top_aligned_panes.sort_by_key(|a| Reverse(a.x())); - for pane in top_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.x() + pane.cols() == pane_to_check.x() { - result_panes.push(pane); - } - } - // leftmost border aligned with a pane border above - let mut left_resize_border = 0; - for pane in &result_panes { - let right_pane_boundary = pane.x() + pane.cols(); - if pane_borders_above.get(&right_pane_boundary).is_some() - && left_resize_border < right_pane_boundary - { - left_resize_border = right_pane_boundary - } - } - result_panes.retain(|pane| pane.x() >= left_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let left_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.x() - } else { - left_resize_border + border: &HashSet, + alignment: &Direction, + direction: &Direction, + ) -> Result { + let err_context = || { + format!("failed to find contiguous panes {direction} from pane {id:?} with {alignment} alignment") }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (left_resize_border, pane_ids) - } - fn top_aligned_contiguous_panes_to_the_right( - &self, - id: &PaneId, - pane_borders_above: &HashSet, - ) -> BorderAndPaneIds { + let input_error = + anyhow!("Invalid combination of alignment ({alignment}) and direction ({direction})"); + let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut top_aligned_panes: Vec<_> = self - .pane_ids_top_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - // panes that are next to each other up to current - top_aligned_panes.sort_by_key(|a| a.x()); - for pane in top_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.x() == pane_to_check.x() + pane_to_check.cols() { - result_panes.push(pane); - } - } - // rightmost border aligned with a pane border above - let mut right_resize_border = self.viewport.x + self.viewport.cols; - for pane in &result_panes { - let left_pane_boundary = pane.x(); - if pane_borders_above.get(&left_pane_boundary).is_some() - && right_resize_border > left_pane_boundary - { - right_resize_border = left_pane_boundary; - } - } - result_panes.retain(|pane| pane.x() + pane.cols() <= right_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let right_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.x() + pane_to_check.cols() - } else { - right_resize_border + let pane_to_check = panes + .get(id) + .with_context(|| no_pane_id(id)) + .with_context(err_context)?; + let mut result = vec![]; + let mut aligned_panes: Vec<_> = self + .pane_ids_aligned_with(id, alignment) + .and_then(|pane_ids| { + Ok(pane_ids + .iter() + .map(|p_id| panes.get(p_id).unwrap()) // <-- TODO: Bad unwrap! + .collect()) + }) + .with_context(err_context)?; + + use Direction::Down as D; + use Direction::Left as L; + use Direction::Right as R; + use Direction::Up as U; + + match (alignment, direction) { + (&R, &U) | (&L, &U) => aligned_panes.sort_by_key(|a| Reverse(a.y())), + (&R, &D) | (&L, &D) => aligned_panes.sort_by_key(|a| a.y()), + (&D, &L) | (&U, &L) => aligned_panes.sort_by_key(|a| Reverse(a.x())), + (&D, &R) | (&U, &R) => aligned_panes.sort_by_key(|a| a.x()), + _ => return Err(input_error).with_context(err_context), }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (right_resize_border, pane_ids) - } - fn bottom_aligned_contiguous_panes_to_the_left( - &self, - id: &PaneId, - pane_borders_below: &HashSet, - ) -> BorderAndPaneIds { - let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut bottom_aligned_panes: Vec<_> = self - .pane_ids_bottom_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - bottom_aligned_panes.sort_by_key(|a| Reverse(a.x())); - // panes that are next to each other up to current - for pane in bottom_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.x() + pane.cols() == pane_to_check.x() { - result_panes.push(pane); - } - } - // leftmost border aligned with a pane border above - let mut left_resize_border = 0; - for pane in &result_panes { - let right_pane_boundary = pane.x() + pane.cols(); - if pane_borders_below.get(&right_pane_boundary).is_some() - && left_resize_border < right_pane_boundary - { - left_resize_border = right_pane_boundary; + + for pane in aligned_panes { + let pane_to_check = result.last().unwrap_or(&pane_to_check); + if match (alignment, direction) { + (&R, &U) | (&L, &U) => (pane.y() + pane.rows()) == pane_to_check.y(), + (&R, &D) | (&L, &D) => pane.y() == (pane_to_check.y() + pane_to_check.rows()), + (&D, &L) | (&U, &L) => (pane.x() + pane.cols()) == pane_to_check.x(), + (&D, &R) | (&U, &R) => pane.x() == (pane_to_check.x() + pane_to_check.cols()), + _ => return Err(input_error).with_context(err_context), + } { + result.push(pane); } } - result_panes.retain(|terminal| terminal.x() >= left_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let left_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.x() - } else { - left_resize_border + + let mut resize_border = match direction { + &L => 0, + &D => self.viewport.y + self.viewport.rows, + &U => 0, + &R => self.viewport.x + self.viewport.cols, }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (left_resize_border, pane_ids) - } - fn bottom_aligned_contiguous_panes_to_the_right( - &self, - id: &PaneId, - pane_borders_below: &HashSet, - ) -> BorderAndPaneIds { - let panes = self.panes.borrow(); - let mut result_panes = vec![]; - let mut bottom_aligned_panes: Vec<_> = self - .pane_ids_bottom_aligned_with_pane_id(id) - .iter() - .map(|p_id| panes.get(p_id).unwrap()) - .collect(); - bottom_aligned_panes.sort_by_key(|a| a.x()); - // panes that are next to each other up to current - for pane in bottom_aligned_panes { - let pane_to_check = panes.get(id).unwrap(); - let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); - if pane.x() == pane_to_check.x() + pane_to_check.cols() { - result_panes.push(pane); + + for pane in &result { + let pane_boundary = match direction { + &L => pane.x() + pane.cols(), + &D => pane.y(), + &U => pane.y() + pane.rows(), + &R => pane.x(), + }; + if border.get(&pane_boundary).is_some() { + match direction { + &R | &D => { + if pane_boundary < resize_border { + resize_border = pane_boundary + } + }, + &L | &U => { + if pane_boundary > resize_border { + resize_border = pane_boundary + } + }, + } } } - // leftmost border aligned with a pane border above - let mut right_resize_border = self.viewport.x + self.viewport.cols; - for pane in &result_panes { - let left_pane_boundary = pane.x(); - if pane_borders_below.get(&left_pane_boundary).is_some() - && right_resize_border > left_pane_boundary - { - right_resize_border = left_pane_boundary; + result.retain(|pane| match direction { + &L => pane.x() >= resize_border, + &D => (pane.y() + pane.rows()) <= resize_border, + &U => pane.y() >= resize_border, + &R => (pane.x() + pane.cols()) <= resize_border, + }); + + let resize_border = if result.is_empty() { + match direction { + &L => pane_to_check.x(), + &D => pane_to_check.y() + pane_to_check.rows(), + &U => pane_to_check.y(), + &R => pane_to_check.x() + pane_to_check.cols(), } - } - result_panes.retain(|terminal| terminal.x() + terminal.cols() <= right_resize_border); - let right_resize_border = if result_panes.is_empty() { - let pane_to_check = panes.get(id).unwrap(); - pane_to_check.x() + pane_to_check.cols() } else { - right_resize_border + resize_border }; - let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); - (right_resize_border, pane_ids) - } - fn ids_are_flexible(&self, direction: SplitDirection, pane_ids: Option>) -> bool { - let panes = self.panes.borrow(); - pane_ids.is_some() - && pane_ids.unwrap().iter().all(|id| { - let pane_to_check = panes.get(id).unwrap(); - let geom = pane_to_check.current_geom(); - let dimension = match direction { - SplitDirection::Vertical => geom.rows, - SplitDirection::Horizontal => geom.cols, - }; - !dimension.is_fixed() - }) + let pane_ids: Vec = result.iter().map(|t| t.pid()).collect(); + + Ok((resize_border, pane_ids)) } + fn pane_is_between_vertical_borders( &self, id: &PaneId, @@ -949,6 +692,7 @@ impl<'a> TiledPaneGrid<'a> { let pane = panes.get(id).unwrap(); pane.x() >= left_border_x && pane.x() + pane.cols() <= right_border_x } + fn pane_is_between_horizontal_borders( &self, id: &PaneId, @@ -959,341 +703,27 @@ impl<'a> TiledPaneGrid<'a> { let pane = panes.get(id).unwrap(); pane.y() >= top_border_y && pane.y() + pane.rows() <= bottom_border_y } - fn try_increase_pane_and_surroundings_right( - &mut self, - pane_id: &PaneId, - reduce_by: f64, - ) -> bool { - if self.can_increase_pane_and_surroundings_right(pane_id, reduce_by) { - self.increase_pane_and_surroundings_right(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); - return true; - } - false - } - fn try_increase_pane_and_surroundings_left( - &mut self, - pane_id: &PaneId, - reduce_by: f64, - ) -> bool { - if self.can_increase_pane_and_surroundings_left(pane_id, reduce_by) { - self.increase_pane_and_surroundings_left(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); - return true; - } - false - } - fn try_increase_pane_and_surroundings_up(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_increase_pane_and_surroundings_up(pane_id, reduce_by) { - self.increase_pane_and_surroundings_up(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); - return true; - } - false - } - fn try_increase_pane_and_surroundings_down( - &mut self, - pane_id: &PaneId, - reduce_by: f64, - ) -> bool { - if self.can_increase_pane_and_surroundings_down(pane_id, reduce_by) { - self.increase_pane_and_surroundings_down(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); - return true; - } - false - } - fn try_increase_pane_and_surroundings_right_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_right = - self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_increase_pane_up = - self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_increase_pane_right && can_increase_pane_up { - let pane_above_with_right_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_right_aligned_border) = pane_above_with_right_aligned_border - { - self.try_reduce_pane_and_surroundings_right( - &pane_above_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_increase_pane_and_surroundings_left_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_left = - self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_increase_pane_up = - self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_increase_pane_left && can_increase_pane_up { - let pane_above_with_left_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_left_aligned_border) = pane_above_with_left_aligned_border { - self.try_reduce_pane_and_surroundings_left( - &pane_above_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_increase_pane_and_surroundings_right_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_right = - self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_increase_pane_down = - self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_increase_pane_right && can_increase_pane_down { - let pane_below_with_right_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_right_aligned_border) = pane_below_with_right_aligned_border - { - self.try_reduce_pane_and_surroundings_right( - &pane_below_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_increase_pane_and_surroundings_left_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_left = - self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_increase_pane_down = - self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_increase_pane_left && can_increase_pane_down { - let pane_below_with_left_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_left_aligned_border) = pane_below_with_left_aligned_border { - self.try_reduce_pane_and_surroundings_left( - &pane_below_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_right_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_right = - self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_reduce_pane_up = self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_reduce_pane_right && can_reduce_pane_up { - let pane_below_with_left_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_left_aligned_border) = pane_below_with_left_aligned_border { - self.try_increase_pane_and_surroundings_right( - &pane_below_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_left_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_left = - self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_reduce_pane_up = self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_reduce_pane_left && can_reduce_pane_up { - let pane_below_with_right_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_right_aligned_border) = pane_below_with_right_aligned_border - { - self.try_increase_pane_and_surroundings_left( - &pane_below_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_right_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_right = - self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_reduce_pane_down = - self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_reduce_pane_right && can_reduce_pane_down { - let pane_above_with_left_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_left_aligned_border) = pane_above_with_left_aligned_border { - self.try_increase_pane_and_surroundings_right( - &pane_above_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_left_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_left = - self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_reduce_pane_down = - self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_reduce_pane_left && can_reduce_pane_down { - let pane_above_with_right_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let panes = self.panes.borrow(); - let pane = panes.get(p_id).unwrap(); - let active_pane = panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_right_aligned_border) = pane_above_with_right_aligned_border - { - self.try_increase_pane_and_surroundings_left( - &pane_above_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_right(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_right(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_right(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); - return true; - } - false - } - fn try_reduce_pane_and_surroundings_left(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_left(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_left(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols); - return true; - } - false - } - fn try_reduce_pane_and_surroundings_up(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_up(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_up(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); - return true; - } - false - } - fn try_reduce_pane_and_surroundings_down(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_down(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_down(pane_id, reduce_by); - let mut pane_resizer = PaneResizer::new(self.panes.clone()); - let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows); - return true; - } - false - } + fn viewport_pane_ids_directly_above(&self, pane_id: &PaneId) -> Vec { - self.pane_ids_directly_above(pane_id) + self.pane_ids_directly_next_to(pane_id, &Direction::Up) .unwrap_or_default() .into_iter() .filter(|id| self.is_inside_viewport(id)) .collect() } + fn viewport_pane_ids_directly_below(&self, pane_id: &PaneId) -> Vec { - self.pane_ids_directly_below(pane_id) + self.pane_ids_directly_next_to(pane_id, &Direction::Down) .unwrap_or_default() .into_iter() .filter(|id| self.is_inside_viewport(id)) .collect() } + fn is_inside_viewport(&self, pane_id: &PaneId) -> bool { is_inside_viewport(&self.viewport, self.panes.borrow().get(pane_id).unwrap()) } + pub fn next_selectable_pane_id(&self, current_pane_id: &PaneId) -> PaneId { let panes = self.panes.borrow(); let mut panes: Vec<(&PaneId, &&mut Box)> = @@ -1317,6 +747,7 @@ impl<'a> TiledPaneGrid<'a> { .unwrap(); next_active_pane_id } + pub fn previous_selectable_pane_id(&self, current_pane_id: &PaneId) -> PaneId { let panes = self.panes.borrow(); let mut panes: Vec<(&PaneId, &&mut Box)> = @@ -1341,6 +772,7 @@ impl<'a> TiledPaneGrid<'a> { }; previous_active_pane_id } + pub fn next_selectable_pane_id_to_the_left(&self, current_pane_id: &PaneId) -> Option { let panes = self.panes.borrow(); let current_pane = panes.get(current_pane_id)?; @@ -1445,24 +877,25 @@ impl<'a> TiledPaneGrid<'a> { let upper_close_border = pane.y(); let lower_close_border = pane.y() + pane.rows(); - if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&id) { - let mut selectable_panes: Vec<_> = panes_to_the_left - .into_iter() - .filter(|pid| panes.get(pid).unwrap().selectable()) - .collect(); - let pane_borders_to_the_left = self.horizontal_borders(&selectable_panes); - if pane_borders_to_the_left.contains(&upper_close_border) - && pane_borders_to_the_left.contains(&lower_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_horizontal_borders( - t, - upper_close_border, - lower_close_border, - ) - }); - return Some(selectable_panes); - } + let panes_to_the_left = self + .pane_ids_directly_next_to(&id, &Direction::Left) + .unwrap(); + let mut selectable_panes: Vec<_> = panes_to_the_left + .into_iter() + .filter(|pid| panes.get(pid).unwrap().selectable()) + .collect(); + let pane_borders_to_the_left = self.horizontal_borders(&selectable_panes); + if pane_borders_to_the_left.contains(&upper_close_border) + && pane_borders_to_the_left.contains(&lower_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_horizontal_borders( + t, + upper_close_border, + lower_close_border, + ) + }); + return Some(selectable_panes); } } None @@ -1473,24 +906,25 @@ impl<'a> TiledPaneGrid<'a> { let upper_close_border = pane.y(); let lower_close_border = pane.y() + pane.rows(); - if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&id) { - let mut selectable_panes: Vec<_> = panes_to_the_right - .into_iter() - .filter(|pid| panes.get(pid).unwrap().selectable()) - .collect(); - let pane_borders_to_the_right = self.horizontal_borders(&selectable_panes); - if pane_borders_to_the_right.contains(&upper_close_border) - && pane_borders_to_the_right.contains(&lower_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_horizontal_borders( - t, - upper_close_border, - lower_close_border, - ) - }); - return Some(selectable_panes); - } + let panes_to_the_right = self + .pane_ids_directly_next_to(&id, &Direction::Right) + .unwrap(); + let mut selectable_panes: Vec<_> = panes_to_the_right + .into_iter() + .filter(|pid| panes.get(pid).unwrap().selectable()) + .collect(); + let pane_borders_to_the_right = self.horizontal_borders(&selectable_panes); + if pane_borders_to_the_right.contains(&upper_close_border) + && pane_borders_to_the_right.contains(&lower_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_horizontal_borders( + t, + upper_close_border, + lower_close_border, + ) + }); + return Some(selectable_panes); } } None @@ -1501,24 +935,19 @@ impl<'a> TiledPaneGrid<'a> { let left_close_border = pane.x(); let right_close_border = pane.x() + pane.cols(); - if let Some(panes_above) = self.pane_ids_directly_above(&id) { - let mut selectable_panes: Vec<_> = panes_above - .into_iter() - .filter(|pid| panes.get(pid).unwrap().selectable()) - .collect(); - let pane_borders_above = self.vertical_borders(&selectable_panes); - if pane_borders_above.contains(&left_close_border) - && pane_borders_above.contains(&right_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_vertical_borders( - t, - left_close_border, - right_close_border, - ) - }); - return Some(selectable_panes); - } + let panes_above = self.pane_ids_directly_next_to(&id, &Direction::Up).unwrap(); + let mut selectable_panes: Vec<_> = panes_above + .into_iter() + .filter(|pid| panes.get(pid).unwrap().selectable()) + .collect(); + let pane_borders_above = self.vertical_borders(&selectable_panes); + if pane_borders_above.contains(&left_close_border) + && pane_borders_above.contains(&right_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_vertical_borders(t, left_close_border, right_close_border) + }); + return Some(selectable_panes); } } None @@ -1529,24 +958,21 @@ impl<'a> TiledPaneGrid<'a> { let left_close_border = pane.x(); let right_close_border = pane.x() + pane.cols(); - if let Some(panes_below) = self.pane_ids_directly_below(&id) { - let mut selectable_panes: Vec<_> = panes_below - .into_iter() - .filter(|pid| panes[pid].selectable()) - .collect(); - let pane_borders_below = self.vertical_borders(&selectable_panes); - if pane_borders_below.contains(&left_close_border) - && pane_borders_below.contains(&right_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_vertical_borders( - t, - left_close_border, - right_close_border, - ) - }); - return Some(selectable_panes); - } + let panes_below = self + .pane_ids_directly_next_to(&id, &Direction::Down) + .unwrap(); + let mut selectable_panes: Vec<_> = panes_below + .into_iter() + .filter(|pid| panes[pid].selectable()) + .collect(); + let pane_borders_below = self.vertical_borders(&selectable_panes); + if pane_borders_below.contains(&left_close_border) + && pane_borders_below.contains(&right_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_vertical_borders(t, left_close_border, right_close_border) + }); + return Some(selectable_panes); } } None diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 0153f31023..584bff3f95 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -9,10 +9,10 @@ use crate::{ }; use zellij_utils::{ channels::SenderWithContext, - data::Event, + data::{Direction, Event, ResizeStrategy}, errors::prelude::*, input::{ - actions::{Action, Direction, ResizeDirection, SearchDirection, SearchOption}, + actions::{Action, SearchDirection, SearchOption}, command::TerminalAction, get_mode_info, }, @@ -102,15 +102,9 @@ pub(crate) fn route_action( .send_to_screen(ScreenInstruction::Render) .with_context(err_context)?; }, - Action::Resize(direction) => { - let screen_instr = match direction { - ResizeDirection::Left => ScreenInstruction::ResizeLeft(client_id), - ResizeDirection::Right => ScreenInstruction::ResizeRight(client_id), - ResizeDirection::Up => ScreenInstruction::ResizeUp(client_id), - ResizeDirection::Down => ScreenInstruction::ResizeDown(client_id), - ResizeDirection::Increase => ScreenInstruction::ResizeIncrease(client_id), - ResizeDirection::Decrease => ScreenInstruction::ResizeDecrease(client_id), - }; + Action::Resize(resize, direction) => { + let screen_instr = + ScreenInstruction::Resize(client_id, ResizeStrategy::new(resize, direction)); session .senders .send_to_screen(screen_instr) diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 7b2b6237e0..f304383bec 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -5,6 +5,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::rc::Rc; use std::str; +use zellij_utils::data::{Direction, Resize, ResizeStrategy}; use zellij_utils::errors::prelude::*; use zellij_utils::input::command::RunCommand; use zellij_utils::input::options::Clipboard; @@ -137,12 +138,7 @@ pub enum ScreenInstruction { HorizontalSplit(PaneId, Option, HoldForCommand, ClientId), VerticalSplit(PaneId, Option, HoldForCommand, ClientId), WriteCharacter(Vec, ClientId), - ResizeLeft(ClientId), - ResizeRight(ClientId), - ResizeDown(ClientId), - ResizeUp(ClientId), - ResizeIncrease(ClientId), - ResizeDecrease(ClientId), + Resize(ClientId, ResizeStrategy), SwitchFocus(ClientId), FocusNextPane(ClientId), FocusPreviousPane(ClientId), @@ -245,12 +241,30 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::HorizontalSplit(..) => ScreenContext::HorizontalSplit, ScreenInstruction::VerticalSplit(..) => ScreenContext::VerticalSplit, ScreenInstruction::WriteCharacter(..) => ScreenContext::WriteCharacter, - ScreenInstruction::ResizeLeft(..) => ScreenContext::ResizeLeft, - ScreenInstruction::ResizeRight(..) => ScreenContext::ResizeRight, - ScreenInstruction::ResizeDown(..) => ScreenContext::ResizeDown, - ScreenInstruction::ResizeUp(..) => ScreenContext::ResizeUp, - ScreenInstruction::ResizeIncrease(..) => ScreenContext::ResizeIncrease, - ScreenInstruction::ResizeDecrease(..) => ScreenContext::ResizeDecrease, + ScreenInstruction::Resize(.., strategy) => match strategy { + ResizeStrategy { + resize: Resize::Increase, + direction, + .. + } => match direction { + Some(Direction::Left) => ScreenContext::ResizeIncreaseLeft, + Some(Direction::Down) => ScreenContext::ResizeIncreaseDown, + Some(Direction::Up) => ScreenContext::ResizeIncreaseUp, + Some(Direction::Right) => ScreenContext::ResizeIncreaseRight, + None => ScreenContext::ResizeIncreaseAll, + }, + ResizeStrategy { + resize: Resize::Decrease, + direction, + .. + } => match direction { + Some(Direction::Left) => ScreenContext::ResizeDecreaseLeft, + Some(Direction::Down) => ScreenContext::ResizeDecreaseDown, + Some(Direction::Up) => ScreenContext::ResizeDecreaseUp, + Some(Direction::Right) => ScreenContext::ResizeDecreaseRight, + None => ScreenContext::ResizeDecreaseAll, + }, + }, ScreenInstruction::SwitchFocus(..) => ScreenContext::SwitchFocus, ScreenInstruction::FocusNextPane(..) => ScreenContext::FocusNextPane, ScreenInstruction::FocusPreviousPane(..) => ScreenContext::FocusPreviousPane, @@ -1491,56 +1505,12 @@ pub(crate) fn screen_thread_main( screen.update_tabs()?; } }, - ScreenInstruction::ResizeLeft(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab.resize_left(client_id) - ); - screen.unblock_input()?; - screen.render()?; - }, - ScreenInstruction::ResizeRight(client_id) => { + ScreenInstruction::Resize(client_id, strategy) => { active_tab_and_connected_client_id!( screen, client_id, - |tab: &mut Tab, client_id: ClientId| tab.resize_right(client_id) - ); - screen.unblock_input()?; - screen.render()?; - }, - ScreenInstruction::ResizeDown(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab.resize_down(client_id) - ); - screen.unblock_input()?; - screen.render()?; - }, - ScreenInstruction::ResizeUp(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab.resize_up(client_id) - ); - screen.unblock_input()?; - screen.render()?; - }, - ScreenInstruction::ResizeIncrease(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab.resize_increase(client_id) - ); - screen.unblock_input()?; - screen.render()?; - }, - ScreenInstruction::ResizeDecrease(client_id) => { - active_tab_and_connected_client_id!( - screen, - client_id, - |tab: &mut Tab, client_id: ClientId| tab.resize_decrease(client_id) + |tab: &mut Tab, client_id: ClientId| tab.resize(client_id, strategy), + ? ); screen.unblock_input()?; screen.render()?; diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 75bb358253..63573a08e6 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -7,6 +7,7 @@ mod copy_command; use copy_command::CopyCommand; use std::env::temp_dir; use uuid::Uuid; +use zellij_utils::data::{Direction, ResizeStrategy}; use zellij_utils::errors::prelude::*; use zellij_utils::input::command::RunCommand; use zellij_utils::position::{Column, Line}; @@ -966,6 +967,7 @@ impl Tab { let replaced_pane = if self.floating_panes.panes_are_visible() { self.floating_panes .replace_active_pane(Box::new(new_pane), client_id) + .ok() } else { self.tiled_panes .replace_active_pane(Box::new(new_pane), client_id) @@ -1290,7 +1292,7 @@ impl Tab { let err_context = || format!("failed to write to terminal at position {position:?}"); if self.floating_panes.panes_are_visible() { - let pane_id = self.floating_panes.get_pane_id_at(position, false); + let pane_id = self.floating_panes.get_pane_id_at(position, false).unwrap(); if let Some(pane_id) = pane_id { self.write_to_pane_id(input_bytes, pane_id) .with_context(err_context)?; @@ -1580,81 +1582,27 @@ impl Tab { } pub fn resize_whole_tab(&mut self, new_screen_size: Size) { self.floating_panes.resize(new_screen_size); - self.floating_panes.resize_pty_all_panes(&mut self.os_api); // we need to do this explicitly because floating_panes.resize does not do this + self.floating_panes + .resize_pty_all_panes(&mut self.os_api) + .unwrap(); // we need to do this explicitly because floating_panes.resize does not do this self.tiled_panes.resize(new_screen_size); self.should_clear_display_before_rendering = true; } - pub fn resize_left(&mut self, client_id: ClientId) { + pub fn resize(&mut self, client_id: ClientId, strategy: ResizeStrategy) -> Result<()> { if self.floating_panes.panes_are_visible() { let successfully_resized = self .floating_panes - .resize_active_pane_left(client_id, &mut self.os_api); + .resize_active_pane(client_id, &mut self.os_api, &strategy) + .unwrap(); if successfully_resized { 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_left(client_id); - } - } - pub fn resize_right(&mut self, client_id: ClientId) { - if self.floating_panes.panes_are_visible() { - let successfully_resized = self - .floating_panes - .resize_active_pane_right(client_id, &mut self.os_api); - if successfully_resized { - 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_right(client_id); - } - } - pub fn resize_down(&mut self, client_id: ClientId) { - if self.floating_panes.panes_are_visible() { - let successfully_resized = self - .floating_panes - .resize_active_pane_down(client_id, &mut self.os_api); - if successfully_resized { - 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_down(client_id); - } - } - pub fn resize_up(&mut self, client_id: ClientId) { - if self.floating_panes.panes_are_visible() { - let successfully_resized = self - .floating_panes - .resize_active_pane_up(client_id, &mut self.os_api); - if successfully_resized { - 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_up(client_id); - } - } - pub fn resize_increase(&mut self, client_id: ClientId) { - if self.floating_panes.panes_are_visible() { - let successfully_resized = self - .floating_panes - .resize_active_pane_increase(client_id, &mut self.os_api); - if successfully_resized { - 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_increase(client_id); - } - } - pub fn resize_decrease(&mut self, client_id: ClientId) { - if self.floating_panes.panes_are_visible() { - let successfully_resized = self - .floating_panes - .resize_active_pane_decrease(client_id, &mut self.os_api); - if successfully_resized { - 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_decrease(client_id); + self.tiled_panes + .resize_active_pane(client_id, &strategy) + .unwrap(); } + Ok(()) } fn set_pane_active_at(&mut self, pane_id: PaneId) { if let Some(pane) = self.tiled_panes.get_pane_mut(pane_id) { @@ -1686,10 +1634,13 @@ impl Tab { // returns a boolean that indicates whether the focus moved pub fn move_focus_left(&mut self, client_id: ClientId) -> bool { if self.floating_panes.panes_are_visible() { - self.floating_panes.move_focus_left( - client_id, - &self.connected_clients.borrow().iter().copied().collect(), - ) + self.floating_panes + .move_focus( + client_id, + &self.connected_clients.borrow().iter().copied().collect(), + &Direction::Left, + ) + .unwrap() } else { if !self.has_selectable_panes() { return false; @@ -1703,10 +1654,13 @@ impl Tab { } pub fn move_focus_down(&mut self, client_id: ClientId) -> bool { if self.floating_panes.panes_are_visible() { - self.floating_panes.move_focus_down( - client_id, - &self.connected_clients.borrow().iter().copied().collect(), - ) + self.floating_panes + .move_focus( + client_id, + &self.connected_clients.borrow().iter().copied().collect(), + &Direction::Down, + ) + .unwrap() } else { if !self.has_selectable_panes() { return false; @@ -1719,10 +1673,13 @@ impl Tab { } pub fn move_focus_up(&mut self, client_id: ClientId) -> bool { if self.floating_panes.panes_are_visible() { - self.floating_panes.move_focus_up( - client_id, - &self.connected_clients.borrow().iter().copied().collect(), - ) + self.floating_panes + .move_focus( + client_id, + &self.connected_clients.borrow().iter().copied().collect(), + &Direction::Up, + ) + .unwrap() } else { if !self.has_selectable_panes() { return false; @@ -1736,10 +1693,13 @@ impl Tab { // returns a boolean that indicates whether the focus moved pub fn move_focus_right(&mut self, client_id: ClientId) -> bool { if self.floating_panes.panes_are_visible() { - self.floating_panes.move_focus_right( - client_id, - &self.connected_clients.borrow().iter().copied().collect(), - ) + self.floating_panes + .move_focus( + client_id, + &self.connected_clients.borrow().iter().copied().collect(), + &Direction::Right, + ) + .unwrap() } else { if !self.has_selectable_panes() { return false; @@ -1920,7 +1880,9 @@ impl Tab { .and_then(|suppressed_pane| { let suppressed_pane_id = suppressed_pane.pid(); let replaced_pane = if self.are_floating_panes_visible() { - self.floating_panes.replace_pane(pane_id, suppressed_pane) + Some(self.floating_panes.replace_pane(pane_id, suppressed_pane)) + .transpose() + .unwrap() } else { self.tiled_panes.replace_pane(pane_id, suppressed_pane) }; @@ -2179,7 +2141,11 @@ impl Tab { search_selectable: bool, ) -> Result>> { if self.floating_panes.panes_are_visible() { - if let Some(pane_id) = self.floating_panes.get_pane_id_at(point, search_selectable) { + if let Some(pane_id) = self + .floating_panes + .get_pane_id_at(point, search_selectable) + .unwrap() + { return Ok(self.floating_panes.get_pane_mut(pane_id)); } } @@ -2321,7 +2287,7 @@ impl Tab { || format!("failed to focus pane at position {point:?} for client {client_id}"); if self.floating_panes.panes_are_visible() { - if let Some(clicked_pane) = self.floating_panes.get_pane_id_at(point, true) { + if let Some(clicked_pane) = self.floating_panes.get_pane_id_at(point, true).unwrap() { self.floating_panes.focus_pane(clicked_pane, client_id); self.set_pane_active_at(clicked_pane); return Ok(()); diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index c635b125e4..81be51cc89 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -11,6 +11,9 @@ use crate::{ }; use std::path::PathBuf; use zellij_utils::channels::Receiver; +use zellij_utils::data::Direction; +use zellij_utils::data::Resize; +use zellij_utils::data::ResizeStrategy; use zellij_utils::envs::set_session_name; use zellij_utils::errors::{prelude::*, ErrorContext}; use zellij_utils::input::layout::{Layout, PaneLayout}; @@ -765,7 +768,8 @@ fn increase_floating_pane_size() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.resize_increase(client_id); + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -794,7 +798,8 @@ fn decrease_floating_pane_size() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.resize_decrease(client_id); + tab.resize(client_id, ResizeStrategy::new(Resize::Decrease, None)) + .unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -823,7 +828,11 @@ fn resize_floating_pane_left() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.resize_left(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Left)), + ) + .unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -852,7 +861,11 @@ fn resize_floating_pane_right() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.resize_right(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Right)), + ) + .unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -881,7 +894,11 @@ fn resize_floating_pane_up() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.resize_up(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Up)), + ) + .unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -910,7 +927,11 @@ fn resize_floating_pane_down() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.resize_down(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Down)), + ) + .unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 4ddd0fda84..a676f3c681 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -8,6 +8,7 @@ use crate::{ ClientId, }; use std::path::PathBuf; +use zellij_utils::data::{Direction, Resize, ResizeStrategy}; use zellij_utils::errors::prelude::*; use zellij_utils::input::layout::PaneLayout; use zellij_utils::ipc::IpcReceiverWithContext; @@ -98,6 +99,43 @@ impl ServerOsApi for FakeInputOutput { } } +fn tab_resize_increase(tab: &mut Tab, id: ClientId) { + tab.resize(id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); +} + +fn tab_resize_left(tab: &mut Tab, id: ClientId) { + tab.resize( + id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Left)), + ) + .unwrap(); +} + +fn tab_resize_down(tab: &mut Tab, id: ClientId) { + tab.resize( + id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Down)), + ) + .unwrap(); +} + +fn tab_resize_up(tab: &mut Tab, id: ClientId) { + tab.resize( + id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Up)), + ) + .unwrap(); +} + +fn tab_resize_right(tab: &mut Tab, id: ClientId) { + tab.resize( + id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Right)), + ) + .unwrap(); +} + fn create_new_tab(size: Size) -> Tab { let index = 0; let position = 0; @@ -1521,7 +1559,7 @@ pub fn close_pane_with_multiple_panes_above_it_away_from_screen_edges() { tab.horizontal_split(new_pane_id_5, None, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); tab.vertical_split(new_pane_id_6, None, 1).unwrap(); tab.move_focus_down(1); tab.close_focused_pane(1).unwrap(); @@ -1820,7 +1858,7 @@ pub fn close_pane_with_multiple_panes_below_it_away_from_screen_edges() { tab.move_focus_right(1); tab.horizontal_split(new_pane_id_5, None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); tab.vertical_split(new_pane_id_6, None, 1).unwrap(); tab.move_focus_up(1); tab.close_focused_pane(1).unwrap(); @@ -2122,9 +2160,9 @@ pub fn close_pane_with_multiple_panes_to_the_left_away_from_screen_edges() { tab.vertical_split(new_pane_id_5, None, 1).unwrap(); tab.move_focus_up(1); tab.move_focus_left(1); - tab.resize_right(1); - tab.resize_up(1); - tab.resize_up(1); + tab_resize_right(&mut tab, 1); + tab_resize_up(&mut tab, 1); + tab_resize_up(&mut tab, 1); tab.horizontal_split(new_pane_id_6, None, 1).unwrap(); tab.move_focus_right(1); tab.close_focused_pane(1).unwrap(); @@ -2425,9 +2463,9 @@ pub fn close_pane_with_multiple_panes_to_the_right_away_from_screen_edges() { tab.move_focus_down(1); tab.vertical_split(new_pane_id_5, None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_left(1); - tab.resize_up(1); - tab.resize_up(1); + tab_resize_left(&mut tab, 1); + tab_resize_up(&mut tab, 1); + tab_resize_up(&mut tab, 1); tab.horizontal_split(new_pane_id_6, None, 1).unwrap(); tab.move_focus_left(1); tab.close_focused_pane(1).unwrap(); @@ -3145,7 +3183,7 @@ pub fn resize_down_with_pane_above() { let mut tab = create_new_tab(size); let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -3252,7 +3290,7 @@ pub fn resize_down_with_pane_below() { let new_pane_id = PaneId::Terminal(2); tab.horizontal_split(new_pane_id, None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -3366,7 +3404,7 @@ pub fn resize_down_with_panes_above_and_below() { tab.horizontal_split(new_pane_id_1, None, 1).unwrap(); tab.horizontal_split(new_pane_id_2, None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -3520,7 +3558,7 @@ pub fn resize_down_with_multiple_panes_above() { tab.move_focus_up(1); tab.vertical_split(new_pane_id_2, None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -3676,7 +3714,7 @@ pub fn resize_down_with_panes_above_aligned_left_with_current_pane() { tab.move_focus_up(1); tab.vertical_split(pane_above, None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -3875,7 +3913,7 @@ pub fn resize_down_with_panes_below_aligned_left_with_current_pane() { tab.vertical_split(pane_below, None, 1).unwrap(); tab.move_focus_up(1); tab.vertical_split(focused_pane, None, 1).unwrap(); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -4076,7 +4114,7 @@ pub fn resize_down_with_panes_above_aligned_right_with_current_pane() { tab.vertical_split(pane_above_and_right, None, 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -4276,7 +4314,7 @@ pub fn resize_down_with_panes_below_aligned_right_with_current_pane() { tab.move_focus_up(1); tab.vertical_split(pane_to_the_right, None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -4475,7 +4513,7 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_current_pane() { tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_down(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -4759,7 +4797,7 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_current_pane() { tab.vertical_split(PaneId::Terminal(5), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -5047,7 +5085,7 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_panes_to_the_lef tab.vertical_split(PaneId::Terminal(7), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -5423,7 +5461,7 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_to_the_left_and_ tab.move_focus_left(1); tab.move_focus_up(1); tab.move_focus_left(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -5786,7 +5824,7 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() { let mut tab = create_new_tab(size); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -5827,7 +5865,7 @@ pub fn resize_left_with_pane_to_the_left() { }; let mut tab = create_new_tab(size); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -5931,7 +5969,7 @@ pub fn resize_left_with_pane_to_the_right() { let mut tab = create_new_tab(size); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -6037,7 +6075,7 @@ pub fn resize_left_with_panes_to_the_left_and_right() { tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -6186,7 +6224,7 @@ pub fn resize_left_with_multiple_panes_to_the_left() { tab.move_focus_left(1); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_right(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -6337,7 +6375,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_with_current_pane() { tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -6532,7 +6570,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_with_current_pane() { tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -6725,7 +6763,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_bottom_with_current_pane() { tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -6919,7 +6957,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_bottom_with_current_pane() { tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -7118,7 +7156,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_current_pa tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -7404,7 +7442,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_current_p tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -7688,12 +7726,12 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abov tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(5), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(7), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8063,13 +8101,13 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_panes_abo tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(5), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_left(1); tab.horizontal_split(PaneId::Terminal(7), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8428,7 +8466,7 @@ pub fn cannot_resize_left_when_pane_to_the_left_is_at_minimum_width() { let size = Size { cols: 10, rows: 20 }; let mut tab = create_new_tab(size); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); - tab.resize_left(1); + tab_resize_left(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8469,7 +8507,7 @@ pub fn resize_right_with_pane_to_the_left() { }; let mut tab = create_new_tab(size); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8574,7 +8612,7 @@ pub fn resize_right_with_pane_to_the_right() { let mut tab = create_new_tab(size); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8680,7 +8718,7 @@ pub fn resize_right_with_panes_to_the_left_and_right() { tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8830,7 +8868,7 @@ pub fn resize_right_with_multiple_panes_to_the_left() { tab.move_focus_left(1); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_right(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -8981,7 +9019,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_with_current_pane() { tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_right(1); tab.horizontal_split(PaneId::Terminal(4), None, 1).unwrap(); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -9175,7 +9213,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_with_current_pane() { tab.move_focus_right(1); tab.horizontal_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -9370,7 +9408,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_bottom_with_current_pane() { tab.move_focus_right(1); tab.horizontal_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -9566,7 +9604,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_bottom_with_current_pane() { tab.horizontal_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_up(1); tab.move_focus_left(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -9765,7 +9803,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_current_p tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -10050,7 +10088,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_current_ tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_down(1); tab.move_focus_left(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -10333,12 +10371,12 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abo tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(5), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(7), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -10707,13 +10745,13 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_panes_ab tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(5), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_left(1); tab.horizontal_split(PaneId::Terminal(7), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11071,7 +11109,7 @@ pub fn cannot_resize_right_when_pane_to_the_left_is_at_minimum_width() { let size = Size { cols: 10, rows: 20 }; let mut tab = create_new_tab(size); tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); - tab.resize_right(1); + tab_resize_right(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11113,7 +11151,7 @@ pub fn resize_up_with_pane_above() { }; let mut tab = create_new_tab(size); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11219,7 +11257,7 @@ pub fn resize_up_with_pane_below() { let mut tab = create_new_tab(size); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11329,7 +11367,7 @@ pub fn resize_up_with_panes_above_and_below() { tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11479,7 +11517,7 @@ pub fn resize_up_with_multiple_panes_above() { tab.move_focus_up(1); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_down(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11629,7 +11667,7 @@ pub fn resize_up_with_panes_above_aligned_left_with_current_pane() { tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_down(1); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -11825,7 +11863,7 @@ pub fn resize_up_with_panes_below_aligned_left_with_current_pane() { tab.move_focus_down(1); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_up(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -12021,7 +12059,7 @@ pub fn resize_up_with_panes_above_aligned_right_with_current_pane() { tab.move_focus_down(1); tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -12218,7 +12256,7 @@ pub fn resize_up_with_panes_below_aligned_right_with_current_pane() { tab.vertical_split(PaneId::Terminal(4), None, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -12415,7 +12453,7 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_current_pane() { tab.vertical_split(PaneId::Terminal(5), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -12699,7 +12737,7 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_current_pane() { tab.vertical_split(PaneId::Terminal(6), None, 1).unwrap(); tab.move_focus_left(1); tab.move_focus_up(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -12986,7 +13024,7 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_panes_to_the_left_ tab.vertical_split(PaneId::Terminal(7), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -13360,7 +13398,7 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_to_the_left_and_ri tab.vertical_split(PaneId::Terminal(7), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(8), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_up(1); + tab_resize_up(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -13722,7 +13760,7 @@ pub fn cannot_resize_up_when_pane_above_is_at_minimum_height() { }; let mut tab = create_new_tab(size); tab.horizontal_split(PaneId::Terminal(2), None, 1).unwrap(); - tab.resize_down(1); + tab_resize_down(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -13755,7 +13793,7 @@ pub fn nondirectional_resize_increase_with_1_pane() { rows: 10, }; let mut tab = create_new_tab(size); - tab.resize_increase(1); + tab_resize_increase(&mut tab, 1); assert_eq!( tab.get_active_pane(1).unwrap().position_and_size().y, @@ -13779,7 +13817,7 @@ pub fn nondirectional_resize_increase_with_1_pane_to_left() { let mut tab = create_new_tab(size); let new_pane_id_1 = PaneId::Terminal(2); tab.vertical_split(new_pane_id_1, None, 1).unwrap(); - tab.resize_increase(1); + tab_resize_increase(&mut tab, 1); // should behave like `resize_left_with_pane_to_the_left` assert_eq!( @@ -13815,7 +13853,7 @@ pub fn nondirectional_resize_increase_with_2_panes_to_left() { tab.move_focus_left(1); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_right(1); - tab.resize_increase(1); + tab_resize_increase(&mut tab, 1); // should behave like `resize_left_with_multiple_panes_to_the_left` assert_eq!( @@ -13872,7 +13910,7 @@ pub fn nondirectional_resize_increase_with_1_pane_to_right_1_pane_above() { tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.move_focus_left(1); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); - tab.resize_increase(1); + tab_resize_increase(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -13928,7 +13966,7 @@ pub fn nondirectional_resize_increase_with_1_pane_to_right_1_pane_to_left() { tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_increase(1); + tab_resize_increase(&mut tab, 1); assert_eq!( tab.tiled_panes @@ -13984,7 +14022,7 @@ pub fn nondirectional_resize_increase_with_pane_above_aligned_right_with_current tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap(); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); tab.move_focus_left(1); - tab.resize_increase(1); + tab_resize_increase(&mut tab, 1); assert_eq!( tab.tiled_panes diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 89e8b38659..2c512bd801 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -10,8 +10,9 @@ use crate::{ use insta::assert_snapshot; use std::path::PathBuf; use zellij_utils::cli::CliAction; +use zellij_utils::data::Resize; use zellij_utils::errors::{prelude::*, ErrorContext}; -use zellij_utils::input::actions::{Action, Direction, ResizeDirection}; +use zellij_utils::input::actions::Action; use zellij_utils::input::command::{RunCommand, TerminalAction}; use zellij_utils::input::layout::{PaneLayout, SplitDirection}; use zellij_utils::input::options::Options; @@ -28,7 +29,7 @@ use zellij_utils::ipc::PixelDimensions; use zellij_utils::{ channels::{self, ChannelWithContext, Receiver}, - data::{InputMode, ModeInfo, Palette, PluginCapabilities}, + data::{Direction, InputMode, ModeInfo, Palette, PluginCapabilities}, interprocess::local_socket::LocalSocketStream, ipc::{ClientAttributes, ClientToServerMsg, ServerToClientMsg}, }; @@ -997,7 +998,8 @@ pub fn send_cli_resize_action_to_screen() { server_receiver ); let resize_cli_action = CliAction::Resize { - resize_direction: ResizeDirection::Left, + resize: Resize::Increase, + direction: Some(Direction::Left), }; send_cli_action_to_server( &session_metadata, diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index d048b8e500..2397f5bae2 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -9,10 +9,14 @@ keybinds { } resize { bind "Ctrl n" { SwitchToMode "Normal"; } - bind "h" "Left" { Resize "Left"; } - bind "j" "Down" { Resize "Down"; } - bind "k" "Up" { Resize "Up"; } - bind "l" "Right" { Resize "Right"; } + bind "h" "Left" { Resize "Increase Left"; } + bind "j" "Down" { Resize "Increase Down"; } + bind "k" "Up" { Resize "Increase Up"; } + bind "l" "Right" { Resize "Increase Right"; } + bind "H" { Resize "Decrease Left"; } + bind "J" { Resize "Decrease Down"; } + bind "K" { Resize "Decrease Up"; } + bind "L" { Resize "Decrease Right"; } bind "=" "+" { Resize "Increase"; } bind "-" { Resize "Decrease"; } } diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 694032dc1e..1d62e7d078 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -1,8 +1,7 @@ -use crate::data::InputMode; +use crate::data::{Direction, InputMode, Resize}; use crate::setup::Setup; use crate::{ consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV}, - input::actions::{Direction, ResizeDirection}, input::options::CliOptions, }; use clap::{Parser, Subcommand}; @@ -184,8 +183,11 @@ pub enum CliAction { Write { bytes: Vec }, /// Write characters to the terminal. WriteChars { chars: String }, - /// Resize the focused pane in the specified direction. [right|left|up|down|+|-] - Resize { resize_direction: ResizeDirection }, + /// [increase|decrease] the focused panes area at the [left|down|up|right] border. + Resize { + resize: Resize, + direction: Option, + }, /// Change focus to the next pane FocusNextPane, /// Change focus to the previous pane diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 605cf89808..a0f255daa6 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -203,6 +203,25 @@ pub enum Direction { Down, } +impl Direction { + pub fn invert(&self) -> Direction { + match *self { + Direction::Left => Direction::Right, + Direction::Down => Direction::Up, + Direction::Up => Direction::Down, + Direction::Right => Direction::Left, + } + } + + pub fn is_horizontal(&self) -> bool { + matches!(self, Direction::Left | Direction::Right) + } + + pub fn is_vertical(&self) -> bool { + matches!(self, Direction::Down | Direction::Up) + } +} + impl fmt::Display for Direction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -214,6 +233,215 @@ impl fmt::Display for Direction { } } +impl FromStr for Direction { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "Left" | "left" => Ok(Direction::Left), + "Right" | "right" => Ok(Direction::Right), + "Up" | "up" => Ok(Direction::Up), + "Down" | "down" => Ok(Direction::Down), + _ => Err(format!( + "Failed to parse Direction. Unknown Direction: {}", + s + )), + } + } +} + +/// Resize operation to perform. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub enum Resize { + Increase, + Decrease, +} + +impl Resize { + pub fn invert(&self) -> Self { + match self { + Resize::Increase => Resize::Decrease, + Resize::Decrease => Resize::Increase, + } + } +} + +impl fmt::Display for Resize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Resize::Increase => write!(f, "+"), + Resize::Decrease => write!(f, "-"), + } + } +} + +impl FromStr for Resize { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "Increase" | "increase" | "+" => Ok(Resize::Increase), + "Decrease" | "decrease" | "-" => Ok(Resize::Decrease), + _ => Err(format!( + "failed to parse resize type. Unknown specifier '{}'", + s + )), + } + } +} + +/// Container type that fully describes resize operations. +/// +/// This is best thought of as follows: +/// +/// - `resize` commands how the total *area* of the pane will change as part of this resize +/// operation. +/// - `direction` has two meanings: +/// - `None` means to resize all borders equally +/// - Anything else means to move the named border to achieve the change in area +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct ResizeStrategy { + /// Whether to increase or resize total area + pub resize: Resize, + /// With which border, if any, to change area + pub direction: Option, + /// If set to true (default), increasing resizes towards a viewport border will be inverted. + /// I.e. a scenario like this ("increase right"): + /// + /// ```text + /// +---+---+ + /// | | X |-> + /// +---+---+ + /// ``` + /// + /// turns into this ("decrease left"): + /// + /// ```text + /// +---+---+ + /// | |-> | + /// +---+---+ + /// ``` + pub invert_on_boundaries: bool, +} + +impl From for ResizeStrategy { + fn from(direction: Direction) -> Self { + ResizeStrategy::new(Resize::Increase, Some(direction)) + } +} + +impl From for ResizeStrategy { + fn from(resize: Resize) -> Self { + ResizeStrategy::new(resize, None) + } +} + +impl ResizeStrategy { + pub fn new(resize: Resize, direction: Option) -> Self { + ResizeStrategy { + resize, + direction, + invert_on_boundaries: true, + } + } + + pub fn invert(&self) -> ResizeStrategy { + let resize = match self.resize { + Resize::Increase => Resize::Decrease, + Resize::Decrease => Resize::Increase, + }; + let direction = match self.direction { + Some(direction) => Some(direction.invert()), + None => None, + }; + + ResizeStrategy::new(resize, direction) + } + + pub fn resize_type(&self) -> Resize { + self.resize + } + + pub fn direction(&self) -> Option { + self.direction + } + + pub fn direction_horizontal(&self) -> bool { + matches!( + self.direction, + Some(Direction::Left) | Some(Direction::Right) + ) + } + + pub fn direction_vertical(&self) -> bool { + matches!(self.direction, Some(Direction::Up) | Some(Direction::Down)) + } + + pub fn resize_increase(&self) -> bool { + self.resize == Resize::Increase + } + + pub fn resize_decrease(&self) -> bool { + self.resize == Resize::Decrease + } + + pub fn move_left_border_left(&self) -> bool { + (self.resize == Resize::Increase) && (self.direction == Some(Direction::Left)) + } + + pub fn move_left_border_right(&self) -> bool { + (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Left)) + } + + pub fn move_lower_border_down(&self) -> bool { + (self.resize == Resize::Increase) && (self.direction == Some(Direction::Down)) + } + + pub fn move_lower_border_up(&self) -> bool { + (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Down)) + } + + pub fn move_upper_border_up(&self) -> bool { + (self.resize == Resize::Increase) && (self.direction == Some(Direction::Up)) + } + + pub fn move_upper_border_down(&self) -> bool { + (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Up)) + } + + pub fn move_right_border_right(&self) -> bool { + (self.resize == Resize::Increase) && (self.direction == Some(Direction::Right)) + } + + pub fn move_right_border_left(&self) -> bool { + (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Right)) + } + + pub fn move_all_borders_out(&self) -> bool { + (self.resize == Resize::Increase) && (self.direction == None) + } + + pub fn move_all_borders_in(&self) -> bool { + (self.resize == Resize::Decrease) && (self.direction == None) + } +} + +impl fmt::Display for ResizeStrategy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let resize = match self.resize { + Resize::Increase => "increase", + Resize::Decrease => "decrease", + }; + let border = match self.direction { + Some(Direction::Left) => "left", + Some(Direction::Down) => "bottom", + Some(Direction::Up) => "top", + Some(Direction::Right) => "right", + None => "every", + }; + + write!(f, "{} size on {} border", resize, border) + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] // FIXME: This should be extended to handle different button clicks (not just // left click) and the `ScrollUp` and `ScrollDown` events could probably be diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 70d4b3f6db..cbd0bdf1d5 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -256,6 +256,16 @@ pub enum ScreenContext { HorizontalSplit, VerticalSplit, WriteCharacter, + ResizeIncreaseAll, + ResizeIncreaseLeft, + ResizeIncreaseDown, + ResizeIncreaseUp, + ResizeIncreaseRight, + ResizeDecreaseAll, + ResizeDecreaseLeft, + ResizeDecreaseDown, + ResizeDecreaseUp, + ResizeDecreaseRight, ResizeLeft, ResizeRight, ResizeDown, diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index e62a6ec629..e1a45bf5cc 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -4,6 +4,7 @@ use super::command::RunCommandAction; use super::layout::{Layout, PaneLayout}; use crate::cli::CliAction; use crate::data::InputMode; +use crate::data::{Direction, Resize}; use crate::input::config::{ConfigError, KdlError}; use crate::input::options::OnForceClose; use miette::{NamedSource, Report}; @@ -14,30 +15,6 @@ use std::str::FromStr; use crate::position::Position; -/// The four directions (left, right, up, down). -#[derive(Eq, Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] -pub enum Direction { - Left, - Right, - Up, - Down, -} -impl FromStr for Direction { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "Left" | "left" => Ok(Direction::Left), - "Right" | "right" => Ok(Direction::Right), - "Up" | "up" => Ok(Direction::Up), - "Down" | "down" => Ok(Direction::Down), - _ => Err(format!( - "Failed to parse Direction. Unknown Direction: {}", - s - )), - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum ResizeDirection { Left, @@ -126,8 +103,8 @@ pub enum Action { SwitchToMode(InputMode), /// Switch all connected clients to the specified input mode. SwitchModeForAllClients(InputMode), - /// Resize focus pane in specified direction. - Resize(ResizeDirection), + /// Shrink/enlarge focused pane at specified border + Resize(Resize, Option), /// Switch focus to next pane in specified direction. FocusNextPane, FocusPreviousPane, @@ -235,7 +212,7 @@ impl Action { match cli_action { CliAction::Write { bytes } => Ok(vec![Action::Write(bytes)]), CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]), - CliAction::Resize { resize_direction } => Ok(vec![Action::Resize(resize_direction)]), + CliAction::Resize { resize, direction } => Ok(vec![Action::Resize(resize, direction)]), CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]), CliAction::FocusPreviousPane => Ok(vec![Action::FocusPreviousPane]), CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus(direction)]), diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs index 14b87755fd..3b6f250258 100644 --- a/zellij-utils/src/input/command.rs +++ b/zellij-utils/src/input/command.rs @@ -1,5 +1,5 @@ //! Trigger a command -use super::actions::Direction; +use crate::data::Direction; use serde::{Deserialize, Serialize}; use std::path::PathBuf; diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 94b4f93220..6c4a222ccb 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -9,6 +9,7 @@ // If plugins should be able to depend on the layout system // then [`zellij-utils`] could be a proper place. use crate::{ + data::Direction, input::{ command::RunCommand, config::{Config, ConfigError}, @@ -48,6 +49,15 @@ impl Not for SplitDirection { } } +impl From for SplitDirection { + fn from(direction: Direction) -> Self { + match direction { + Direction::Left | Direction::Right => SplitDirection::Horizontal, + Direction::Down | Direction::Up => SplitDirection::Vertical, + } + } +} + #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum SplitSize { #[serde(alias = "percent")] diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs index 57126d595a..36c81cf813 100644 --- a/zellij-utils/src/input/unit/keybinds_test.rs +++ b/zellij-utils/src/input/unit/keybinds_test.rs @@ -1,6 +1,6 @@ use super::super::actions::*; use super::super::keybinds::*; -use crate::data::{self, CharOrArrow, Key}; +use crate::data::{self, CharOrArrow, Direction, Key}; use crate::input::config::Config; use insta::assert_snapshot; use strum::IntoEnumIterator; diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 54560bf58a..b28a8cc3c2 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,5 +1,5 @@ mod kdl_layout_parser; -use crate::data::{InputMode, Key, Palette, PaletteColor}; +use crate::data::{Direction, InputMode, Key, Palette, PaletteColor, Resize}; use crate::envs::EnvironmentVariables; use crate::input::config::{Config, ConfigError, KdlError}; use crate::input::keybinds::Keybinds; @@ -21,7 +21,7 @@ use kdl::{KdlDocument, KdlEntry, KdlNode}; use std::path::PathBuf; use std::str::FromStr; -use crate::input::actions::{Action, Direction, ResizeDirection, SearchDirection, SearchOption}; +use crate::input::actions::{Action, SearchDirection, SearchOption}; use crate::input::command::RunCommandAction; #[macro_export] @@ -394,14 +394,28 @@ impl Action { }, }, "Resize" => { - let direction = ResizeDirection::from_str(string.as_str()).map_err(|_| { - ConfigError::new_kdl_error( - format!("Invalid direction: '{}'", string), - action_node.span().offset(), - action_node.span().len(), - ) - })?; - Ok(Action::Resize(direction)) + let mut resize: Option = None; + let mut direction: Option = None; + for word in string.to_ascii_lowercase().split_whitespace() { + match Resize::from_str(word) { + Ok(value) => resize = Some(value), + Err(_) => match Direction::from_str(word) { + Ok(value) => direction = Some(value), + Err(_) => { + return Err(ConfigError::new_kdl_error( + format!( + "failed to read either of resize type or direction from '{}'", + word + ), + action_node.span().offset(), + action_node.span().len(), + )) + }, + }, + } + } + let resize = resize.unwrap_or(Resize::Increase); + Ok(Action::Resize(resize, direction)) }, "MoveFocus" => { let direction = Direction::from_str(string.as_str()).map_err(|_| { @@ -691,6 +705,11 @@ impl TryFrom<&KdlNode> for Action { action_arguments, kdl_action ), + "ResizeNew" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), "MoveFocus" => parse_kdl_action_char_or_string_arguments!( action_name, action_arguments, 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 ff1a3ff973..7791459470 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 @@ -12,6 +12,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -21,6 +22,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -30,6 +32,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -188,22 +191,34 @@ Config { Resize: { Left: [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Down: [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Up: [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Right: [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Char( @@ -218,6 +233,7 @@ Config { ): [ Resize( Increase, + None, ), ], Char( @@ -225,6 +241,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Char( @@ -232,34 +249,87 @@ Config { ): [ Resize( Increase, + None, + ), + ], + Char( + 'H', + ): [ + Resize( + Decrease, + Some( + Left, + ), + ), + ], + Char( + 'J', + ): [ + Resize( + Decrease, + Some( + Down, + ), + ), + ], + Char( + 'K', + ): [ + Resize( + Decrease, + Some( + Up, + ), + ), + ], + Char( + 'L', + ): [ + Resize( + Decrease, + Some( + Right, + ), ), ], Char( 'h', ): [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Char( 'j', ): [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Char( 'k', ): [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Char( 'l', ): [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Alt( @@ -269,6 +339,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -278,6 +349,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -287,6 +359,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -595,6 +668,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -604,6 +678,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -613,6 +688,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -940,6 +1016,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -949,6 +1026,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -958,6 +1036,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1195,6 +1274,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1204,6 +1284,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1213,6 +1294,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1390,6 +1472,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1399,6 +1482,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1408,6 +1492,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1664,6 +1749,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1673,6 +1759,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1682,6 +1769,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1859,6 +1947,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1868,6 +1957,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1877,6 +1967,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2051,6 +2142,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2060,6 +2152,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2069,6 +2162,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2248,6 +2342,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2257,6 +2352,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2266,6 +2362,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2503,6 +2600,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2512,6 +2610,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2521,6 +2620,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2692,6 +2792,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2701,6 +2802,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2710,6 +2812,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3033,6 +3136,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3042,6 +3146,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -3051,6 +3156,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( 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 4fb6fe5991..9d15cc6a7e 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 @@ -12,6 +12,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -21,6 +22,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -30,6 +32,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -188,22 +191,34 @@ Config { Resize: { Left: [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Down: [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Up: [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Right: [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Char( @@ -218,6 +233,7 @@ Config { ): [ Resize( Increase, + None, ), ], Char( @@ -225,6 +241,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Char( @@ -232,34 +249,87 @@ Config { ): [ Resize( Increase, + None, + ), + ], + Char( + 'H', + ): [ + Resize( + Decrease, + Some( + Left, + ), + ), + ], + Char( + 'J', + ): [ + Resize( + Decrease, + Some( + Down, + ), + ), + ], + Char( + 'K', + ): [ + Resize( + Decrease, + Some( + Up, + ), + ), + ], + Char( + 'L', + ): [ + Resize( + Decrease, + Some( + Right, + ), ), ], Char( 'h', ): [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Char( 'j', ): [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Char( 'k', ): [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Char( 'l', ): [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Alt( @@ -269,6 +339,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -278,6 +349,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -287,6 +359,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -595,6 +668,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -604,6 +678,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -613,6 +688,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -940,6 +1016,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -949,6 +1026,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -958,6 +1036,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1195,6 +1274,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1204,6 +1284,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1213,6 +1294,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1390,6 +1472,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1399,6 +1482,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1408,6 +1492,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1664,6 +1749,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1673,6 +1759,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1682,6 +1769,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1859,6 +1947,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1868,6 +1957,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1877,6 +1967,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2051,6 +2142,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2060,6 +2152,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2069,6 +2162,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2248,6 +2342,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2257,6 +2352,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2266,6 +2362,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2503,6 +2600,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2512,6 +2610,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2521,6 +2620,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2692,6 +2792,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2701,6 +2802,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2710,6 +2812,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3033,6 +3136,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3042,6 +3146,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -3051,6 +3156,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap index 41604668ce..aadd52ed20 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap @@ -12,6 +12,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -21,6 +22,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -30,6 +32,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -188,22 +191,34 @@ Config { Resize: { Left: [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Down: [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Up: [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Right: [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Char( @@ -218,6 +233,7 @@ Config { ): [ Resize( Increase, + None, ), ], Char( @@ -225,6 +241,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Char( @@ -232,34 +249,87 @@ Config { ): [ Resize( Increase, + None, + ), + ], + Char( + 'H', + ): [ + Resize( + Decrease, + Some( + Left, + ), + ), + ], + Char( + 'J', + ): [ + Resize( + Decrease, + Some( + Down, + ), + ), + ], + Char( + 'K', + ): [ + Resize( + Decrease, + Some( + Up, + ), + ), + ], + Char( + 'L', + ): [ + Resize( + Decrease, + Some( + Right, + ), ), ], Char( 'h', ): [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Char( 'j', ): [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Char( 'k', ): [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Char( 'l', ): [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Alt( @@ -269,6 +339,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -278,6 +349,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -287,6 +359,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -595,6 +668,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -604,6 +678,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -613,6 +688,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -940,6 +1016,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -949,6 +1026,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -958,6 +1036,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1195,6 +1274,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1204,6 +1284,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1213,6 +1294,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1390,6 +1472,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1399,6 +1482,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1408,6 +1492,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1664,6 +1749,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1673,6 +1759,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1682,6 +1769,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1859,6 +1947,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1868,6 +1957,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1877,6 +1967,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2051,6 +2142,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2060,6 +2152,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2069,6 +2162,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2248,6 +2342,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2257,6 +2352,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2266,6 +2362,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2503,6 +2600,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2512,6 +2610,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2521,6 +2620,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2692,6 +2792,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2701,6 +2802,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2710,6 +2812,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3033,6 +3136,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3042,6 +3146,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -3051,6 +3156,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( 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 91d7c6501f..46146fdf34 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 @@ -12,6 +12,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -21,6 +22,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -30,6 +32,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -188,22 +191,34 @@ Config { Resize: { Left: [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Down: [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Up: [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Right: [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Char( @@ -218,6 +233,7 @@ Config { ): [ Resize( Increase, + None, ), ], Char( @@ -225,6 +241,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Char( @@ -232,34 +249,87 @@ Config { ): [ Resize( Increase, + None, + ), + ], + Char( + 'H', + ): [ + Resize( + Decrease, + Some( + Left, + ), + ), + ], + Char( + 'J', + ): [ + Resize( + Decrease, + Some( + Down, + ), + ), + ], + Char( + 'K', + ): [ + Resize( + Decrease, + Some( + Up, + ), + ), + ], + Char( + 'L', + ): [ + Resize( + Decrease, + Some( + Right, + ), ), ], Char( 'h', ): [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Char( 'j', ): [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Char( 'k', ): [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Char( 'l', ): [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Alt( @@ -269,6 +339,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -278,6 +349,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -287,6 +359,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -595,6 +668,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -604,6 +678,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -613,6 +688,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -940,6 +1016,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -949,6 +1026,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -958,6 +1036,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1195,6 +1274,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1204,6 +1284,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1213,6 +1294,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1390,6 +1472,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1399,6 +1482,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1408,6 +1492,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1664,6 +1749,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1673,6 +1759,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1682,6 +1769,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1859,6 +1947,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1868,6 +1957,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1877,6 +1967,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2051,6 +2142,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2060,6 +2152,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2069,6 +2162,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2248,6 +2342,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2257,6 +2352,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2266,6 +2362,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2503,6 +2600,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2512,6 +2610,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2521,6 +2620,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2692,6 +2792,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2701,6 +2802,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2710,6 +2812,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3033,6 +3136,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3042,6 +3146,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -3051,6 +3156,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( 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 caeaed714c..0f2e1e6216 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 @@ -12,6 +12,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -21,6 +22,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -30,6 +32,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -188,22 +191,34 @@ Config { Resize: { Left: [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Down: [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Up: [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Right: [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Char( @@ -218,6 +233,7 @@ Config { ): [ Resize( Increase, + None, ), ], Char( @@ -225,6 +241,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Char( @@ -232,34 +249,87 @@ Config { ): [ Resize( Increase, + None, + ), + ], + Char( + 'H', + ): [ + Resize( + Decrease, + Some( + Left, + ), + ), + ], + Char( + 'J', + ): [ + Resize( + Decrease, + Some( + Down, + ), + ), + ], + Char( + 'K', + ): [ + Resize( + Decrease, + Some( + Up, + ), + ), + ], + Char( + 'L', + ): [ + Resize( + Decrease, + Some( + Right, + ), ), ], Char( 'h', ): [ Resize( - Left, + Increase, + Some( + Left, + ), ), ], Char( 'j', ): [ Resize( - Down, + Increase, + Some( + Down, + ), ), ], Char( 'k', ): [ Resize( - Up, + Increase, + Some( + Up, + ), ), ], Char( 'l', ): [ Resize( - Right, + Increase, + Some( + Right, + ), ), ], Alt( @@ -269,6 +339,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -278,6 +349,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -287,6 +359,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -595,6 +668,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -604,6 +678,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -613,6 +688,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -940,6 +1016,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -949,6 +1026,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -958,6 +1036,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1195,6 +1274,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1204,6 +1284,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1213,6 +1294,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1390,6 +1472,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1399,6 +1482,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1408,6 +1492,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1664,6 +1749,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1673,6 +1759,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1682,6 +1769,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1859,6 +1947,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -1868,6 +1957,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -1877,6 +1967,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2051,6 +2142,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2060,6 +2152,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2069,6 +2162,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2248,6 +2342,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2257,6 +2352,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2266,6 +2362,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2503,6 +2600,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2512,6 +2610,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2521,6 +2620,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2692,6 +2792,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -2701,6 +2802,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -2710,6 +2812,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3033,6 +3136,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt( @@ -3042,6 +3146,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Alt( @@ -3051,6 +3156,7 @@ Config { ): [ Resize( Increase, + None, ), ], Alt(