From f2c5ee44f7f2c61f50b043bd8e55f915a3667fce Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Sat, 29 May 2021 23:12:11 +0100 Subject: [PATCH 1/3] Getting back to where we started... (Buggy Resizing) --- Cargo.lock | 7 + default-plugins/status-bar/src/main.rs | 2 +- default-plugins/tab-bar/src/main.rs | 2 +- zellij-server/Cargo.toml | 1 + zellij-server/src/lib.rs | 2 + zellij-server/src/panes/plugin_pane.rs | 32 +- zellij-server/src/panes/terminal_pane.rs | 27 +- zellij-server/src/screen.rs | 16 +- zellij-server/src/tab.rs | 40 +- zellij-server/src/ui/layout.rs | 29 +- zellij-server/src/ui/pane_resizer.rs | 720 +++++++---------------- zellij-server/src/wasm_vm.rs | 22 +- zellij-tile/src/shim.rs | 11 +- zellij-utils/src/errors.rs | 3 +- zellij-utils/src/pane_size.rs | 7 +- 15 files changed, 327 insertions(+), 594 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc862d53..31e038a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + [[package]] name = "cc" version = "1.0.67" @@ -2345,6 +2351,7 @@ version = "0.13.0" dependencies = [ "ansi_term 0.12.1", "async-trait", + "cassowary", "daemonize", "insta", "serde_json", diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index cf9445d6..83eb50c9 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -135,7 +135,7 @@ impl ZellijPlugin for State { fn load(&mut self) { set_selectable(false); set_invisible_borders(true); - set_max_height(2); + set_fixed_height(2); subscribe(&[EventType::ModeUpdate]); } diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs index 46336be3..7ff639e8 100644 --- a/default-plugins/tab-bar/src/main.rs +++ b/default-plugins/tab-bar/src/main.rs @@ -26,7 +26,7 @@ impl ZellijPlugin for State { fn load(&mut self) { set_selectable(false); set_invisible_borders(true); - set_max_height(1); + set_fixed_height(1); subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]); } diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml index 1168dac4..b04bd04d 100644 --- a/zellij-server/Cargo.toml +++ b/zellij-server/Cargo.toml @@ -16,6 +16,7 @@ serde_json = "1.0" unicode-width = "0.1.8" wasmer = "1.0.0" wasmer-wasi = "1.0.0" +cassowary = "0.3.0" zellij-utils = { path = "../zellij-utils/", version = "0.13.0" } [dev-dependencies] diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 76b4b7b1..6e6392c2 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -112,6 +112,8 @@ pub fn start_server(os_input: Box, socket_path: PathBuf) { daemonize::Daemonize::new() .working_directory(std::env::current_dir().unwrap()) .umask(0o077) + // FIXME: My cherished `dbg!` was broken, so this is a hack to bring it back + .stderr(std::fs::File::create("dbg.log").unwrap()) .start() .expect("could not daemonize the server process"); diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index e5c5e15d..b08d528f 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -16,8 +16,6 @@ pub(crate) struct PluginPane { pub position_and_size: PositionAndSize, pub position_and_size_override: Option, pub send_plugin_instructions: SenderWithContext, - pub max_height: Option, - pub max_width: Option, pub active_at: Instant, } @@ -35,8 +33,6 @@ impl PluginPane { position_and_size, position_and_size_override: None, send_plugin_instructions, - max_height: None, - max_width: None, active_at: Instant::now(), } } @@ -95,7 +91,9 @@ impl Pane for PluginPane { fn adjust_input_to_terminal(&self, _input_bytes: Vec) -> Vec { unimplemented!() // FIXME: Shouldn't need this implmented? } - + fn position_and_size(&self) -> PositionAndSize { + self.position_and_size + } fn position_and_size_override(&self) -> Option { self.position_and_size_override } @@ -114,11 +112,13 @@ impl Pane for PluginPane { fn set_invisible_borders(&mut self, invisible_borders: bool) { self.invisible_borders = invisible_borders; } - fn set_max_height(&mut self, max_height: usize) { - self.max_height = Some(max_height); + fn set_fixed_height(&mut self, fixed_height: usize) { + self.position_and_size.rows = fixed_height; + self.position_and_size.rows_fixed = true; } - fn set_max_width(&mut self, max_width: usize) { - self.max_width = Some(max_width); + fn set_fixed_width(&mut self, fixed_width: usize) { + self.position_and_size.columns = fixed_width; + self.position_and_size.cols_fixed = true; } fn render(&mut self) -> Option { // if self.should_render { @@ -204,11 +204,21 @@ impl Pane for PluginPane { fn clear_scroll(&mut self) { unimplemented!() } + // FIXME: This need to be reevaluated and deleted if possible. + // `max` doesn't make sense when things are fixed... fn max_height(&self) -> Option { - self.max_height + if self.position_and_size.rows_fixed { + Some(self.position_and_size.rows) + } else { + None + } } fn max_width(&self) -> Option { - self.max_width + if self.position_and_size.cols_fixed { + Some(self.position_and_size.columns) + } else { + None + } } fn invisible_borders(&self) -> bool { self.invisible_borders diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 32910618..2dbd11a0 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -27,8 +27,6 @@ pub struct TerminalPane { pub selectable: bool, pub position_and_size: PositionAndSize, pub position_and_size_override: Option, - pub max_height: Option, - pub max_width: Option, pub active_at: Instant, pub colors: Palette, vte_parser: vte::Parser, @@ -52,8 +50,7 @@ impl Pane for TerminalPane { self.reflow_lines(); } fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) { - self.position_and_size.columns = position_and_size.columns; - self.position_and_size.rows = position_and_size.rows; + self.position_and_size = *position_and_size; self.reflow_lines(); } fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) { @@ -119,7 +116,9 @@ impl Pane for TerminalPane { }; input_bytes } - + fn position_and_size(&self) -> PositionAndSize { + self.position_and_size + } fn position_and_size_override(&self) -> Option { self.position_and_size_override } @@ -135,21 +134,17 @@ impl Pane for TerminalPane { fn set_selectable(&mut self, selectable: bool) { self.selectable = selectable; } - fn set_max_height(&mut self, max_height: usize) { - self.max_height = Some(max_height); + fn set_fixed_height(&mut self, fixed_height: usize) { + self.position_and_size.rows = fixed_height; + self.position_and_size.rows_fixed = true; } - fn set_max_width(&mut self, max_width: usize) { - self.max_width = Some(max_width); + fn set_fixed_width(&mut self, fixed_width: usize) { + self.position_and_size.columns = fixed_width; + self.position_and_size.cols_fixed = true; } fn set_invisible_borders(&mut self, _invisible_borders: bool) { unimplemented!(); } - fn max_height(&self) -> Option { - self.max_height - } - fn max_width(&self) -> Option { - self.max_width - } fn render(&mut self) -> Option { if self.should_render() { let mut vte_output = String::new(); @@ -294,8 +289,6 @@ impl TerminalPane { selectable: true, position_and_size, position_and_size_override: None, - max_height: None, - max_width: None, vte_parser: vte::Parser::new(), active_at: Instant::now(), colors: palette, diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 48be44bf..ab34dfaf 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -55,7 +55,8 @@ pub(crate) enum ScreenInstruction { CloseFocusedPane, ToggleActiveTerminalFullscreen, SetSelectable(PaneId, bool), - SetMaxHeight(PaneId, usize), + SetFixedHeight(PaneId, usize), + SetFixedWidth(PaneId, usize), SetInvisibleBorders(PaneId, bool), ClosePane(PaneId), ApplyLayout(Layout, Vec), @@ -106,7 +107,8 @@ impl From<&ScreenInstruction> for ScreenContext { } ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable, ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders, - ScreenInstruction::SetMaxHeight(..) => ScreenContext::SetMaxHeight, + ScreenInstruction::SetFixedHeight(..) => ScreenContext::SetFixedHeight, + ScreenInstruction::SetFixedWidth(..) => ScreenContext::SetFixedWidth, ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane, ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout, ScreenInstruction::NewTab(_) => ScreenContext::NewTab, @@ -574,11 +576,17 @@ pub(crate) fn screen_thread_main( .unwrap() .set_pane_selectable(id, selectable); } - ScreenInstruction::SetMaxHeight(id, max_height) => { + ScreenInstruction::SetFixedHeight(id, fixed_height) => { screen .get_active_tab_mut() .unwrap() - .set_pane_max_height(id, max_height); + .set_pane_fixed_height(id, fixed_height); + } + ScreenInstruction::SetFixedWidth(id, fixed_width) => { + screen + .get_active_tab_mut() + .unwrap() + .set_pane_fixed_width(id, fixed_width); } ScreenInstruction::SetInvisibleBorders(id, invisible_borders) => { screen diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index 7daf2092..45f386c7 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -104,15 +104,15 @@ pub trait Pane { fn handle_pty_bytes(&mut self, bytes: VteBytes); fn cursor_coordinates(&self) -> Option<(usize, usize)>; fn adjust_input_to_terminal(&self, input_bytes: Vec) -> Vec; - + fn position_and_size(&self) -> PositionAndSize; fn position_and_size_override(&self) -> Option; fn should_render(&self) -> bool; fn set_should_render(&mut self, should_render: bool); fn selectable(&self) -> bool; fn set_selectable(&mut self, selectable: bool); fn set_invisible_borders(&mut self, invisible_borders: bool); - fn set_max_height(&mut self, max_height: usize); - fn set_max_width(&mut self, max_width: usize); + fn set_fixed_height(&mut self, fixed_height: usize); + fn set_fixed_width(&mut self, fixed_width: usize); fn render(&mut self) -> Option; fn pid(&self) -> PaneId; fn reduce_height_down(&mut self, count: usize); @@ -178,15 +178,6 @@ pub trait Pane { std::cmp::min(self.x() + self.columns(), other.x() + other.columns()) - std::cmp::max(self.x(), other.x()) } - fn position_and_size(&self) -> PositionAndSize { - PositionAndSize { - x: self.x(), - y: self.y(), - columns: self.columns(), - rows: self.rows(), - ..Default::default() - } - } fn can_increase_height_by(&self, increase_by: usize) -> bool { self.max_height() .map(|max_height| self.rows() + increase_by <= max_height) @@ -294,12 +285,6 @@ impl Tab { match positions_and_size.next() { Some((_, position_and_size)) => { terminal_pane.reset_size_and_position_override(); - if let Some(max_rows) = position_and_size.max_rows { - terminal_pane.set_max_height(max_rows); - } - if let Some(max_columns) = position_and_size.max_columns { - terminal_pane.set_max_width(max_columns); - } terminal_pane.change_pos_and_size(&position_and_size); self.os_api.set_terminal_size_using_fd( *pid, @@ -317,24 +302,18 @@ impl Tab { } let mut new_pids = new_pids.iter(); for (layout, position_and_size) in positions_and_size { - // Just a regular terminal + // A plugin pane if let Some(plugin) = &layout.plugin { let (pid_tx, pid_rx) = channel(); self.senders .send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone())) .unwrap(); let pid = pid_rx.recv().unwrap(); - let mut new_plugin = PluginPane::new( + let new_plugin = PluginPane::new( pid, *position_and_size, self.senders.to_plugin.as_ref().unwrap().clone(), ); - if let Some(max_rows) = position_and_size.max_rows { - new_plugin.set_max_height(max_rows); - } - if let Some(max_columns) = position_and_size.max_columns { - new_plugin.set_max_width(max_columns); - } self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin)); // Send an initial mode update to the newly loaded plugin only! self.senders @@ -2121,9 +2100,14 @@ impl Tab { pane.set_invisible_borders(invisible_borders); } } - pub fn set_pane_max_height(&mut self, id: PaneId, max_height: usize) { + pub fn set_pane_fixed_height(&mut self, id: PaneId, fixed_height: usize) { if let Some(pane) = self.panes.get_mut(&id) { - pane.set_max_height(max_height); + pane.set_fixed_height(fixed_height); + } + } + pub fn set_pane_fixed_width(&mut self, id: PaneId, fixed_width: usize) { + if let Some(pane) = self.panes.get_mut(&id) { + pane.set_fixed_width(fixed_width); } } pub fn close_pane(&mut self, id: PaneId) { diff --git a/zellij-server/src/ui/layout.rs b/zellij-server/src/ui/layout.rs index a9af665f..7a19d889 100644 --- a/zellij-server/src/ui/layout.rs +++ b/zellij-server/src/ui/layout.rs @@ -19,17 +19,14 @@ fn split_space_to_parts_vertically( // First fit in the parameterized sizes for size in sizes { - let (columns, max_columns) = match size { + let columns = match size { Some(SplitSize::Percent(percent)) => { - ((max_width as f32 * (percent as f32 / 100.0)) as usize, None) + (max_width as f32 * (percent as f32 / 100.0)) as usize } // TODO: round properly - Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)), + Some(SplitSize::Fixed(size)) => size as usize, None => { parts_to_grow.push(current_x_position); - ( - 1, // This is grown later on - None, - ) + 1 // This is grown later on } }; split_parts.push(PositionAndSize { @@ -37,7 +34,6 @@ fn split_space_to_parts_vertically( y: space_to_split.y, columns, rows: space_to_split.rows, - max_columns, ..Default::default() }); current_width += columns; @@ -87,18 +83,14 @@ fn split_space_to_parts_horizontally( let mut parts_to_grow = Vec::new(); for size in sizes { - let (rows, max_rows) = match size { - Some(SplitSize::Percent(percent)) => ( - (max_height as f32 * (percent as f32 / 100.0)) as usize, - None, - ), // TODO: round properly - Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)), + let rows = match size { + Some(SplitSize::Percent(percent)) => { + (max_height as f32 * (percent as f32 / 100.0)) as usize + } // TODO: round properly + Some(SplitSize::Fixed(size)) => size as usize, None => { parts_to_grow.push(current_y_position); - ( - 1, // This is grown later on - None, - ) + 1 // This is grown later on } }; split_parts.push(PositionAndSize { @@ -106,7 +98,6 @@ fn split_space_to_parts_horizontally( y: current_y_position, columns: space_to_split.columns, rows, - max_rows, ..Default::default() }); current_height += rows; diff --git a/zellij-server/src/ui/pane_resizer.rs b/zellij-server/src/ui/pane_resizer.rs index b2001945..73673a9c 100644 --- a/zellij-server/src/ui/pane_resizer.rs +++ b/zellij-server/src/ui/pane_resizer.rs @@ -1,531 +1,247 @@ use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane}; +use cassowary::{ + strength::{REQUIRED, STRONG}, + Constraint, Solver, Variable, + WeightedRelation::*, +}; use std::{ - cmp::Ordering, collections::{BTreeMap, HashSet}, + ops::Not, }; use zellij_utils::pane_size::PositionAndSize; -pub(crate) struct PaneResizer<'a> { +const GAP_SIZE: usize = 1; // Panes are separated by this number of rows / columns + +pub struct PaneResizer<'a> { panes: &'a mut BTreeMap>, + vars: BTreeMap, + solver: Solver, os_api: &'a mut Box, } +#[derive(Debug, Clone, Copy)] +enum Direction { + Horizontal, + Vertical, +} + +impl Not for Direction { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Direction::Horizontal => Direction::Vertical, + Direction::Vertical => Direction::Horizontal, + } + } +} + +#[derive(Debug, Clone, Copy)] +struct Span { + pid: PaneId, + direction: Direction, + fixed: bool, + pos: usize, + size: usize, + pos_var: Variable, + size_var: Variable, +} + // TODO: currently there are some functions here duplicated with Tab // all resizing functions should move here +// FIXME: +// 1. Rounding causes a loss of ratios, I need to store an internal f64 for +// each pane as well as the displayed usize and add custom rounding logic. +// 2. Vertical resizing doesn't seem to respect the space consumed by the tab +// and status bars? +// 3. A 2x2 layout and simultaneous vertical + horizontal resizing sometimes +// leads to unsolvable constraints? Maybe related to 2 (and possibly 1). +// I should sanity-check the `spans_in_boundary()` here! + impl<'a> PaneResizer<'a> { pub fn new( panes: &'a mut BTreeMap>, os_api: &'a mut Box, ) -> Self { - PaneResizer { panes, os_api } + let mut vars = BTreeMap::new(); + for &k in panes.keys() { + vars.insert(k, (Variable::new(), Variable::new())); + } + PaneResizer { + panes, + vars, + solver: Solver::new(), + os_api, + } } + pub fn resize( &mut self, - mut current_size: PositionAndSize, + current_size: PositionAndSize, new_size: PositionAndSize, ) -> Option<(isize, isize)> { - // (column_difference, row_difference) - let mut successfully_resized = false; - let mut column_difference: isize = 0; - let mut row_difference: isize = 0; - match new_size.columns.cmp(¤t_size.columns) { - Ordering::Greater => { - let increase_by = new_size.columns - current_size.columns; - if let Some(panes_to_resize) = find_increasable_vertical_chain( - &self.panes, - increase_by, - current_size.columns, - current_size.rows, - ) { - self.increase_panes_right_and_push_adjacents_right( - panes_to_resize, - increase_by, - ); - column_difference = new_size.columns as isize - current_size.columns as isize; - current_size.columns = - (current_size.columns as isize + column_difference) as usize; - successfully_resized = true; - }; - } - Ordering::Less => { - let reduce_by = current_size.columns - new_size.columns; - if let Some(panes_to_resize) = find_reducible_vertical_chain( - &self.panes, - reduce_by, - current_size.columns, - current_size.rows, - ) { - self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by); - column_difference = new_size.columns as isize - current_size.columns as isize; - current_size.columns = - (current_size.columns as isize + column_difference) as usize; - successfully_resized = true; - }; - } - Ordering::Equal => (), + let col_delta = new_size.columns as isize - current_size.columns as isize; + let row_delta = new_size.rows as isize - current_size.rows as isize; + if col_delta != 0 { + let spans = self.solve_direction(Direction::Horizontal, new_size.columns)?; + self.collapse_spans(&spans); } - match new_size.rows.cmp(¤t_size.rows) { - Ordering::Greater => { - let increase_by = new_size.rows - current_size.rows; - if let Some(panes_to_resize) = find_increasable_horizontal_chain( - &self.panes, - increase_by, - current_size.columns, - current_size.rows, - ) { - self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by); - row_difference = new_size.rows as isize - current_size.rows as isize; - current_size.rows = (current_size.rows as isize + row_difference) as usize; - successfully_resized = true; - }; - } - Ordering::Less => { - let reduce_by = current_size.rows - new_size.rows; - if let Some(panes_to_resize) = find_reducible_horizontal_chain( - &self.panes, - reduce_by, - current_size.columns, - current_size.rows, - ) { - self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by); - row_difference = new_size.rows as isize - current_size.rows as isize; - current_size.rows = (current_size.rows as isize + row_difference) as usize; - successfully_resized = true; - }; - } - Ordering::Equal => (), + self.solver.reset(); + if row_delta != 0 { + let spans = self.solve_direction(Direction::Vertical, new_size.rows)?; + self.collapse_spans(&spans); } - if successfully_resized { - Some((column_difference, row_difference)) + Some((col_delta, row_delta)) + } + + fn solve_direction(&mut self, direction: Direction, space: usize) -> Option> { + let mut grid = Vec::new(); + for boundary in self.grid_boundaries(direction) { + grid.push(self.spans_in_boundary(direction, boundary)); + } + + let constraints: Vec<_> = grid + .iter() + .flat_map(|s| constrain_spans(space, s)) + .collect(); + + // FIXME: This line needs to be restored before merging! + //self.solver.add_constraints(&constraints).ok()?; + self.solver.add_constraints(&constraints).unwrap(); + Some(grid.into_iter().flatten().collect()) + } + + fn grid_boundaries(&self, direction: Direction) -> Vec<(usize, usize)> { + // Select the spans running *perpendicular* to the direction of resize + let spans: Vec = self + .panes + .values() + .map(|p| self.get_span(!direction, p.as_ref())) + .collect(); + + let mut last_edge = 0; + let mut bounds = Vec::new(); + loop { + let mut spans_on_edge: Vec<&Span> = + spans.iter().filter(|p| p.pos == last_edge).collect(); + spans_on_edge.sort_unstable_by_key(|s| s.size); + if let Some(next) = spans_on_edge.first() { + let next_edge = last_edge + next.size; + bounds.push((last_edge, next_edge)); + last_edge = next_edge + GAP_SIZE; + } else { + break; + } + } + bounds + } + + fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec { + let (start, end) = boundary; + let bwn = |v| start <= v && v < end; + let mut spans: Vec<_> = self + .panes + .values() + .filter(|p| { + let s = self.get_span(!direction, p.as_ref()); + bwn(s.pos) || bwn(s.pos + s.size) + }) + .map(|p| self.get_span(direction, p.as_ref())) + .collect(); + spans.sort_unstable_by_key(|s| s.pos); + spans + } + + fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span { + let pas = pane.position_and_size(); + let (pos_var, size_var) = self.vars[&pane.pid()]; + match direction { + Direction::Horizontal => Span { + pid: pane.pid(), + direction, + fixed: pas.cols_fixed, + pos: pas.x, + size: pas.columns, + pos_var, + size_var, + }, + Direction::Vertical => Span { + pid: pane.pid(), + direction, + fixed: pas.rows_fixed, + pos: pas.y, + size: pas.rows, + pos_var, + size_var, + }, + } + } + + fn collapse_spans(&mut self, spans: &[Span]) { + for span in spans { + let solver = &self.solver; // Hand-holding the borrow-checker + let pane = self.panes.get_mut(&span.pid).unwrap(); + let fetch_usize = |v| solver.get_value(v).round() as usize; + match span.direction { + Direction::Horizontal => pane.change_pos_and_size(&PositionAndSize { + x: fetch_usize(span.pos_var), + columns: fetch_usize(span.size_var), + ..pane.position_and_size() + }), + Direction::Vertical => pane.change_pos_and_size(&PositionAndSize { + y: fetch_usize(span.pos_var), + rows: fetch_usize(span.size_var), + ..pane.position_and_size() + }), + } + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + pane.columns() as u16, + pane.rows() as u16, + ); + } + } + } +} + +fn constrain_spans(space: usize, spans: &[Span]) -> HashSet { + let mut constraints = HashSet::new(); + + // The first span needs to start at 0 + constraints.insert(spans[0].pos_var | EQ(REQUIRED) | 0.0); + + // Calculating "flexible" space (space not consumed by fixed-size spans) + let gap_space = GAP_SIZE * (spans.len() - 1); + let old_flex_space = spans + .iter() + .fold(0, |a, s| if !s.fixed { a + s.size } else { a }); + let new_flex_space = spans.iter().fold( + space - gap_space, + |a, s| if s.fixed { a - s.size } else { a }, + ); + + // Keep spans stuck together + for pair in spans.windows(2) { + let (ls, rs) = (pair[0], pair[1]); + constraints + .insert((ls.pos_var + ls.size_var + GAP_SIZE as f64) | EQ(REQUIRED) | rs.pos_var); + } + + // Try to maintain ratios and lock non-flexible sizes + for span in spans { + if span.fixed { + constraints.insert(span.size_var | EQ(REQUIRED) | span.size as f64); } else { - None - } - } - fn reduce_panes_left_and_pull_adjacents_left( - &mut self, - panes_to_reduce: Vec, - reduce_by: usize, - ) { - let mut pulled_panes: HashSet = HashSet::new(); - for pane_id in panes_to_reduce { - let (pane_x, pane_y, pane_columns, pane_rows) = { - let pane = self.panes.get(&pane_id).unwrap(); - (pane.x(), pane.y(), pane.columns(), pane.rows()) - }; - let panes_to_pull = self.panes.values_mut().filter(|p| { - p.x() > pane_x + pane_columns - && (p.y() <= pane_y && p.y() + p.rows() >= pane_y - || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows) - }); - for pane in panes_to_pull { - if !pulled_panes.contains(&pane.pid()) { - pane.pull_left(reduce_by); - pulled_panes.insert(pane.pid()); - } - } - self.reduce_pane_width_left(&pane_id, reduce_by); - } - } - fn reduce_panes_up_and_pull_adjacents_up( - &mut self, - panes_to_reduce: Vec, - reduce_by: usize, - ) { - let mut pulled_panes: HashSet = HashSet::new(); - for pane_id in panes_to_reduce { - let (pane_x, pane_y, pane_columns, pane_rows) = { - let pane = self.panes.get(&pane_id).unwrap(); - (pane.x(), pane.y(), pane.columns(), pane.rows()) - }; - let panes_to_pull = self.panes.values_mut().filter(|p| { - p.y() > pane_y + pane_rows - && (p.x() <= pane_x && p.x() + p.columns() >= pane_x - || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns) - }); - for pane in panes_to_pull { - if !pulled_panes.contains(&pane.pid()) { - pane.pull_up(reduce_by); - pulled_panes.insert(pane.pid()); - } - } - self.reduce_pane_height_up(&pane_id, reduce_by); - } - } - fn increase_panes_down_and_push_down_adjacents( - &mut self, - panes_to_increase: Vec, - increase_by: usize, - ) { - let mut pushed_panes: HashSet = HashSet::new(); - for pane_id in panes_to_increase { - let (pane_x, pane_y, pane_columns, pane_rows) = { - let pane = self.panes.get(&pane_id).unwrap(); - (pane.x(), pane.y(), pane.columns(), pane.rows()) - }; - let panes_to_push = self.panes.values_mut().filter(|p| { - p.y() > pane_y + pane_rows - && (p.x() <= pane_x && p.x() + p.columns() >= pane_x - || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns) - }); - for pane in panes_to_push { - if !pushed_panes.contains(&pane.pid()) { - pane.push_down(increase_by); - pushed_panes.insert(pane.pid()); - } - } - self.increase_pane_height_down(&pane_id, increase_by); - } - } - fn increase_panes_right_and_push_adjacents_right( - &mut self, - panes_to_increase: Vec, - increase_by: usize, - ) { - let mut pushed_panes: HashSet = HashSet::new(); - for pane_id in panes_to_increase { - let (pane_x, pane_y, pane_columns, pane_rows) = { - let pane = self.panes.get(&pane_id).unwrap(); - (pane.x(), pane.y(), pane.columns(), pane.rows()) - }; - let panes_to_push = self.panes.values_mut().filter(|p| { - p.x() > pane_x + pane_columns - && (p.y() <= pane_y && p.y() + p.rows() >= pane_y - || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows) - }); - for pane in panes_to_push { - if !pushed_panes.contains(&pane.pid()) { - pane.push_right(increase_by); - pushed_panes.insert(pane.pid()); - } - } - self.increase_pane_width_right(&pane_id, increase_by); - } - } - fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) { - let pane = self.panes.get_mut(id).unwrap(); - pane.reduce_height_up(count); - if let PaneId::Terminal(pid) = id { - self.os_api - .set_terminal_size_using_fd(*pid, pane.columns() as u16, pane.rows() as u16); - } - } - fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) { - let pane = self.panes.get_mut(id).unwrap(); - pane.increase_height_down(count); - if let PaneId::Terminal(pid) = pane.pid() { - self.os_api - .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); - } - } - fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) { - let pane = self.panes.get_mut(id).unwrap(); - pane.increase_width_right(count); - if let PaneId::Terminal(pid) = pane.pid() { - self.os_api - .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); - } - } - fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) { - let pane = self.panes.get_mut(id).unwrap(); - pane.reduce_width_left(count); - if let PaneId::Terminal(pid) = pane.pid() { - self.os_api - .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); - } - } -} - -fn find_next_increasable_horizontal_pane( - panes: &BTreeMap>, - right_of: &dyn Pane, - increase_by: usize, -) -> Option { - let next_pane_candidates = panes.values().filter( - |p| { - p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of) - }, // TODO: the name here is wrong, it should be vertically_overlaps_with - ); - let resizable_candidates = - next_pane_candidates.filter(|p| p.can_increase_height_by(increase_by)); - resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { - Some(next_pane) => { - let next_pane = panes.get(&next_pane).unwrap(); - if next_pane.y() < p.y() { - next_pane_id - } else { - Some(p.pid()) - } - } - None => Some(p.pid()), - }) -} - -fn find_next_increasable_vertical_pane( - panes: &BTreeMap>, - below: &dyn Pane, - increase_by: usize, -) -> Option { - let next_pane_candidates = panes.values().filter( - |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with - ); - let resizable_candidates = - next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by)); - resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { - Some(next_pane) => { - let next_pane = panes.get(&next_pane).unwrap(); - if next_pane.x() < p.x() { - next_pane_id - } else { - Some(p.pid()) - } - } - None => Some(p.pid()), - }) -} - -fn find_next_reducible_vertical_pane( - panes: &BTreeMap>, - below: &dyn Pane, - reduce_by: usize, -) -> Option { - let next_pane_candidates = panes.values().filter( - |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with - ); - let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by)); - resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { - Some(next_pane) => { - let next_pane = panes.get(&next_pane).unwrap(); - if next_pane.x() < p.x() { - next_pane_id - } else { - Some(p.pid()) - } - } - None => Some(p.pid()), - }) -} - -fn find_next_reducible_horizontal_pane( - panes: &BTreeMap>, - right_of: &dyn Pane, - reduce_by: usize, -) -> Option { - let next_pane_candidates = panes.values().filter( - |p| { - p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of) - }, // TODO: the name here is wrong, it should be vertically_overlaps_with - ); - let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by)); - resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { - Some(next_pane) => { - let next_pane = panes.get(&next_pane).unwrap(); - if next_pane.y() < p.y() { - next_pane_id - } else { - Some(p.pid()) - } - } - None => Some(p.pid()), - }) -} - -fn find_increasable_horizontal_chain( - panes: &BTreeMap>, - increase_by: usize, - screen_width: usize, - screen_height: usize, // TODO: this is the previous size (make this clearer) -) -> Option> { - let mut horizontal_coordinate = 0; - loop { - if horizontal_coordinate == screen_height { - return None; - } - - match panes - .values() - .find(|p| p.x() == 0 && p.y() == horizontal_coordinate) - { - Some(leftmost_pane) => { - if !leftmost_pane.can_increase_height_by(increase_by) { - horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; - continue; - } - let mut panes_to_resize = vec![]; - let mut current_pane = leftmost_pane; - loop { - panes_to_resize.push(current_pane.pid()); - if current_pane.x() + current_pane.columns() == screen_width { - return Some(panes_to_resize); - } - match find_next_increasable_horizontal_pane( - panes, - current_pane.as_ref(), - increase_by, - ) { - Some(next_pane_id) => { - current_pane = panes.get(&next_pane_id).unwrap(); - } - None => { - horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; - break; - } - }; - } - } - None => { - return None; - } - } - } -} - -fn find_increasable_vertical_chain( - panes: &BTreeMap>, - increase_by: usize, - screen_width: usize, - screen_height: usize, // TODO: this is the previous size (make this clearer) -) -> Option> { - let mut vertical_coordinate = 0; - loop { - if vertical_coordinate == screen_width { - return None; - } - - match panes - .values() - .find(|p| p.y() == 0 && p.x() == vertical_coordinate) - { - Some(topmost_pane) => { - if !topmost_pane.can_increase_width_by(increase_by) { - vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; - continue; - } - let mut panes_to_resize = vec![]; - let mut current_pane = topmost_pane; - loop { - panes_to_resize.push(current_pane.pid()); - if current_pane.y() + current_pane.rows() == screen_height { - return Some(panes_to_resize); - } - match find_next_increasable_vertical_pane( - panes, - current_pane.as_ref(), - increase_by, - ) { - Some(next_pane_id) => { - current_pane = panes.get(&next_pane_id).unwrap(); - } - None => { - vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; - break; - } - }; - } - } - None => { - return None; - } - } - } -} - -fn find_reducible_horizontal_chain( - panes: &BTreeMap>, - reduce_by: usize, - screen_width: usize, - screen_height: usize, // TODO: this is the previous size (make this clearer) -) -> Option> { - let mut horizontal_coordinate = 0; - loop { - if horizontal_coordinate == screen_height { - return None; - } - - match panes - .values() - .find(|p| p.x() == 0 && p.y() == horizontal_coordinate) - { - Some(leftmost_pane) => { - if !leftmost_pane.can_reduce_height_by(reduce_by) { - horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; - continue; - } - let mut panes_to_resize = vec![]; - let mut current_pane = leftmost_pane; - loop { - panes_to_resize.push(current_pane.pid()); - if current_pane.x() + current_pane.columns() == screen_width { - return Some(panes_to_resize); - } - match find_next_reducible_horizontal_pane( - panes, - current_pane.as_ref(), - reduce_by, - ) { - Some(next_pane_id) => { - current_pane = panes.get(&next_pane_id).unwrap(); - } - None => { - horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; - break; - } - }; - } - } - None => { - return None; - } - } - } -} - -fn find_reducible_vertical_chain( - panes: &BTreeMap>, - increase_by: usize, - screen_width: usize, - screen_height: usize, // TODO: this is the previous size (make this clearer) -) -> Option> { - let mut vertical_coordinate = 0; - loop { - if vertical_coordinate == screen_width { - return None; - } - - match panes - .values() - .find(|p| p.y() == 0 && p.x() == vertical_coordinate) - { - Some(topmost_pane) => { - if !topmost_pane.can_reduce_width_by(increase_by) { - vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; - continue; - } - let mut panes_to_resize = vec![]; - let mut current_pane = topmost_pane; - loop { - panes_to_resize.push(current_pane.pid()); - if current_pane.y() + current_pane.rows() == screen_height { - return Some(panes_to_resize); - } - match find_next_reducible_vertical_pane( - panes, - current_pane.as_ref(), - increase_by, - ) { - Some(next_pane_id) => { - current_pane = panes.get(&next_pane_id).unwrap(); - } - None => { - vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; - break; - } - }; - } - } - None => { - return None; - } + let ratio = span.size as f64 / old_flex_space as f64; + constraints.insert((span.size_var / new_flex_space as f64) | EQ(STRONG) | ratio); } } + + // The last pane needs to end at the end of the space + let last = spans.last().unwrap(); + constraints.insert((last.pos_var + last.size_var) | EQ(REQUIRED) | space as f64); + + constraints } diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index 53dceaa7..62198d68 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -158,7 +158,8 @@ pub(crate) fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObj host_subscribe, host_unsubscribe, host_set_invisible_borders, - host_set_max_height, + host_set_fixed_height, + host_set_fixed_width, host_set_selectable, host_get_plugin_ids, host_open_file, @@ -189,13 +190,24 @@ fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { .unwrap() } -fn host_set_max_height(plugin_env: &PluginEnv, max_height: i32) { - let max_height = max_height as usize; +fn host_set_fixed_height(plugin_env: &PluginEnv, fixed_height: i32) { + let fixed_height = fixed_height as usize; plugin_env .senders - .send_to_screen(ScreenInstruction::SetMaxHeight( + .send_to_screen(ScreenInstruction::SetFixedHeight( PaneId::Plugin(plugin_env.plugin_id), - max_height, + fixed_height, + )) + .unwrap() +} + +fn host_set_fixed_width(plugin_env: &PluginEnv, fixed_width: i32) { + let fixed_width = fixed_width as usize; + plugin_env + .senders + .send_to_screen(ScreenInstruction::SetFixedWidth( + PaneId::Plugin(plugin_env.plugin_id), + fixed_width, )) .unwrap() } diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 856dcc54..ed033d4b 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -17,8 +17,12 @@ pub fn unsubscribe(event_types: &[EventType]) { // Plugin Settings -pub fn set_max_height(max_height: i32) { - unsafe { host_set_max_height(max_height) }; +pub fn set_fixed_height(fixed_height: i32) { + unsafe { host_set_fixed_height(fixed_height) }; +} + +pub fn set_fixed_width(fixed_width: i32) { + unsafe { host_set_fixed_width(fixed_width) }; } pub fn set_selectable(selectable: bool) { @@ -64,7 +68,8 @@ pub fn object_to_stdout(object: &impl Serialize) { extern "C" { fn host_subscribe(); fn host_unsubscribe(); - fn host_set_max_height(max_height: i32); + fn host_set_fixed_height(fixed_height: i32); + fn host_set_fixed_width(fixed_width: i32); fn host_set_selectable(selectable: i32); fn host_set_invisible_borders(invisible_borders: i32); fn host_get_plugin_ids(); diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index b47ddb9a..b37a3a07 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -207,7 +207,8 @@ pub enum ScreenContext { ToggleActiveTerminalFullscreen, SetSelectable, SetInvisibleBorders, - SetMaxHeight, + SetFixedHeight, + SetFixedWidth, ClosePane, ApplyLayout, NewTab, diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 2744c43f..b242b8ba 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -9,8 +9,11 @@ pub struct PositionAndSize { pub y: usize, pub rows: usize, pub columns: usize, - pub max_rows: Option, - pub max_columns: Option, + // FIXME: Honestly, these shouldn't exist and rows / columns should be enums like: + // Dimension::Flex(usize) / Dimension::Fixed(usize), but 400+ compiler errors is more than + // I'm in the mood for right now... + pub rows_fixed: bool, + pub cols_fixed: bool, } impl From for PositionAndSize { From 5164bd99b7750122b36fd1fa54a90047e483067c Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Thu, 3 Jun 2021 12:38:23 +0100 Subject: [PATCH 2/3] Fixed a build warning and renamed a struct field --- default-plugins/tab-bar/src/main.rs | 1 - default-plugins/tab-bar/src/tab.rs | 1 - src/tests/integration/basic.rs | 28 +++++------ src/tests/integration/close_pane.rs | 26 +++++----- src/tests/integration/compatibility.rs | 42 ++++++++-------- src/tests/integration/layouts.rs | 2 +- src/tests/integration/move_focus_down.rs | 4 +- src/tests/integration/move_focus_left.rs | 6 +-- src/tests/integration/move_focus_right.rs | 6 +-- src/tests/integration/move_focus_up.rs | 4 +- src/tests/integration/resize_down.rs | 26 +++++----- src/tests/integration/resize_left.rs | 26 +++++----- src/tests/integration/resize_right.rs | 26 +++++----- src/tests/integration/resize_up.rs | 26 +++++----- src/tests/integration/tabs.rs | 16 +++--- .../integration/terminal_window_resize.rs | 16 +++--- src/tests/integration/toggle_fullscreen.rs | 4 +- zellij-server/src/panes/plugin_pane.rs | 16 +++--- zellij-server/src/panes/terminal_pane.rs | 18 +++---- zellij-server/src/tab.rs | 50 +++++++++---------- zellij-server/src/ui/layout.rs | 14 +++--- zellij-server/src/ui/pane_resizer.rs | 8 +-- zellij-utils/src/pane_size.rs | 4 +- 23 files changed, 184 insertions(+), 186 deletions(-) diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs index 7ff639e8..1f83dcde 100644 --- a/default-plugins/tab-bar/src/main.rs +++ b/default-plugins/tab-bar/src/main.rs @@ -57,7 +57,6 @@ impl ZellijPlugin for State { let tab = tab_style( tabname, t.active, - t.position, t.is_sync_panes_active, self.mode_info.palette, self.mode_info.capabilities, diff --git a/default-plugins/tab-bar/src/tab.rs b/default-plugins/tab-bar/src/tab.rs index 7e83e85b..9edfba13 100644 --- a/default-plugins/tab-bar/src/tab.rs +++ b/default-plugins/tab-bar/src/tab.rs @@ -40,7 +40,6 @@ pub fn non_active_tab(text: String, palette: Palette, separator: &str) -> LinePa pub fn tab_style( text: String, is_active_tab: bool, - position: usize, is_sync_panes_active: bool, palette: Palette, capabilities: PluginCapabilities, diff --git a/src/tests/integration/basic.rs b/src/tests/integration/basic.rs index 640b1c55..155ff8b1 100644 --- a/src/tests/integration/basic.rs +++ b/src/tests/integration/basic.rs @@ -20,7 +20,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn starts_with_one_terminal() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -48,7 +48,7 @@ pub fn starts_with_one_terminal() { #[test] pub fn split_terminals_vertically() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -76,7 +76,7 @@ pub fn split_terminals_vertically() { #[test] pub fn split_terminals_horizontally() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -105,7 +105,7 @@ pub fn split_terminals_horizontally() { pub fn split_largest_terminal() { // this finds the largest pane and splits along its longest edge (vertically or horizontally) let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -139,7 +139,7 @@ pub fn split_largest_terminal() { #[test] pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { let fake_win_size = PositionAndSize { - columns: 8, + cols: 8, rows: 20, x: 0, y: 0, @@ -167,7 +167,7 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { #[test] pub fn cannot_split_terminals_horizontally_when_active_terminal_is_too_small() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 4, x: 0, y: 0, @@ -195,7 +195,7 @@ pub fn cannot_split_terminals_horizontally_when_active_terminal_is_too_small() { #[test] pub fn cannot_split_largest_terminal_when_there_is_no_room() { let fake_win_size = PositionAndSize { - columns: 8, + cols: 8, rows: 4, x: 0, y: 0, @@ -223,7 +223,7 @@ pub fn cannot_split_largest_terminal_when_there_is_no_room() { #[test] pub fn scrolling_up_inside_a_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -259,7 +259,7 @@ pub fn scrolling_up_inside_a_pane() { #[test] pub fn scrolling_down_inside_a_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -297,7 +297,7 @@ pub fn scrolling_down_inside_a_pane() { #[test] pub fn scrolling_page_up_inside_a_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -332,7 +332,7 @@ pub fn scrolling_page_up_inside_a_pane() { #[test] pub fn scrolling_page_down_inside_a_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -372,7 +372,7 @@ pub fn max_panes() { // with the --max-panes option, we only allow a certain amount of panes on screen // simultaneously, new panes beyond this limit will close older panes on screen let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -409,7 +409,7 @@ pub fn max_panes() { #[test] pub fn toggle_focused_pane_fullscreen() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -452,7 +452,7 @@ pub fn bracketed_paste() { // since it's inside a bracketed paste block, while the "QUIT" command is, since it is already // past the block let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/close_pane.rs b/src/tests/integration/close_pane.rs index 59881514..afdc3f2e 100644 --- a/src/tests/integration/close_pane.rs +++ b/src/tests/integration/close_pane.rs @@ -28,7 +28,7 @@ pub fn close_pane_with_another_pane_above_it() { // └───────────┘ └───────────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -70,7 +70,7 @@ pub fn close_pane_with_another_pane_below_it() { // └───────────┘ └───────────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -111,7 +111,7 @@ pub fn close_pane_with_another_pane_to_the_left() { // └─────┴─────┘ └──────────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -151,7 +151,7 @@ pub fn close_pane_with_another_pane_to_the_right() { // └─────┴─────┘ └──────────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -194,7 +194,7 @@ pub fn close_pane_with_multiple_panes_above_it() { // └───────────┘ └─────┴─────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -240,7 +240,7 @@ pub fn close_pane_with_multiple_panes_below_it() { // └─────┴─────┘ └─────┴─────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -284,7 +284,7 @@ pub fn close_pane_with_multiple_panes_to_the_left() { // └─────┴─────┘ └──────────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -330,7 +330,7 @@ pub fn close_pane_with_multiple_panes_to_the_right() { // └─────┴─────┘ └──────────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -374,7 +374,7 @@ pub fn close_pane_with_multiple_panes_above_it_away_from_screen_edges() { // └───┴───────┴───┘ └───┴───┴───┴───┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -441,7 +441,7 @@ pub fn close_pane_with_multiple_panes_below_it_away_from_screen_edges() { // └───┴───┴───┴───┘ └───┴───┴───┴───┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -506,7 +506,7 @@ pub fn close_pane_with_multiple_panes_to_the_left_away_from_screen_edges() { // └────┴──────┘ └────┴──────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 30, x: 0, y: 0, @@ -571,7 +571,7 @@ pub fn close_pane_with_multiple_panes_to_the_right_away_from_screen_edges() { // └────┴──────┘ └────┴──────┘ // █ == pane being closed let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 30, x: 0, y: 0, @@ -626,7 +626,7 @@ pub fn close_pane_with_multiple_panes_to_the_right_away_from_screen_edges() { #[test] pub fn closing_last_pane_exits_app() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/compatibility.rs b/src/tests/integration/compatibility.rs index ec983b09..06a409ff 100644 --- a/src/tests/integration/compatibility.rs +++ b/src/tests/integration/compatibility.rs @@ -27,14 +27,14 @@ use zellij_utils::input::config::Config; fn get_fake_os_input(fake_win_size: &PositionAndSize, fixture_name: &str) -> FakeInputOutput { let mut tty_inputs = HashMap::new(); let fixture_bytes = Bytes::from_file_in_fixtures(&fixture_name); - tty_inputs.insert(fake_win_size.columns as u16, fixture_bytes); + tty_inputs.insert(fake_win_size.cols as u16, fixture_bytes); FakeInputOutput::new(fake_win_size.clone()).with_tty_inputs(tty_inputs) } #[test] pub fn run_bandwhich_from_fish_shell() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -64,7 +64,7 @@ pub fn run_bandwhich_from_fish_shell() { #[test] pub fn fish_tab_completion_options() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -98,7 +98,7 @@ pub fn fish_select_tab_completion_options() { // this is not clearly seen in the snapshot because it does not include styles, // but we can see the command line change and the cursor staying in place let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -136,7 +136,7 @@ pub fn vim_scroll_region_down() { // file // experience appear to the user let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -171,7 +171,7 @@ pub fn vim_ctrl_d() { // end of the scroll region // vim makes sure to fill these empty lines with the rest of the file let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -205,7 +205,7 @@ pub fn vim_ctrl_u() { // this causes the effect of scrolling up X lines (vim replaces the lines with the ones in the // file above the current content) let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -234,7 +234,7 @@ pub fn vim_ctrl_u() { #[test] pub fn htop() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -263,7 +263,7 @@ pub fn htop() { #[test] pub fn htop_scrolling() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -292,7 +292,7 @@ pub fn htop_scrolling() { #[test] pub fn htop_right_scrolling() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -329,7 +329,7 @@ pub fn vim_overwrite() { // * confirm you would like to change the file by pressing 'y' and then ENTER // * if everything looks fine, this test passed :) let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -361,7 +361,7 @@ pub fn clear_scroll_region() { // this means that when vim exits, we get back the previous scroll // buffer let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -390,7 +390,7 @@ pub fn clear_scroll_region() { #[test] pub fn display_tab_characters_properly() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -419,7 +419,7 @@ pub fn display_tab_characters_properly() { #[test] pub fn neovim_insert_mode() { let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -450,7 +450,7 @@ pub fn bash_cursor_linewrap() { // this test makes sure that when we enter a command that is beyond the screen border, that it // immediately goes down one line let fake_win_size = PositionAndSize { - columns: 116, + cols: 116, rows: 28, x: 0, y: 0, @@ -481,7 +481,7 @@ pub fn fish_paste_multiline() { // here we paste a multiline command in fish shell, making sure we support it // going up and changing the colors of our line-wrapped pasted text let fake_win_size = PositionAndSize { - columns: 149, + cols: 149, rows: 28, x: 0, y: 0, @@ -510,7 +510,7 @@ pub fn fish_paste_multiline() { #[test] pub fn git_log() { let fake_win_size = PositionAndSize { - columns: 149, + cols: 149, rows: 28, x: 0, y: 0, @@ -541,7 +541,7 @@ pub fn git_diff_scrollup() { // this tests makes sure that when we have a git diff that exceeds the screen size // we are able to scroll up let fake_win_size = PositionAndSize { - columns: 149, + cols: 149, rows: 28, x: 0, y: 0, @@ -570,7 +570,7 @@ pub fn git_diff_scrollup() { #[test] pub fn emacs_longbuf() { let fake_win_size = PositionAndSize { - columns: 284, + cols: 284, rows: 60, x: 0, y: 0, @@ -599,7 +599,7 @@ pub fn emacs_longbuf() { #[test] pub fn top_and_quit() { let fake_win_size = PositionAndSize { - columns: 235, + cols: 235, rows: 56, x: 0, y: 0, @@ -634,7 +634,7 @@ pub fn exa_plus_omf_theme() { // over existing on-screen content without deleting it, so we must // convert it to spaces let fake_win_size = PositionAndSize { - columns: 235, + cols: 235, rows: 56, x: 0, y: 0, diff --git a/src/tests/integration/layouts.rs b/src/tests/integration/layouts.rs index e6986924..0572de6c 100644 --- a/src/tests/integration/layouts.rs +++ b/src/tests/integration/layouts.rs @@ -16,7 +16,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn accepts_basic_layout() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/move_focus_down.rs b/src/tests/integration/move_focus_down.rs index 2f18ab62..5f3afc6e 100644 --- a/src/tests/integration/move_focus_down.rs +++ b/src/tests/integration/move_focus_down.rs @@ -19,7 +19,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn move_focus_down() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -54,7 +54,7 @@ pub fn move_focus_down() { #[test] pub fn move_focus_down_to_the_most_recently_used_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/move_focus_left.rs b/src/tests/integration/move_focus_left.rs index a9fca49e..32c93770 100644 --- a/src/tests/integration/move_focus_left.rs +++ b/src/tests/integration/move_focus_left.rs @@ -20,7 +20,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn move_focus_left() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -54,7 +54,7 @@ pub fn move_focus_left() { #[test] pub fn move_focus_left_to_the_most_recently_used_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -92,7 +92,7 @@ pub fn move_focus_left_to_the_most_recently_used_pane() { #[test] pub fn move_focus_left_changes_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/move_focus_right.rs b/src/tests/integration/move_focus_right.rs index c85d5b96..370fdcbf 100644 --- a/src/tests/integration/move_focus_right.rs +++ b/src/tests/integration/move_focus_right.rs @@ -20,7 +20,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn move_focus_right() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -55,7 +55,7 @@ pub fn move_focus_right() { #[test] pub fn move_focus_right_to_the_most_recently_used_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -92,7 +92,7 @@ pub fn move_focus_right_to_the_most_recently_used_pane() { #[test] pub fn move_focus_right_changes_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/move_focus_up.rs b/src/tests/integration/move_focus_up.rs index d161ff8a..1111660c 100644 --- a/src/tests/integration/move_focus_up.rs +++ b/src/tests/integration/move_focus_up.rs @@ -19,7 +19,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn move_focus_up() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -53,7 +53,7 @@ pub fn move_focus_up() { #[test] pub fn move_focus_up_to_the_most_recently_used_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/resize_down.rs b/src/tests/integration/resize_down.rs index e5d35c26..6f087643 100644 --- a/src/tests/integration/resize_down.rs +++ b/src/tests/integration/resize_down.rs @@ -29,7 +29,7 @@ pub fn resize_down_with_pane_above() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -73,7 +73,7 @@ pub fn resize_down_with_pane_below() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -122,7 +122,7 @@ pub fn resize_down_with_panes_above_and_below() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 25, x: 0, y: 0, @@ -169,7 +169,7 @@ pub fn resize_down_with_multiple_panes_above() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -219,7 +219,7 @@ pub fn resize_down_with_panes_above_aligned_left_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -271,7 +271,7 @@ pub fn resize_down_with_panes_below_aligned_left_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -322,7 +322,7 @@ pub fn resize_down_with_panes_above_aligned_right_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -371,7 +371,7 @@ pub fn resize_down_with_panes_below_aligned_right_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -421,7 +421,7 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_current_pane() { // └───┴───┴───┘ └───┴───┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -475,7 +475,7 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_current_pane() { // └───┴───┴───┘ └───┴───┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -531,7 +531,7 @@ pub fn resize_down_with_panes_above_aligned_left_and_right_with_panes_to_the_lef // └─┴─┴───┴─┴─┘ └─┴─┴───┴─┴─┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -605,7 +605,7 @@ pub fn resize_down_with_panes_below_aligned_left_and_right_with_to_the_left_and_ // └─┴───────┴─┘ └─┴───────┴─┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -678,7 +678,7 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 7, x: 0, y: 0, diff --git a/src/tests/integration/resize_left.rs b/src/tests/integration/resize_left.rs index 564d5c60..a53e8255 100644 --- a/src/tests/integration/resize_left.rs +++ b/src/tests/integration/resize_left.rs @@ -25,7 +25,7 @@ pub fn resize_left_with_pane_to_the_left() { // └─────┴─────┘ └───┴───────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -67,7 +67,7 @@ pub fn resize_left_with_pane_to_the_right() { // └─────┴─────┘ └───┴───────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -110,7 +110,7 @@ pub fn resize_left_with_panes_to_the_left_and_right() { // └─────┴─────┴─────┘ └─────┴───┴───────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -155,7 +155,7 @@ pub fn resize_left_with_multiple_panes_to_the_left() { // └─────┴─────┘ └───┴───────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -203,7 +203,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_with_current_pane() { // └─────┴─────┘ └───┴───────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -253,7 +253,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_with_current_pane() { // └─────┴─────┘ └───┴───────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -300,7 +300,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_bottom_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -349,7 +349,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_bottom_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -399,7 +399,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_current_pa // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -453,7 +453,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_current_p // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -509,7 +509,7 @@ pub fn resize_left_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abov // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -583,7 +583,7 @@ pub fn resize_left_with_panes_to_the_right_aligned_top_and_bottom_with_panes_abo // █ == focused pane let fake_win_size = PositionAndSize { // TODO: combine with above - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -655,7 +655,7 @@ pub fn cannot_resize_left_when_pane_to_the_left_is_at_minimum_width() { // └─┴─┘ └─┴─┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 9, + cols: 9, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/resize_right.rs b/src/tests/integration/resize_right.rs index 716ed128..f5e62043 100644 --- a/src/tests/integration/resize_right.rs +++ b/src/tests/integration/resize_right.rs @@ -25,7 +25,7 @@ pub fn resize_right_with_pane_to_the_left() { // └─────┴─────┘ └───────┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -67,7 +67,7 @@ pub fn resize_right_with_pane_to_the_right() { // └─────┴─────┘ └───────┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -110,7 +110,7 @@ pub fn resize_right_with_panes_to_the_left_and_right() { // └─────┴─────┴─────┘ └─────┴───────┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -155,7 +155,7 @@ pub fn resize_right_with_multiple_panes_to_the_left() { // └─────┴─────┘ └───────┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -203,7 +203,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_with_current_pane() { // └─────┴─────┘ └───────┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -253,7 +253,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_with_current_pane() { // └─────┴─────┘ └───────┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -300,7 +300,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_bottom_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -349,7 +349,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_bottom_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -399,7 +399,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_current_p // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -453,7 +453,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_current_ // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -509,7 +509,7 @@ pub fn resize_right_with_panes_to_the_left_aligned_top_and_bottom_with_panes_abo // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -582,7 +582,7 @@ pub fn resize_right_with_panes_to_the_right_aligned_top_and_bottom_with_panes_ab // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -654,7 +654,7 @@ pub fn cannot_resize_right_when_pane_to_the_left_is_at_minimum_width() { // └─┴─┘ └─┴─┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 9, + cols: 9, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/resize_up.rs b/src/tests/integration/resize_up.rs index 0097756a..5d68d1d3 100644 --- a/src/tests/integration/resize_up.rs +++ b/src/tests/integration/resize_up.rs @@ -27,7 +27,7 @@ pub fn resize_up_with_pane_above() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -71,7 +71,7 @@ pub fn resize_up_with_pane_below() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -119,7 +119,7 @@ pub fn resize_up_with_panes_above_and_below() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -165,7 +165,7 @@ pub fn resize_up_with_multiple_panes_above() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -213,7 +213,7 @@ pub fn resize_up_with_panes_above_aligned_left_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -265,7 +265,7 @@ pub fn resize_up_with_panes_below_aligned_left_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -316,7 +316,7 @@ pub fn resize_up_with_panes_above_aligned_right_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -365,7 +365,7 @@ pub fn resize_up_with_panes_below_aligned_right_with_current_pane() { // └─────┴─────┘ └─────┴─────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -415,7 +415,7 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_current_pane() { // └───┴───┴───┘ └───┴───┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -469,7 +469,7 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_current_pane() { // └───┴───┴───┘ └───┴───┴───┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -525,7 +525,7 @@ pub fn resize_up_with_panes_above_aligned_left_and_right_with_panes_to_the_left_ // └─┴─┴───┴─┴─┘ └─┴─┴───┴─┴─┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -599,7 +599,7 @@ pub fn resize_up_with_panes_below_aligned_left_and_right_with_to_the_left_and_ri // └─┴───────┴─┘ └─┴───────┴─┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 40, x: 0, y: 0, @@ -672,7 +672,7 @@ pub fn cannot_resize_up_when_pane_above_is_at_minimum_height() { // └───────────┘ └───────────┘ // █ == focused pane let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 7, x: 0, y: 0, diff --git a/src/tests/integration/tabs.rs b/src/tests/integration/tabs.rs index 90f82c6d..922a03bc 100644 --- a/src/tests/integration/tabs.rs +++ b/src/tests/integration/tabs.rs @@ -21,7 +21,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn open_new_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -56,7 +56,7 @@ pub fn open_new_tab() { #[test] pub fn switch_to_prev_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -92,7 +92,7 @@ pub fn switch_to_prev_tab() { #[test] pub fn switch_to_next_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -128,7 +128,7 @@ pub fn switch_to_next_tab() { #[test] pub fn close_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -164,7 +164,7 @@ pub fn close_tab() { #[test] pub fn close_last_pane_in_a_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -201,7 +201,7 @@ pub fn close_last_pane_in_a_tab() { #[test] pub fn close_the_middle_tab() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -240,7 +240,7 @@ pub fn close_the_middle_tab() { #[test] pub fn close_the_tab_that_has_a_pane_in_fullscreen() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -284,7 +284,7 @@ pub fn close_the_tab_that_has_a_pane_in_fullscreen() { #[test] pub fn closing_last_tab_exits_the_app() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/src/tests/integration/terminal_window_resize.rs b/src/tests/integration/terminal_window_resize.rs index 2be41c3a..104e89e8 100644 --- a/src/tests/integration/terminal_window_resize.rs +++ b/src/tests/integration/terminal_window_resize.rs @@ -15,7 +15,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn window_width_decrease_with_one_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -24,7 +24,7 @@ pub fn window_width_decrease_with_one_pane() { let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&QUIT]); fake_input_output.add_sigwinch_event(PositionAndSize { - columns: 90, + cols: 90, rows: 20, x: 0, y: 0, @@ -51,7 +51,7 @@ pub fn window_width_decrease_with_one_pane() { #[test] pub fn window_width_increase_with_one_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -60,7 +60,7 @@ pub fn window_width_increase_with_one_pane() { let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&QUIT]); fake_input_output.add_sigwinch_event(PositionAndSize { - columns: 141, + cols: 141, rows: 20, x: 0, y: 0, @@ -87,7 +87,7 @@ pub fn window_width_increase_with_one_pane() { #[test] pub fn window_height_increase_with_one_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -96,7 +96,7 @@ pub fn window_height_increase_with_one_pane() { let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&QUIT]); fake_input_output.add_sigwinch_event(PositionAndSize { - columns: 121, + cols: 121, rows: 30, x: 0, y: 0, @@ -123,7 +123,7 @@ pub fn window_height_increase_with_one_pane() { #[test] pub fn window_width_and_height_decrease_with_one_pane() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -132,7 +132,7 @@ pub fn window_width_and_height_decrease_with_one_pane() { let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&QUIT]); fake_input_output.add_sigwinch_event(PositionAndSize { - columns: 90, + cols: 90, rows: 10, x: 0, y: 0, diff --git a/src/tests/integration/toggle_fullscreen.rs b/src/tests/integration/toggle_fullscreen.rs index 80257982..52e243e6 100644 --- a/src/tests/integration/toggle_fullscreen.rs +++ b/src/tests/integration/toggle_fullscreen.rs @@ -19,7 +19,7 @@ fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput { #[test] pub fn adding_new_terminal_in_fullscreen() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, @@ -54,7 +54,7 @@ pub fn adding_new_terminal_in_fullscreen() { #[test] pub fn move_focus_is_disabled_in_fullscreen() { let fake_win_size = PositionAndSize { - columns: 121, + cols: 121, rows: 20, x: 0, y: 0, diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index b08d528f..c01c7b87 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -60,7 +60,7 @@ impl Pane for PluginPane { fn columns(&self) -> usize { self.position_and_size_override .unwrap_or(self.position_and_size) - .columns + .cols } fn reset_size_and_position_override(&mut self) { self.position_and_size_override = None; @@ -76,7 +76,7 @@ impl Pane for PluginPane { x, y, rows: size.rows, - columns: size.columns, + cols: size.cols, ..Default::default() }; self.position_and_size_override = Some(position_and_size_override); @@ -117,7 +117,7 @@ impl Pane for PluginPane { self.position_and_size.rows_fixed = true; } fn set_fixed_width(&mut self, fixed_width: usize) { - self.position_and_size.columns = fixed_width; + self.position_and_size.cols = fixed_width; self.position_and_size.cols_fixed = true; } fn render(&mut self) -> Option { @@ -167,20 +167,20 @@ impl Pane for PluginPane { } fn reduce_width_right(&mut self, count: usize) { self.position_and_size.x += count; - self.position_and_size.columns -= count; + self.position_and_size.cols -= count; self.should_render = true; } fn reduce_width_left(&mut self, count: usize) { - self.position_and_size.columns -= count; + self.position_and_size.cols -= count; self.should_render = true; } fn increase_width_left(&mut self, count: usize) { self.position_and_size.x -= count; - self.position_and_size.columns += count; + self.position_and_size.cols += count; self.should_render = true; } fn increase_width_right(&mut self, count: usize) { - self.position_and_size.columns += count; + self.position_and_size.cols += count; self.should_render = true; } fn push_down(&mut self, count: usize) { @@ -215,7 +215,7 @@ impl Pane for PluginPane { } fn max_width(&self) -> Option { if self.position_and_size.cols_fixed { - Some(self.position_and_size.columns) + Some(self.position_and_size.cols) } else { None } diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 2dbd11a0..435037ec 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -58,7 +58,7 @@ impl Pane for TerminalPane { x, y, rows: size.rows, - columns: size.columns, + cols: size.cols, ..Default::default() }; self.position_and_size_override = Some(position_and_size_override); @@ -139,7 +139,7 @@ impl Pane for TerminalPane { self.position_and_size.rows_fixed = true; } fn set_fixed_width(&mut self, fixed_width: usize) { - self.position_and_size.columns = fixed_width; + self.position_and_size.cols = fixed_width; self.position_and_size.cols_fixed = true; } fn set_invisible_borders(&mut self, _invisible_borders: bool) { @@ -217,20 +217,20 @@ impl Pane for TerminalPane { } fn reduce_width_right(&mut self, count: usize) { self.position_and_size.x += count; - self.position_and_size.columns -= count; + self.position_and_size.cols -= count; self.reflow_lines(); } fn reduce_width_left(&mut self, count: usize) { - self.position_and_size.columns -= count; + self.position_and_size.cols -= count; self.reflow_lines(); } fn increase_width_left(&mut self, count: usize) { self.position_and_size.x -= count; - self.position_and_size.columns += count; + self.position_and_size.cols += count; self.reflow_lines(); } fn increase_width_right(&mut self, count: usize) { - self.position_and_size.columns += count; + self.position_and_size.cols += count; self.reflow_lines(); } fn push_down(&mut self, count: usize) { @@ -282,7 +282,7 @@ impl Pane for TerminalPane { impl TerminalPane { pub fn new(pid: RawFd, position_and_size: PositionAndSize, palette: Palette) -> TerminalPane { - let grid = Grid::new(position_and_size.rows, position_and_size.columns, palette); + let grid = Grid::new(position_and_size.rows, position_and_size.cols, palette); TerminalPane { pid, grid, @@ -308,8 +308,8 @@ impl TerminalPane { } pub fn get_columns(&self) -> usize { match &self.position_and_size_override.as_ref() { - Some(position_and_size_override) => position_and_size_override.columns, - None => self.position_and_size.columns as usize, + Some(position_and_size_override) => position_and_size_override.cols, + None => self.position_and_size.cols as usize, } } pub fn get_rows(&self) -> usize { diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index 0ef616b4..e90984ca 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -33,16 +33,16 @@ const MIN_TERMINAL_WIDTH: usize = 4; type BorderAndPaneIds = (usize, Vec); fn split_vertically_with_gap(rect: &PositionAndSize) -> (PositionAndSize, PositionAndSize) { - let width_of_each_half = (rect.columns - 1) / 2; + let width_of_each_half = (rect.cols - 1) / 2; let mut first_rect = *rect; let mut second_rect = *rect; - if rect.columns % 2 == 0 { - first_rect.columns = width_of_each_half + 1; + if rect.cols % 2 == 0 { + first_rect.cols = width_of_each_half + 1; } else { - first_rect.columns = width_of_each_half; + first_rect.cols = width_of_each_half; } - second_rect.x = first_rect.x + first_rect.columns + 1; - second_rect.columns = width_of_each_half; + second_rect.x = first_rect.x + first_rect.cols + 1; + second_rect.cols = width_of_each_half; (first_rect, second_rect) } @@ -280,7 +280,7 @@ impl Tab { x: 0, y: 0, rows: self.full_screen_ws.rows, - columns: self.full_screen_ws.columns, + cols: self.full_screen_ws.cols, ..Default::default() }; self.panes_to_hide.clear(); @@ -295,7 +295,7 @@ impl Tab { terminal_pane.change_pos_and_size(&position_and_size); self.os_api.set_terminal_size_using_fd( *pid, - position_and_size.columns as u16, + position_and_size.cols as u16, position_and_size.rows as u16, ); } @@ -400,7 +400,7 @@ impl Tab { let terminal_to_split = self.panes.get_mut(&terminal_id_to_split).unwrap(); let terminal_ws = PositionAndSize { rows: terminal_to_split.rows(), - columns: terminal_to_split.columns(), + cols: terminal_to_split.columns(), x: terminal_to_split.x(), y: terminal_to_split.y(), ..Default::default() @@ -413,7 +413,7 @@ impl Tab { let new_terminal = TerminalPane::new(term_pid, bottom_winsize, self.colors); self.os_api.set_terminal_size_using_fd( new_terminal.pid, - bottom_winsize.columns as u16, + bottom_winsize.cols as u16, bottom_winsize.rows as u16, ); terminal_to_split.change_pos_and_size(&top_winsize); @@ -421,7 +421,7 @@ impl Tab { if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split { self.os_api.set_terminal_size_using_fd( terminal_id_to_split, - top_winsize.columns as u16, + top_winsize.cols as u16, top_winsize.rows as u16, ); } @@ -433,7 +433,7 @@ impl Tab { let new_terminal = TerminalPane::new(term_pid, right_winsize, self.colors); self.os_api.set_terminal_size_using_fd( new_terminal.pid, - right_winsize.columns as u16, + right_winsize.cols as u16, right_winsize.rows as u16, ); terminal_to_split.change_pos_and_size(&left_winsize); @@ -441,7 +441,7 @@ impl Tab { if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split { self.os_api.set_terminal_size_using_fd( terminal_id_to_split, - left_winsize.columns as u16, + left_winsize.cols as u16, left_winsize.rows as u16, ); } @@ -481,7 +481,7 @@ impl Tab { x: active_pane.x(), y: active_pane.y(), rows: active_pane.rows(), - columns: active_pane.columns(), + cols: active_pane.columns(), ..Default::default() }; let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws); @@ -491,7 +491,7 @@ impl Tab { let new_terminal = TerminalPane::new(term_pid, bottom_winsize, self.colors); self.os_api.set_terminal_size_using_fd( new_terminal.pid, - bottom_winsize.columns as u16, + bottom_winsize.cols as u16, bottom_winsize.rows as u16, ); self.panes.insert(pid, Box::new(new_terminal)); @@ -499,7 +499,7 @@ impl Tab { if let PaneId::Terminal(active_terminal_pid) = active_pane_id { self.os_api.set_terminal_size_using_fd( *active_terminal_pid, - top_winsize.columns as u16, + top_winsize.cols as u16, top_winsize.rows as u16, ); } @@ -538,7 +538,7 @@ impl Tab { x: active_pane.x(), y: active_pane.y(), rows: active_pane.rows(), - columns: active_pane.columns(), + cols: active_pane.columns(), ..Default::default() }; let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws); @@ -548,7 +548,7 @@ impl Tab { let new_terminal = TerminalPane::new(term_pid, right_winsize, self.colors); self.os_api.set_terminal_size_using_fd( new_terminal.pid, - right_winsize.columns as u16, + right_winsize.cols as u16, right_winsize.rows as u16, ); self.panes.insert(pid, Box::new(new_terminal)); @@ -556,7 +556,7 @@ impl Tab { if let PaneId::Terminal(active_terminal_pid) = active_pane_id { self.os_api.set_terminal_size_using_fd( *active_terminal_pid, - left_winsize.columns as u16, + left_winsize.cols as u16, left_winsize.rows as u16, ); } @@ -715,7 +715,7 @@ impl Tab { } let mut output = String::new(); let mut boundaries = Boundaries::new( - self.full_screen_ws.columns as u16, + self.full_screen_ws.cols as u16, self.full_screen_ws.rows as u16, ); let hide_cursor = "\u{1b}[?25l"; @@ -1126,7 +1126,7 @@ impl Tab { } } // rightmost border aligned with a pane border above - let mut right_resize_border = self.full_screen_ws.columns; + let mut right_resize_border = self.full_screen_ws.cols; for terminal in &terminals { let left_terminal_boundary = terminal.x(); if terminal_borders_above @@ -1204,7 +1204,7 @@ impl Tab { } } // leftmost border aligned with a pane border above - let mut right_resize_border = self.full_screen_ws.columns; + let mut right_resize_border = self.full_screen_ws.cols; for terminal in &terminals { let left_terminal_boundary = terminal.x(); if terminal_borders_below @@ -1546,7 +1546,7 @@ impl Tab { return false; } let mut new_pos_and_size_for_pane = pane.position_and_size(); - new_pos_and_size_for_pane.columns += increase_by; + new_pos_and_size_for_pane.cols += increase_by; if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) { return panes_to_the_right.iter().all(|id| { @@ -1714,8 +1714,8 @@ impl Tab { .resize(self.full_screen_ws, new_screen_size) { self.should_clear_display_before_rendering = true; - self.full_screen_ws.columns = - (self.full_screen_ws.columns as isize + column_difference) as usize; + self.full_screen_ws.cols = + (self.full_screen_ws.cols as isize + column_difference) as usize; self.full_screen_ws.rows = (self.full_screen_ws.rows as isize + row_difference) as usize; }; diff --git a/zellij-server/src/ui/layout.rs b/zellij-server/src/ui/layout.rs index 7a19d889..571eec3d 100644 --- a/zellij-server/src/ui/layout.rs +++ b/zellij-server/src/ui/layout.rs @@ -13,7 +13,7 @@ fn split_space_to_parts_vertically( let mut split_parts = Vec::new(); let mut current_x_position = space_to_split.x; let mut current_width = 0; - let max_width = space_to_split.columns - (sizes.len() - 1); // minus space for gaps + let max_width = space_to_split.cols - (sizes.len() - 1); // minus space for gaps let mut parts_to_grow = Vec::new(); @@ -32,7 +32,7 @@ fn split_space_to_parts_vertically( split_parts.push(PositionAndSize { x: current_x_position, y: space_to_split.y, - columns, + cols: columns, rows: space_to_split.rows, ..Default::default() }); @@ -51,11 +51,11 @@ fn split_space_to_parts_vertically( for (idx, part) in split_parts.iter_mut().enumerate() { part.x = current_x_position; if parts_to_grow.contains(&part.x) { - part.columns = new_columns; + part.cols = new_columns; last_flexible_index = idx; } - current_width += part.columns; - current_x_position += part.columns + 1; // 1 for gap + current_width += part.cols; + current_x_position += part.cols + 1; // 1 for gap } } @@ -63,7 +63,7 @@ fn split_space_to_parts_vertically( // we have some extra space left, let's add it to the last flexible part let extra = max_width - current_width; let mut last_part = split_parts.get_mut(last_flexible_index).unwrap(); - last_part.columns += extra; + last_part.cols += extra; for part in (&mut split_parts[last_flexible_index + 1..]).iter_mut() { part.x += extra; } @@ -96,7 +96,7 @@ fn split_space_to_parts_horizontally( split_parts.push(PositionAndSize { x: space_to_split.x, y: current_y_position, - columns: space_to_split.columns, + cols: space_to_split.cols, rows, ..Default::default() }); diff --git a/zellij-server/src/ui/pane_resizer.rs b/zellij-server/src/ui/pane_resizer.rs index 73673a9c..ced0193e 100644 --- a/zellij-server/src/ui/pane_resizer.rs +++ b/zellij-server/src/ui/pane_resizer.rs @@ -81,10 +81,10 @@ impl<'a> PaneResizer<'a> { current_size: PositionAndSize, new_size: PositionAndSize, ) -> Option<(isize, isize)> { - let col_delta = new_size.columns as isize - current_size.columns as isize; + let col_delta = new_size.cols as isize - current_size.cols as isize; let row_delta = new_size.rows as isize - current_size.rows as isize; if col_delta != 0 { - let spans = self.solve_direction(Direction::Horizontal, new_size.columns)?; + let spans = self.solve_direction(Direction::Horizontal, new_size.cols)?; self.collapse_spans(&spans); } self.solver.reset(); @@ -162,7 +162,7 @@ impl<'a> PaneResizer<'a> { direction, fixed: pas.cols_fixed, pos: pas.x, - size: pas.columns, + size: pas.cols, pos_var, size_var, }, @@ -186,7 +186,7 @@ impl<'a> PaneResizer<'a> { match span.direction { Direction::Horizontal => pane.change_pos_and_size(&PositionAndSize { x: fetch_usize(span.pos_var), - columns: fetch_usize(span.size_var), + cols: fetch_usize(span.size_var), ..pane.position_and_size() }), Direction::Vertical => pane.change_pos_and_size(&PositionAndSize { diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index b242b8ba..8a939381 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -8,7 +8,7 @@ pub struct PositionAndSize { pub x: usize, pub y: usize, pub rows: usize, - pub columns: usize, + pub cols: usize, // FIXME: Honestly, these shouldn't exist and rows / columns should be enums like: // Dimension::Flex(usize) / Dimension::Fixed(usize), but 400+ compiler errors is more than // I'm in the mood for right now... @@ -19,7 +19,7 @@ pub struct PositionAndSize { impl From for PositionAndSize { fn from(winsize: Winsize) -> PositionAndSize { PositionAndSize { - columns: winsize.ws_col as usize, + cols: winsize.ws_col as usize, rows: winsize.ws_row as usize, ..Default::default() } From a9ce13c1d2ff02981ccb552bf3da9355ec63273b Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Thu, 3 Jun 2021 13:05:52 +0100 Subject: [PATCH 3/3] feat(ui): added feature for an experimental resize The `parametric_resize_beta` feature has been added that enables the new resize system. This change also introduces some background tweaks that start laying the groundwork for future resize work without breaking functionality. --- Cargo.toml | 1 + zellij-server/src/lib.rs | 2 +- zellij-server/src/tab.rs | 6 +- zellij-server/src/ui/mod.rs | 1 + zellij-server/src/ui/pane_resizer.rs | 716 +++++++++++++++------- zellij-server/src/ui/pane_resizer_beta.rs | 248 ++++++++ 6 files changed, 755 insertions(+), 219 deletions(-) create mode 100644 zellij-server/src/ui/pane_resizer_beta.rs diff --git a/Cargo.toml b/Cargo.toml index 64077b58..4d6434d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,3 +57,4 @@ assets = [ [features] disable_automatic_asset_installation = [] +parametric_resize_beta = [] diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 4dff7838..9b859d06 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -114,7 +114,7 @@ pub fn start_server(os_input: Box, socket_path: PathBuf) { .working_directory(std::env::current_dir().unwrap()) .umask(0o077) // FIXME: My cherished `dbg!` was broken, so this is a hack to bring it back - .stderr(std::fs::File::create("dbg.log").unwrap()) + //.stderr(std::fs::File::create("dbg.log").unwrap()) .start() .expect("could not daemonize the server process"); diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index e90984ca..f07e4a41 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -3,12 +3,16 @@ use zellij_utils::{serde, zellij_tile}; +#[cfg(not(feature = "parametric_resize_beta"))] +use crate::ui::pane_resizer::PaneResizer; +#[cfg(feature = "parametric_resize_beta")] +use crate::ui::pane_resizer_beta::PaneResizer; use crate::{ os_input_output::ServerOsApi, panes::{PaneId, PluginPane, TerminalPane}, pty::{PtyInstruction, VteBytes}, thread_bus::ThreadSenders, - ui::{boundaries::Boundaries, layout::Layout, pane_resizer::PaneResizer}, + ui::{boundaries::Boundaries, layout::Layout}, wasm_vm::PluginInstruction, ServerInstruction, SessionState, }; diff --git a/zellij-server/src/ui/mod.rs b/zellij-server/src/ui/mod.rs index 1e4c85fd..6d6a4375 100644 --- a/zellij-server/src/ui/mod.rs +++ b/zellij-server/src/ui/mod.rs @@ -1,3 +1,4 @@ pub mod boundaries; pub mod layout; pub mod pane_resizer; +pub mod pane_resizer_beta; diff --git a/zellij-server/src/ui/pane_resizer.rs b/zellij-server/src/ui/pane_resizer.rs index ced0193e..5ab06548 100644 --- a/zellij-server/src/ui/pane_resizer.rs +++ b/zellij-server/src/ui/pane_resizer.rs @@ -1,247 +1,529 @@ use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane}; -use cassowary::{ - strength::{REQUIRED, STRONG}, - Constraint, Solver, Variable, - WeightedRelation::*, -}; use std::{ + cmp::Ordering, collections::{BTreeMap, HashSet}, - ops::Not, }; use zellij_utils::pane_size::PositionAndSize; -const GAP_SIZE: usize = 1; // Panes are separated by this number of rows / columns - -pub struct PaneResizer<'a> { +pub(crate) struct PaneResizer<'a> { panes: &'a mut BTreeMap>, - vars: BTreeMap, - solver: Solver, os_api: &'a mut Box, } -#[derive(Debug, Clone, Copy)] -enum Direction { - Horizontal, - Vertical, -} - -impl Not for Direction { - type Output = Self; - - fn not(self) -> Self::Output { - match self { - Direction::Horizontal => Direction::Vertical, - Direction::Vertical => Direction::Horizontal, - } - } -} - -#[derive(Debug, Clone, Copy)] -struct Span { - pid: PaneId, - direction: Direction, - fixed: bool, - pos: usize, - size: usize, - pos_var: Variable, - size_var: Variable, -} - // TODO: currently there are some functions here duplicated with Tab // all resizing functions should move here -// FIXME: -// 1. Rounding causes a loss of ratios, I need to store an internal f64 for -// each pane as well as the displayed usize and add custom rounding logic. -// 2. Vertical resizing doesn't seem to respect the space consumed by the tab -// and status bars? -// 3. A 2x2 layout and simultaneous vertical + horizontal resizing sometimes -// leads to unsolvable constraints? Maybe related to 2 (and possibly 1). -// I should sanity-check the `spans_in_boundary()` here! - impl<'a> PaneResizer<'a> { pub fn new( panes: &'a mut BTreeMap>, os_api: &'a mut Box, ) -> Self { - let mut vars = BTreeMap::new(); - for &k in panes.keys() { - vars.insert(k, (Variable::new(), Variable::new())); - } - PaneResizer { - panes, - vars, - solver: Solver::new(), - os_api, - } + PaneResizer { panes, os_api } } - pub fn resize( &mut self, - current_size: PositionAndSize, + mut current_size: PositionAndSize, new_size: PositionAndSize, ) -> Option<(isize, isize)> { - let col_delta = new_size.cols as isize - current_size.cols as isize; - let row_delta = new_size.rows as isize - current_size.rows as isize; - if col_delta != 0 { - let spans = self.solve_direction(Direction::Horizontal, new_size.cols)?; - self.collapse_spans(&spans); - } - self.solver.reset(); - if row_delta != 0 { - let spans = self.solve_direction(Direction::Vertical, new_size.rows)?; - self.collapse_spans(&spans); - } - Some((col_delta, row_delta)) - } - - fn solve_direction(&mut self, direction: Direction, space: usize) -> Option> { - let mut grid = Vec::new(); - for boundary in self.grid_boundaries(direction) { - grid.push(self.spans_in_boundary(direction, boundary)); - } - - let constraints: Vec<_> = grid - .iter() - .flat_map(|s| constrain_spans(space, s)) - .collect(); - - // FIXME: This line needs to be restored before merging! - //self.solver.add_constraints(&constraints).ok()?; - self.solver.add_constraints(&constraints).unwrap(); - Some(grid.into_iter().flatten().collect()) - } - - fn grid_boundaries(&self, direction: Direction) -> Vec<(usize, usize)> { - // Select the spans running *perpendicular* to the direction of resize - let spans: Vec = self - .panes - .values() - .map(|p| self.get_span(!direction, p.as_ref())) - .collect(); - - let mut last_edge = 0; - let mut bounds = Vec::new(); - loop { - let mut spans_on_edge: Vec<&Span> = - spans.iter().filter(|p| p.pos == last_edge).collect(); - spans_on_edge.sort_unstable_by_key(|s| s.size); - if let Some(next) = spans_on_edge.first() { - let next_edge = last_edge + next.size; - bounds.push((last_edge, next_edge)); - last_edge = next_edge + GAP_SIZE; - } else { - break; + // (column_difference, row_difference) + let mut successfully_resized = false; + let mut column_difference: isize = 0; + let mut row_difference: isize = 0; + match new_size.cols.cmp(¤t_size.cols) { + Ordering::Greater => { + let increase_by = new_size.cols - current_size.cols; + if let Some(panes_to_resize) = find_increasable_vertical_chain( + &self.panes, + increase_by, + current_size.cols, + current_size.rows, + ) { + self.increase_panes_right_and_push_adjacents_right( + panes_to_resize, + increase_by, + ); + column_difference = new_size.cols as isize - current_size.cols as isize; + current_size.cols = (current_size.cols as isize + column_difference) as usize; + successfully_resized = true; + }; } - } - bounds - } - - fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec { - let (start, end) = boundary; - let bwn = |v| start <= v && v < end; - let mut spans: Vec<_> = self - .panes - .values() - .filter(|p| { - let s = self.get_span(!direction, p.as_ref()); - bwn(s.pos) || bwn(s.pos + s.size) - }) - .map(|p| self.get_span(direction, p.as_ref())) - .collect(); - spans.sort_unstable_by_key(|s| s.pos); - spans - } - - fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span { - let pas = pane.position_and_size(); - let (pos_var, size_var) = self.vars[&pane.pid()]; - match direction { - Direction::Horizontal => Span { - pid: pane.pid(), - direction, - fixed: pas.cols_fixed, - pos: pas.x, - size: pas.cols, - pos_var, - size_var, - }, - Direction::Vertical => Span { - pid: pane.pid(), - direction, - fixed: pas.rows_fixed, - pos: pas.y, - size: pas.rows, - pos_var, - size_var, - }, - } - } - - fn collapse_spans(&mut self, spans: &[Span]) { - for span in spans { - let solver = &self.solver; // Hand-holding the borrow-checker - let pane = self.panes.get_mut(&span.pid).unwrap(); - let fetch_usize = |v| solver.get_value(v).round() as usize; - match span.direction { - Direction::Horizontal => pane.change_pos_and_size(&PositionAndSize { - x: fetch_usize(span.pos_var), - cols: fetch_usize(span.size_var), - ..pane.position_and_size() - }), - Direction::Vertical => pane.change_pos_and_size(&PositionAndSize { - y: fetch_usize(span.pos_var), - rows: fetch_usize(span.size_var), - ..pane.position_and_size() - }), - } - if let PaneId::Terminal(pid) = pane.pid() { - self.os_api.set_terminal_size_using_fd( - pid, - pane.columns() as u16, - pane.rows() as u16, - ); + Ordering::Less => { + let reduce_by = current_size.cols - new_size.cols; + if let Some(panes_to_resize) = find_reducible_vertical_chain( + &self.panes, + reduce_by, + current_size.cols, + current_size.rows, + ) { + self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by); + column_difference = new_size.cols as isize - current_size.cols as isize; + current_size.cols = (current_size.cols as isize + column_difference) as usize; + successfully_resized = true; + }; } + Ordering::Equal => (), } - } -} - -fn constrain_spans(space: usize, spans: &[Span]) -> HashSet { - let mut constraints = HashSet::new(); - - // The first span needs to start at 0 - constraints.insert(spans[0].pos_var | EQ(REQUIRED) | 0.0); - - // Calculating "flexible" space (space not consumed by fixed-size spans) - let gap_space = GAP_SIZE * (spans.len() - 1); - let old_flex_space = spans - .iter() - .fold(0, |a, s| if !s.fixed { a + s.size } else { a }); - let new_flex_space = spans.iter().fold( - space - gap_space, - |a, s| if s.fixed { a - s.size } else { a }, - ); - - // Keep spans stuck together - for pair in spans.windows(2) { - let (ls, rs) = (pair[0], pair[1]); - constraints - .insert((ls.pos_var + ls.size_var + GAP_SIZE as f64) | EQ(REQUIRED) | rs.pos_var); - } - - // Try to maintain ratios and lock non-flexible sizes - for span in spans { - if span.fixed { - constraints.insert(span.size_var | EQ(REQUIRED) | span.size as f64); + match new_size.rows.cmp(¤t_size.rows) { + Ordering::Greater => { + let increase_by = new_size.rows - current_size.rows; + if let Some(panes_to_resize) = find_increasable_horizontal_chain( + &self.panes, + increase_by, + current_size.cols, + current_size.rows, + ) { + self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by); + row_difference = new_size.rows as isize - current_size.rows as isize; + current_size.rows = (current_size.rows as isize + row_difference) as usize; + successfully_resized = true; + }; + } + Ordering::Less => { + let reduce_by = current_size.rows - new_size.rows; + if let Some(panes_to_resize) = find_reducible_horizontal_chain( + &self.panes, + reduce_by, + current_size.cols, + current_size.rows, + ) { + self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by); + row_difference = new_size.rows as isize - current_size.rows as isize; + current_size.rows = (current_size.rows as isize + row_difference) as usize; + successfully_resized = true; + }; + } + Ordering::Equal => (), + } + if successfully_resized { + Some((column_difference, row_difference)) } else { - let ratio = span.size as f64 / old_flex_space as f64; - constraints.insert((span.size_var / new_flex_space as f64) | EQ(STRONG) | ratio); + None + } + } + fn reduce_panes_left_and_pull_adjacents_left( + &mut self, + panes_to_reduce: Vec, + reduce_by: usize, + ) { + let mut pulled_panes: HashSet = HashSet::new(); + for pane_id in panes_to_reduce { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_pull = self.panes.values_mut().filter(|p| { + p.x() > pane_x + pane_columns + && (p.y() <= pane_y && p.y() + p.rows() >= pane_y + || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows) + }); + for pane in panes_to_pull { + if !pulled_panes.contains(&pane.pid()) { + pane.pull_left(reduce_by); + pulled_panes.insert(pane.pid()); + } + } + self.reduce_pane_width_left(&pane_id, reduce_by); + } + } + fn reduce_panes_up_and_pull_adjacents_up( + &mut self, + panes_to_reduce: Vec, + reduce_by: usize, + ) { + let mut pulled_panes: HashSet = HashSet::new(); + for pane_id in panes_to_reduce { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_pull = self.panes.values_mut().filter(|p| { + p.y() > pane_y + pane_rows + && (p.x() <= pane_x && p.x() + p.columns() >= pane_x + || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns) + }); + for pane in panes_to_pull { + if !pulled_panes.contains(&pane.pid()) { + pane.pull_up(reduce_by); + pulled_panes.insert(pane.pid()); + } + } + self.reduce_pane_height_up(&pane_id, reduce_by); + } + } + fn increase_panes_down_and_push_down_adjacents( + &mut self, + panes_to_increase: Vec, + increase_by: usize, + ) { + let mut pushed_panes: HashSet = HashSet::new(); + for pane_id in panes_to_increase { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_push = self.panes.values_mut().filter(|p| { + p.y() > pane_y + pane_rows + && (p.x() <= pane_x && p.x() + p.columns() >= pane_x + || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns) + }); + for pane in panes_to_push { + if !pushed_panes.contains(&pane.pid()) { + pane.push_down(increase_by); + pushed_panes.insert(pane.pid()); + } + } + self.increase_pane_height_down(&pane_id, increase_by); + } + } + fn increase_panes_right_and_push_adjacents_right( + &mut self, + panes_to_increase: Vec, + increase_by: usize, + ) { + let mut pushed_panes: HashSet = HashSet::new(); + for pane_id in panes_to_increase { + let (pane_x, pane_y, pane_columns, pane_rows) = { + let pane = self.panes.get(&pane_id).unwrap(); + (pane.x(), pane.y(), pane.columns(), pane.rows()) + }; + let panes_to_push = self.panes.values_mut().filter(|p| { + p.x() > pane_x + pane_columns + && (p.y() <= pane_y && p.y() + p.rows() >= pane_y + || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows) + }); + for pane in panes_to_push { + if !pushed_panes.contains(&pane.pid()) { + pane.push_right(increase_by); + pushed_panes.insert(pane.pid()); + } + } + self.increase_pane_width_right(&pane_id, increase_by); + } + } + fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.reduce_height_up(count); + if let PaneId::Terminal(pid) = id { + self.os_api + .set_terminal_size_using_fd(*pid, pane.columns() as u16, pane.rows() as u16); + } + } + fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.increase_height_down(count); + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api + .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); + } + } + fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.increase_width_right(count); + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api + .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); + } + } + fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) { + let pane = self.panes.get_mut(id).unwrap(); + pane.reduce_width_left(count); + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api + .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16); + } + } +} + +fn find_next_increasable_horizontal_pane( + panes: &BTreeMap>, + right_of: &dyn Pane, + increase_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| { + p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of) + }, // TODO: the name here is wrong, it should be vertically_overlaps_with + ); + let resizable_candidates = + next_pane_candidates.filter(|p| p.can_increase_height_by(increase_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.y() < p.y() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_next_increasable_vertical_pane( + panes: &BTreeMap>, + below: &dyn Pane, + increase_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with + ); + let resizable_candidates = + next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.x() < p.x() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_next_reducible_vertical_pane( + panes: &BTreeMap>, + below: &dyn Pane, + reduce_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with + ); + let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.x() < p.x() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_next_reducible_horizontal_pane( + panes: &BTreeMap>, + right_of: &dyn Pane, + reduce_by: usize, +) -> Option { + let next_pane_candidates = panes.values().filter( + |p| { + p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of) + }, // TODO: the name here is wrong, it should be vertically_overlaps_with + ); + let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by)); + resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id { + Some(next_pane) => { + let next_pane = panes.get(&next_pane).unwrap(); + if next_pane.y() < p.y() { + next_pane_id + } else { + Some(p.pid()) + } + } + None => Some(p.pid()), + }) +} + +fn find_increasable_horizontal_chain( + panes: &BTreeMap>, + increase_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut horizontal_coordinate = 0; + loop { + if horizontal_coordinate == screen_height { + return None; + } + + match panes + .values() + .find(|p| p.x() == 0 && p.y() == horizontal_coordinate) + { + Some(leftmost_pane) => { + if !leftmost_pane.can_increase_height_by(increase_by) { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = leftmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.x() + current_pane.columns() == screen_width { + return Some(panes_to_resize); + } + match find_next_increasable_horizontal_pane( + panes, + current_pane.as_ref(), + increase_by, + ) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} + +fn find_increasable_vertical_chain( + panes: &BTreeMap>, + increase_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut vertical_coordinate = 0; + loop { + if vertical_coordinate == screen_width { + return None; + } + + match panes + .values() + .find(|p| p.y() == 0 && p.x() == vertical_coordinate) + { + Some(topmost_pane) => { + if !topmost_pane.can_increase_width_by(increase_by) { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = topmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.y() + current_pane.rows() == screen_height { + return Some(panes_to_resize); + } + match find_next_increasable_vertical_pane( + panes, + current_pane.as_ref(), + increase_by, + ) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} + +fn find_reducible_horizontal_chain( + panes: &BTreeMap>, + reduce_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut horizontal_coordinate = 0; + loop { + if horizontal_coordinate == screen_height { + return None; + } + + match panes + .values() + .find(|p| p.x() == 0 && p.y() == horizontal_coordinate) + { + Some(leftmost_pane) => { + if !leftmost_pane.can_reduce_height_by(reduce_by) { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = leftmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.x() + current_pane.columns() == screen_width { + return Some(panes_to_resize); + } + match find_next_reducible_horizontal_pane( + panes, + current_pane.as_ref(), + reduce_by, + ) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1; + break; + } + }; + } + } + None => { + return None; + } + } + } +} + +fn find_reducible_vertical_chain( + panes: &BTreeMap>, + increase_by: usize, + screen_width: usize, + screen_height: usize, // TODO: this is the previous size (make this clearer) +) -> Option> { + let mut vertical_coordinate = 0; + loop { + if vertical_coordinate == screen_width { + return None; + } + + match panes + .values() + .find(|p| p.y() == 0 && p.x() == vertical_coordinate) + { + Some(topmost_pane) => { + if !topmost_pane.can_reduce_width_by(increase_by) { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + continue; + } + let mut panes_to_resize = vec![]; + let mut current_pane = topmost_pane; + loop { + panes_to_resize.push(current_pane.pid()); + if current_pane.y() + current_pane.rows() == screen_height { + return Some(panes_to_resize); + } + match find_next_reducible_vertical_pane( + panes, + current_pane.as_ref(), + increase_by, + ) { + Some(next_pane_id) => { + current_pane = panes.get(&next_pane_id).unwrap(); + } + None => { + vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1; + break; + } + }; + } + } + None => { + return None; + } } } - - // The last pane needs to end at the end of the space - let last = spans.last().unwrap(); - constraints.insert((last.pos_var + last.size_var) | EQ(REQUIRED) | space as f64); - - constraints } diff --git a/zellij-server/src/ui/pane_resizer_beta.rs b/zellij-server/src/ui/pane_resizer_beta.rs new file mode 100644 index 00000000..b6ffd784 --- /dev/null +++ b/zellij-server/src/ui/pane_resizer_beta.rs @@ -0,0 +1,248 @@ +#![allow(dead_code)] +use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane}; +use cassowary::{ + strength::{REQUIRED, STRONG}, + Constraint, Solver, Variable, + WeightedRelation::*, +}; +use std::{ + collections::{BTreeMap, HashSet}, + ops::Not, +}; +use zellij_utils::pane_size::PositionAndSize; + +const GAP_SIZE: usize = 1; // Panes are separated by this number of rows / columns + +pub struct PaneResizer<'a> { + panes: &'a mut BTreeMap>, + vars: BTreeMap, + solver: Solver, + os_api: &'a mut Box, +} + +#[derive(Debug, Clone, Copy)] +enum Direction { + Horizontal, + Vertical, +} + +impl Not for Direction { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + Direction::Horizontal => Direction::Vertical, + Direction::Vertical => Direction::Horizontal, + } + } +} + +#[derive(Debug, Clone, Copy)] +struct Span { + pid: PaneId, + direction: Direction, + fixed: bool, + pos: usize, + size: usize, + pos_var: Variable, + size_var: Variable, +} + +// TODO: currently there are some functions here duplicated with Tab +// all resizing functions should move here + +// FIXME: +// 1. Rounding causes a loss of ratios, I need to store an internal f64 for +// each pane as well as the displayed usize and add custom rounding logic. +// 2. Vertical resizing doesn't seem to respect the space consumed by the tab +// and status bars? +// 3. A 2x2 layout and simultaneous vertical + horizontal resizing sometimes +// leads to unsolvable constraints? Maybe related to 2 (and possibly 1). +// I should sanity-check the `spans_in_boundary()` here! + +impl<'a> PaneResizer<'a> { + pub fn new( + panes: &'a mut BTreeMap>, + os_api: &'a mut Box, + ) -> Self { + let mut vars = BTreeMap::new(); + for &k in panes.keys() { + vars.insert(k, (Variable::new(), Variable::new())); + } + PaneResizer { + panes, + vars, + solver: Solver::new(), + os_api, + } + } + + pub fn resize( + &mut self, + current_size: PositionAndSize, + new_size: PositionAndSize, + ) -> Option<(isize, isize)> { + let col_delta = new_size.cols as isize - current_size.cols as isize; + let row_delta = new_size.rows as isize - current_size.rows as isize; + if col_delta != 0 { + let spans = self.solve_direction(Direction::Horizontal, new_size.cols)?; + self.collapse_spans(&spans); + } + self.solver.reset(); + if row_delta != 0 { + let spans = self.solve_direction(Direction::Vertical, new_size.rows)?; + self.collapse_spans(&spans); + } + Some((col_delta, row_delta)) + } + + fn solve_direction(&mut self, direction: Direction, space: usize) -> Option> { + let mut grid = Vec::new(); + for boundary in self.grid_boundaries(direction) { + grid.push(self.spans_in_boundary(direction, boundary)); + } + + let constraints: Vec<_> = grid + .iter() + .flat_map(|s| constrain_spans(space, s)) + .collect(); + + // FIXME: This line needs to be restored before merging! + //self.solver.add_constraints(&constraints).ok()?; + self.solver.add_constraints(&constraints).unwrap(); + Some(grid.into_iter().flatten().collect()) + } + + fn grid_boundaries(&self, direction: Direction) -> Vec<(usize, usize)> { + // Select the spans running *perpendicular* to the direction of resize + let spans: Vec = self + .panes + .values() + .map(|p| self.get_span(!direction, p.as_ref())) + .collect(); + + let mut last_edge = 0; + let mut bounds = Vec::new(); + loop { + let mut spans_on_edge: Vec<&Span> = + spans.iter().filter(|p| p.pos == last_edge).collect(); + spans_on_edge.sort_unstable_by_key(|s| s.size); + if let Some(next) = spans_on_edge.first() { + let next_edge = last_edge + next.size; + bounds.push((last_edge, next_edge)); + last_edge = next_edge + GAP_SIZE; + } else { + break; + } + } + bounds + } + + fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec { + let (start, end) = boundary; + let bwn = |v| start <= v && v < end; + let mut spans: Vec<_> = self + .panes + .values() + .filter(|p| { + let s = self.get_span(!direction, p.as_ref()); + bwn(s.pos) || bwn(s.pos + s.size) + }) + .map(|p| self.get_span(direction, p.as_ref())) + .collect(); + spans.sort_unstable_by_key(|s| s.pos); + spans + } + + fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span { + let pas = pane.position_and_size(); + let (pos_var, size_var) = self.vars[&pane.pid()]; + match direction { + Direction::Horizontal => Span { + pid: pane.pid(), + direction, + fixed: pas.cols_fixed, + pos: pas.x, + size: pas.cols, + pos_var, + size_var, + }, + Direction::Vertical => Span { + pid: pane.pid(), + direction, + fixed: pas.rows_fixed, + pos: pas.y, + size: pas.rows, + pos_var, + size_var, + }, + } + } + + fn collapse_spans(&mut self, spans: &[Span]) { + for span in spans { + let solver = &self.solver; // Hand-holding the borrow-checker + let pane = self.panes.get_mut(&span.pid).unwrap(); + let fetch_usize = |v| solver.get_value(v).round() as usize; + match span.direction { + Direction::Horizontal => pane.change_pos_and_size(&PositionAndSize { + x: fetch_usize(span.pos_var), + cols: fetch_usize(span.size_var), + ..pane.position_and_size() + }), + Direction::Vertical => pane.change_pos_and_size(&PositionAndSize { + y: fetch_usize(span.pos_var), + rows: fetch_usize(span.size_var), + ..pane.position_and_size() + }), + } + if let PaneId::Terminal(pid) = pane.pid() { + self.os_api.set_terminal_size_using_fd( + pid, + pane.columns() as u16, + pane.rows() as u16, + ); + } + } + } +} + +fn constrain_spans(space: usize, spans: &[Span]) -> HashSet { + let mut constraints = HashSet::new(); + + // The first span needs to start at 0 + constraints.insert(spans[0].pos_var | EQ(REQUIRED) | 0.0); + + // Calculating "flexible" space (space not consumed by fixed-size spans) + let gap_space = GAP_SIZE * (spans.len() - 1); + let old_flex_space = spans + .iter() + .fold(0, |a, s| if !s.fixed { a + s.size } else { a }); + let new_flex_space = spans.iter().fold( + space - gap_space, + |a, s| if s.fixed { a - s.size } else { a }, + ); + + // Keep spans stuck together + for pair in spans.windows(2) { + let (ls, rs) = (pair[0], pair[1]); + constraints + .insert((ls.pos_var + ls.size_var + GAP_SIZE as f64) | EQ(REQUIRED) | rs.pos_var); + } + + // Try to maintain ratios and lock non-flexible sizes + for span in spans { + if span.fixed { + constraints.insert(span.size_var | EQ(REQUIRED) | span.size as f64); + } else { + let ratio = span.size as f64 / old_flex_space as f64; + constraints.insert((span.size_var / new_flex_space as f64) | EQ(STRONG) | ratio); + } + } + + // The last pane needs to end at the end of the space + let last = spans.last().unwrap(); + constraints.insert((last.pos_var + last.size_var) | EQ(REQUIRED) | space as f64); + + constraints +}