diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab/mod.rs similarity index 50% rename from zellij-server/src/tab.rs rename to zellij-server/src/tab/mod.rs index 9aceafd65..247f809c0 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab/mod.rs @@ -1,11 +1,14 @@ //! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size, //! as well as how they should be resized +mod pane_grid; +mod pane_resizer; + use zellij_utils::position::{Column, Line}; use zellij_utils::{position::Position, serde, zellij_tile}; use crate::ui::pane_boundaries_frame::FrameParams; -use crate::ui::pane_resizer::PaneResizer; +use pane_grid::{split, PaneGrid}; use crate::{ os_input_output::ServerOsApi, @@ -24,7 +27,6 @@ use std::rc::Rc; use std::sync::mpsc::channel; use std::time::Instant; use std::{ - cmp::Reverse, collections::{BTreeMap, HashMap, HashSet}, str, }; @@ -34,55 +36,15 @@ use zellij_utils::{ layout::{Direction, Layout, Run}, parse_keys, }, - pane_size::{Dimension, Offset, PaneGeom, Size, Viewport}, + pane_size::{Offset, PaneGeom, Size, Viewport}, }; -const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this - // FIXME: This should be replaced by `RESIZE_PERCENT` at some point const MIN_TERMINAL_HEIGHT: usize = 5; const MIN_TERMINAL_WIDTH: usize = 5; -const RESIZE_PERCENT: f64 = 5.0; - const MAX_PENDING_VTE_EVENTS: usize = 7000; -type BorderAndPaneIds = (usize, Vec); - -fn split(direction: Direction, rect: &PaneGeom) -> Option<(PaneGeom, PaneGeom)> { - let space = match direction { - Direction::Vertical => rect.cols, - Direction::Horizontal => rect.rows, - }; - if let Some(p) = space.as_percent() { - let first_rect = match direction { - Direction::Vertical => PaneGeom { - cols: Dimension::percent(p / 2.0), - ..*rect - }, - Direction::Horizontal => PaneGeom { - rows: Dimension::percent(p / 2.0), - ..*rect - }, - }; - let second_rect = match direction { - Direction::Vertical => PaneGeom { - x: first_rect.x + 1, - cols: first_rect.cols, - ..*rect - }, - Direction::Horizontal => PaneGeom { - y: first_rect.y + 1, - rows: first_rect.rows, - ..*rect - }, - }; - Some((first_rect, second_rect)) - } else { - None - } -} - fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (usize, usize) { // (columns_offset, rows_offset) // if the pane is not on the bottom or right edge on the screen, we need to reserve one space @@ -580,83 +542,43 @@ impl Tab { if self.fullscreen_is_active { self.unset_fullscreen(); } - // TODO: check minimum size of active terminal - - let (_largest_terminal_size, terminal_id_to_split) = self.get_panes().fold( - (0, None), - |(current_largest_terminal_size, current_terminal_id_to_split), - id_and_terminal_to_check| { - let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check; - let terminal_size = (terminal_to_check.rows() * CURSOR_HEIGHT_WIDTH_RATIO) - * terminal_to_check.cols(); - let terminal_can_be_split = terminal_to_check.cols() >= MIN_TERMINAL_WIDTH - && terminal_to_check.rows() >= MIN_TERMINAL_HEIGHT - && ((terminal_to_check.cols() > terminal_to_check.min_width() * 2) - || (terminal_to_check.rows() > terminal_to_check.min_height() * 2)); - if terminal_can_be_split && terminal_size > current_largest_terminal_size { - (terminal_size, Some(*id_of_terminal_to_check)) + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let terminal_id_and_split_direction = pane_grid.find_room_for_new_pane(); + if let Some((terminal_id_to_split, split_direction)) = terminal_id_and_split_direction { + let next_terminal_position = self.get_next_terminal_position(); + let terminal_to_split = self.panes.get_mut(&terminal_id_to_split).unwrap(); + let terminal_ws = terminal_to_split.position_and_size(); + if let PaneId::Terminal(term_pid) = pid { + if let Some((first_winsize, second_winsize)) = split(split_direction, &terminal_ws) + { + let new_terminal = TerminalPane::new( + term_pid, + second_winsize, + self.colors, + next_terminal_position, + String::new(), + ); + terminal_to_split.set_geom(first_winsize); + self.panes.insert(pid, Box::new(new_terminal)); + // ¯\_(ツ)_/¯ + let relayout_direction = match split_direction { + Direction::Vertical => Direction::Horizontal, + Direction::Horizontal => Direction::Vertical, + }; + self.relayout_tab(relayout_direction); + } + } + if let Some(client_id) = client_id { + if self.session_is_mirrored { + // move all clients + let connected_clients: Vec = + self.connected_clients.iter().copied().collect(); + for client_id in connected_clients { + self.active_panes.insert(client_id, pid); + } } else { - (current_largest_terminal_size, current_terminal_id_to_split) - } - }, - ); - if terminal_id_to_split.is_none() { - self.senders - .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty - .unwrap(); - return; // likely no terminal large enough to split - } - let terminal_id_to_split = terminal_id_to_split.unwrap(); - let next_terminal_position = self.get_next_terminal_position(); - let terminal_to_split = self.panes.get_mut(&terminal_id_to_split).unwrap(); - let terminal_ws = terminal_to_split.position_and_size(); - if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.cols() - && terminal_to_split.rows() > terminal_to_split.min_height() * 2 - { - if let PaneId::Terminal(term_pid) = pid { - if let Some((top_winsize, bottom_winsize)) = - split(Direction::Horizontal, &terminal_ws) - { - let new_terminal = TerminalPane::new( - term_pid, - bottom_winsize, - self.colors, - next_terminal_position, - String::new(), - ); - terminal_to_split.set_geom(top_winsize); - self.panes.insert(pid, Box::new(new_terminal)); - self.relayout_tab(Direction::Vertical); - } - } - } else if terminal_to_split.cols() > terminal_to_split.min_width() * 2 { - if let PaneId::Terminal(term_pid) = pid { - if let Some((left_winsize, right_winsize)) = - split(Direction::Vertical, &terminal_ws) - { - let new_terminal = TerminalPane::new( - term_pid, - right_winsize, - self.colors, - next_terminal_position, - String::new(), - ); - terminal_to_split.set_geom(left_winsize); - self.panes.insert(pid, Box::new(new_terminal)); - self.relayout_tab(Direction::Horizontal); - } - } - } - if let Some(client_id) = client_id { - if self.session_is_mirrored { - // move all clients - let connected_clients: Vec = - self.connected_clients.iter().copied().collect(); - for client_id in connected_clients { self.active_panes.insert(client_id, pid); } - } else { - self.active_panes.insert(client_id, pid); } } } @@ -1133,1147 +1055,11 @@ impl Tab { .find(|pid| self.panes.get(pid).unwrap().selectable()) .copied() } - fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let terminal_to_check = self.panes.get(id).unwrap(); - if terminal_to_check.x() == 0 { - return None; - } - for (&pid, terminal) in self.get_panes() { - if terminal.x() + terminal.cols() == terminal_to_check.x() { - ids.push(pid); - } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn pane_ids_directly_right_of(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let terminal_to_check = self.panes.get(id).unwrap(); - for (&pid, terminal) in self.get_panes() { - if terminal.x() == terminal_to_check.x() + terminal_to_check.cols() { - ids.push(pid); - } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn pane_ids_directly_below(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let terminal_to_check = self.panes.get(id).unwrap(); - for (&pid, terminal) in self.get_panes() { - if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() { - ids.push(pid); - } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn pane_ids_directly_above(&self, id: &PaneId) -> Option> { - let mut ids = vec![]; - let terminal_to_check = self.panes.get(id).unwrap(); - for (&pid, terminal) in self.get_panes() { - if terminal.y() + terminal.rows() == terminal_to_check.y() { - ids.push(pid); - } - } - if ids.is_empty() { - None - } else { - Some(ids) - } - } - fn panes_top_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { - self.panes - .keys() - .map(|t_id| self.panes.get(t_id).unwrap().as_ref()) - .filter(|terminal| terminal.pid() != pane.pid() && terminal.y() == pane.y()) - .collect() - } - fn panes_bottom_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { - self.panes - .keys() - .map(|t_id| self.panes.get(t_id).unwrap().as_ref()) - .filter(|terminal| { - terminal.pid() != pane.pid() - && terminal.y() + terminal.rows() == pane.y() + pane.rows() - }) - .collect() - } - fn panes_right_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { - self.panes - .keys() - .map(|t_id| self.panes.get(t_id).unwrap().as_ref()) - .filter(|terminal| { - terminal.pid() != pane.pid() - && terminal.x() + terminal.cols() == pane.x() + pane.cols() - }) - .collect() - } - fn panes_left_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> { - self.panes - .keys() - .map(|t_id| self.panes.get(t_id).unwrap().as_ref()) - .filter(|terminal| terminal.pid() != pane.pid() && terminal.x() == pane.x()) - .collect() - } - fn right_aligned_contiguous_panes_above( - &self, - id: &PaneId, - terminal_borders_to_the_right: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self - .panes - .get(id) - .expect("terminal id does not exist") - .as_ref(); - let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check); - // terminals that are next to each other up to current - right_aligned_terminals.sort_by_key(|a| Reverse(a.y())); - for terminal in right_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() + terminal.rows() == terminal_to_check.y() { - terminals.push(terminal); - } - } - // top-most border aligned with a pane border to the right - let mut top_resize_border = 0; - for terminal in &terminals { - let bottom_terminal_boundary = terminal.y() + terminal.rows(); - if terminal_borders_to_the_right - .get(&bottom_terminal_boundary) - .is_some() - && top_resize_border < bottom_terminal_boundary - { - top_resize_border = bottom_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.y() >= top_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let top_resize_border = if terminals.is_empty() { - terminal_to_check.y() - } else { - top_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (top_resize_border, terminal_ids) - } - fn right_aligned_contiguous_panes_below( - &self, - id: &PaneId, - terminal_borders_to_the_right: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self - .panes - .get(id) - .expect("terminal id does not exist") - .as_ref(); - let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check); - // terminals that are next to each other up to current - right_aligned_terminals.sort_by_key(|a| a.y()); - for terminal in right_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() { - terminals.push(terminal); - } - } - // bottom-most border aligned with a pane border to the right - let mut bottom_resize_border = self.viewport.y + self.viewport.rows; - for terminal in &terminals { - let top_terminal_boundary = terminal.y(); - if terminal_borders_to_the_right - .get(&(top_terminal_boundary)) - .is_some() - && top_terminal_boundary < bottom_resize_border - { - bottom_resize_border = top_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.y() + terminal.rows() <= bottom_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let bottom_resize_border = if terminals.is_empty() { - terminal_to_check.y() + terminal_to_check.rows() - } else { - bottom_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (bottom_resize_border, terminal_ids) - } - fn left_aligned_contiguous_panes_above( - &self, - id: &PaneId, - terminal_borders_to_the_left: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self - .panes - .get(id) - .expect("terminal id does not exist") - .as_ref(); - let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check); - // terminals that are next to each other up to current - left_aligned_terminals.sort_by_key(|a| Reverse(a.y())); - for terminal in left_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() + terminal.rows() == terminal_to_check.y() { - terminals.push(terminal); - } - } - // top-most border aligned with a pane border to the right - let mut top_resize_border = 0; - for terminal in &terminals { - let bottom_terminal_boundary = terminal.y() + terminal.rows(); - if terminal_borders_to_the_left - .get(&bottom_terminal_boundary) - .is_some() - && top_resize_border < bottom_terminal_boundary - { - top_resize_border = bottom_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.y() >= top_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let top_resize_border = if terminals.is_empty() { - terminal_to_check.y() - } else { - top_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (top_resize_border, terminal_ids) - } - fn left_aligned_contiguous_panes_below( - &self, - id: &PaneId, - terminal_borders_to_the_left: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self - .panes - .get(id) - .expect("terminal id does not exist") - .as_ref(); - let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check); - // terminals that are next to each other up to current - left_aligned_terminals.sort_by_key(|a| a.y()); - for terminal in left_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() { - terminals.push(terminal); - } - } - // bottom-most border aligned with a pane border to the left - let mut bottom_resize_border = self.viewport.y + self.viewport.rows; - for terminal in &terminals { - let top_terminal_boundary = terminal.y(); - if terminal_borders_to_the_left - .get(&(top_terminal_boundary)) - .is_some() - && top_terminal_boundary < bottom_resize_border - { - bottom_resize_border = top_terminal_boundary; - } - } - terminals.retain(|terminal| { - // terminal.y() + terminal.rows() < bottom_resize_border - terminal.y() + terminal.rows() <= bottom_resize_border - }); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let bottom_resize_border = if terminals.is_empty() { - terminal_to_check.y() + terminal_to_check.rows() - } else { - bottom_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (bottom_resize_border, terminal_ids) - } - fn top_aligned_contiguous_panes_to_the_left( - &self, - id: &PaneId, - terminal_borders_above: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self - .panes - .get(id) - .expect("terminal id does not exist") - .as_ref(); - let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check); - // terminals that are next to each other up to current - top_aligned_terminals.sort_by_key(|a| Reverse(a.x())); - for terminal in top_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() + terminal.cols() == terminal_to_check.x() { - terminals.push(terminal); - } - } - // leftmost border aligned with a pane border above - let mut left_resize_border = 0; - for terminal in &terminals { - let right_terminal_boundary = terminal.x() + terminal.cols(); - if terminal_borders_above - .get(&right_terminal_boundary) - .is_some() - && left_resize_border < right_terminal_boundary - { - left_resize_border = right_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.x() >= left_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let left_resize_border = if terminals.is_empty() { - terminal_to_check.x() - } else { - left_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (left_resize_border, terminal_ids) - } - fn top_aligned_contiguous_panes_to_the_right( - &self, - id: &PaneId, - terminal_borders_above: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self.panes.get(id).unwrap().as_ref(); - let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check); - // terminals that are next to each other up to current - top_aligned_terminals.sort_by_key(|a| a.x()); - for terminal in top_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() == terminal_to_check.x() + terminal_to_check.cols() { - terminals.push(terminal); - } - } - // rightmost border aligned with a pane border above - let mut right_resize_border = self.viewport.x + self.viewport.cols; - for terminal in &terminals { - let left_terminal_boundary = terminal.x(); - if terminal_borders_above - .get(&left_terminal_boundary) - .is_some() - && right_resize_border > left_terminal_boundary - { - right_resize_border = left_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.x() + terminal.cols() <= right_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let right_resize_border = if terminals.is_empty() { - terminal_to_check.x() + terminal_to_check.cols() - } else { - right_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (right_resize_border, terminal_ids) - } - fn bottom_aligned_contiguous_panes_to_the_left( - &self, - id: &PaneId, - terminal_borders_below: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self.panes.get(id).unwrap().as_ref(); - let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check); - bottom_aligned_terminals.sort_by_key(|a| Reverse(a.x())); - // terminals that are next to each other up to current - for terminal in bottom_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() + terminal.cols() == terminal_to_check.x() { - terminals.push(terminal); - } - } - // leftmost border aligned with a pane border above - let mut left_resize_border = 0; - for terminal in &terminals { - let right_terminal_boundary = terminal.x() + terminal.cols(); - if terminal_borders_below - .get(&right_terminal_boundary) - .is_some() - && left_resize_border < right_terminal_boundary - { - left_resize_border = right_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.x() >= left_resize_border); - // if there are no adjacent panes to resize, we use the border of the main pane we're - // resizing - let left_resize_border = if terminals.is_empty() { - terminal_to_check.x() - } else { - left_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (left_resize_border, terminal_ids) - } - fn bottom_aligned_contiguous_panes_to_the_right( - &self, - id: &PaneId, - terminal_borders_below: &HashSet, - ) -> BorderAndPaneIds { - let mut terminals = vec![]; - let terminal_to_check = self.panes.get(id).unwrap().as_ref(); - let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check); - bottom_aligned_terminals.sort_by_key(|a| a.x()); - // terminals that are next to each other up to current - for terminal in bottom_aligned_terminals { - let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check); - if terminal.x() == terminal_to_check.x() + terminal_to_check.cols() { - terminals.push(terminal); - } - } - // leftmost border aligned with a pane border above - let mut right_resize_border = self.viewport.x + self.viewport.cols; - for terminal in &terminals { - let left_terminal_boundary = terminal.x(); - if terminal_borders_below - .get(&left_terminal_boundary) - .is_some() - && right_resize_border > left_terminal_boundary - { - right_resize_border = left_terminal_boundary; - } - } - terminals.retain(|terminal| terminal.x() + terminal.cols() <= right_resize_border); - let right_resize_border = if terminals.is_empty() { - terminal_to_check.x() + terminal_to_check.cols() - } else { - right_resize_border - }; - let terminal_ids: Vec = terminals.iter().map(|t| t.pid()).collect(); - (right_resize_border, terminal_ids) - } - fn reduce_pane_height(&mut self, id: &PaneId, percent: f64) { - let terminal = self.panes.get_mut(id).unwrap(); - terminal.reduce_height(percent); - } - fn increase_pane_height(&mut self, id: &PaneId, percent: f64) { - let terminal = self.panes.get_mut(id).unwrap(); - terminal.increase_height(percent); - } - fn increase_pane_width(&mut self, id: &PaneId, percent: f64) { - let terminal = self.panes.get_mut(id).unwrap(); - terminal.increase_width(percent); - } - fn reduce_pane_width(&mut self, id: &PaneId, percent: f64) { - let terminal = self.panes.get_mut(id).unwrap(); - terminal.reduce_width(percent); - } - fn pane_is_between_vertical_borders( - &self, - id: &PaneId, - left_border_x: usize, - right_border_x: usize, - ) -> bool { - let terminal = self - .panes - .get(id) - .expect("could not find terminal to check between borders"); - terminal.x() >= left_border_x && terminal.x() + terminal.cols() <= right_border_x - } - fn pane_is_between_horizontal_borders( - &self, - id: &PaneId, - top_border_y: usize, - bottom_border_y: usize, - ) -> bool { - let terminal = self - .panes - .get(id) - .expect("could not find terminal to check between borders"); - terminal.y() >= top_border_y && terminal.y() + terminal.rows() <= bottom_border_y - } - fn reduce_pane_and_surroundings_up(&mut self, id: &PaneId, percent: f64) { - let mut terminals_below = self - .pane_ids_directly_below(id) - .expect("can't reduce pane size up if there are no terminals below"); - let terminal_borders_below: HashSet = terminals_below - .iter() - .map(|t| self.panes.get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.bottom_aligned_contiguous_panes_to_the_left(id, &terminal_borders_below); - let (right_resize_border, terminals_to_the_right) = - self.bottom_aligned_contiguous_panes_to_the_right(id, &terminal_borders_below); - terminals_below.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - let pane = self.panes.get(terminal_id).unwrap(); - if pane.current_geom().rows.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_height(id, percent); - for terminal_id in terminals_below { - self.increase_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.reduce_pane_height(terminal_id, percent); - } - } - fn reduce_pane_and_surroundings_down(&mut self, id: &PaneId, percent: f64) { - let mut terminals_above = self - .pane_ids_directly_above(id) - .expect("can't reduce pane size down if there are no terminals above"); - let terminal_borders_above: HashSet = terminals_above - .iter() - .map(|t| self.panes.get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.top_aligned_contiguous_panes_to_the_left(id, &terminal_borders_above); - let (right_resize_border, terminals_to_the_right) = - self.top_aligned_contiguous_panes_to_the_right(id, &terminal_borders_above); - terminals_above.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - let pane = self.panes.get(terminal_id).unwrap(); - if pane.current_geom().rows.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_height(id, percent); - for terminal_id in terminals_above { - self.increase_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.reduce_pane_height(terminal_id, percent); - } - } - fn reduce_pane_and_surroundings_right(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_left = self - .pane_ids_directly_left_of(id) - .expect("can't reduce pane size right if there are no terminals to the left"); - let terminal_borders_to_the_left: HashSet = terminals_to_the_left - .iter() - .map(|t| self.panes.get(t).unwrap().y()) - .collect(); - let (top_resize_border, terminals_above) = - self.left_aligned_contiguous_panes_above(id, &terminal_borders_to_the_left); - let (bottom_resize_border, terminals_below) = - self.left_aligned_contiguous_panes_below(id, &terminal_borders_to_the_left); - terminals_to_the_left.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_above.iter().chain(&terminals_below) { - let pane = self.panes.get(terminal_id).unwrap(); - if pane.current_geom().cols.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_width(id, percent); - for terminal_id in terminals_to_the_left { - self.increase_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.reduce_pane_width(terminal_id, percent); - } - } - fn reduce_pane_and_surroundings_left(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_right = self - .pane_ids_directly_right_of(id) - .expect("can't reduce pane size left if there are no terminals to the right"); - let terminal_borders_to_the_right: HashSet = terminals_to_the_right - .iter() - .map(|t| self.panes.get(t).unwrap().y()) - .collect(); - let (top_resize_border, terminals_above) = - self.right_aligned_contiguous_panes_above(id, &terminal_borders_to_the_right); - let (bottom_resize_border, terminals_below) = - self.right_aligned_contiguous_panes_below(id, &terminal_borders_to_the_right); - terminals_to_the_right.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - - // FIXME: This checks that we aren't violating the resize constraints of the aligned panes - // above and below this one. This should be moved to a `can_resize` function eventually. - for terminal_id in terminals_above.iter().chain(&terminals_below) { - let pane = self.panes.get(terminal_id).unwrap(); - if pane.current_geom().cols.as_percent().unwrap() - percent < RESIZE_PERCENT { - return; - } - } - - self.reduce_pane_width(id, percent); - for terminal_id in terminals_to_the_right { - self.increase_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.reduce_pane_width(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_up(&mut self, id: &PaneId, percent: f64) { - let mut terminals_above = self - .pane_ids_directly_above(id) - .expect("can't increase pane size up if there are no terminals above"); - let terminal_borders_above: HashSet = terminals_above - .iter() - .map(|t| self.panes.get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.top_aligned_contiguous_panes_to_the_left(id, &terminal_borders_above); - let (right_resize_border, terminals_to_the_right) = - self.top_aligned_contiguous_panes_to_the_right(id, &terminal_borders_above); - terminals_above.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - self.increase_pane_height(id, percent); - for terminal_id in terminals_above { - self.reduce_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.increase_pane_height(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_down(&mut self, id: &PaneId, percent: f64) { - let mut terminals_below = self - .pane_ids_directly_below(id) - .expect("can't increase pane size down if there are no terminals below"); - let terminal_borders_below: HashSet = terminals_below - .iter() - .map(|t| self.panes.get(t).unwrap().x()) - .collect(); - let (left_resize_border, terminals_to_the_left) = - self.bottom_aligned_contiguous_panes_to_the_left(id, &terminal_borders_below); - let (right_resize_border, terminals_to_the_right) = - self.bottom_aligned_contiguous_panes_to_the_right(id, &terminal_borders_below); - terminals_below.retain(|t| { - self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) - }); - self.increase_pane_height(id, percent); - for terminal_id in terminals_below { - self.reduce_pane_height(&terminal_id, percent); - } - for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { - self.increase_pane_height(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_right(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_right = self - .pane_ids_directly_right_of(id) - .expect("can't increase pane size right if there are no terminals to the right"); - let terminal_borders_to_the_right: HashSet = terminals_to_the_right - .iter() - .map(|t| { - return self.panes.get(t).unwrap().y(); - }) - .collect(); - let (top_resize_border, terminals_above) = - self.right_aligned_contiguous_panes_above(id, &terminal_borders_to_the_right); - let (bottom_resize_border, terminals_below) = - self.right_aligned_contiguous_panes_below(id, &terminal_borders_to_the_right); - terminals_to_the_right.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - self.increase_pane_width(id, percent); - for terminal_id in terminals_to_the_right { - self.reduce_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.increase_pane_width(terminal_id, percent); - } - } - fn increase_pane_and_surroundings_left(&mut self, id: &PaneId, percent: f64) { - let mut terminals_to_the_left = self - .pane_ids_directly_left_of(id) - .expect("can't increase pane size right if there are no terminals to the right"); - let terminal_borders_to_the_left: HashSet = terminals_to_the_left - .iter() - .map(|t| self.panes.get(t).unwrap().y()) - .collect(); - let (top_resize_border, terminals_above) = - self.left_aligned_contiguous_panes_above(id, &terminal_borders_to_the_left); - let (bottom_resize_border, terminals_below) = - self.left_aligned_contiguous_panes_below(id, &terminal_borders_to_the_left); - terminals_to_the_left.retain(|t| { - self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) - }); - self.increase_pane_width(id, percent); - for terminal_id in terminals_to_the_left { - self.reduce_pane_width(&terminal_id, percent); - } - for terminal_id in terminals_above.iter().chain(&terminals_below) { - self.increase_pane_width(terminal_id, percent); - } - } - // FIXME: The if-let nesting and explicit `false`s are... suboptimal. - // FIXME: Quite a lot of duplication between these functions... - fn can_increase_pane_and_surroundings_right(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(pane_id) { - panes_to_the_right.iter().all(|id| { - let p = self.panes.get(id).unwrap(); - if let Some(cols) = p.position_and_size().cols.as_percent() { - let current_fixed_cols = p.position_and_size().cols.as_usize(); - let will_reduce_by = - ((self.display_area.cols as f64 / 100.0) * increase_by) as usize; - cols - increase_by >= RESIZE_PERCENT - && current_fixed_cols.saturating_sub(will_reduce_by) >= p.min_width() - } else { - false - } - }) - } else { - false - } - } - fn can_increase_pane_and_surroundings_left(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(pane_id) { - panes_to_the_left.iter().all(|id| { - let p = self.panes.get(id).unwrap(); - if let Some(cols) = p.position_and_size().cols.as_percent() { - let current_fixed_cols = p.position_and_size().cols.as_usize(); - let will_reduce_by = - ((self.display_area.cols as f64 / 100.0) * increase_by) as usize; - cols - increase_by >= RESIZE_PERCENT - && current_fixed_cols.saturating_sub(will_reduce_by) >= p.min_width() - } else { - false - } - }) - } else { - false - } - } - fn can_increase_pane_and_surroundings_down(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_below) = self.pane_ids_directly_below(pane_id) { - panes_below.iter().all(|id| { - let p = self.panes.get(id).unwrap(); - if let Some(rows) = p.position_and_size().rows.as_percent() { - let current_fixed_rows = p.position_and_size().rows.as_usize(); - let will_reduce_by = - ((self.display_area.rows as f64 / 100.0) * increase_by) as usize; - rows - increase_by >= RESIZE_PERCENT - && current_fixed_rows.saturating_sub(will_reduce_by) >= p.min_height() - } else { - false - } - }) - } else { - false - } - } - - fn can_increase_pane_and_surroundings_up(&self, pane_id: &PaneId, increase_by: f64) -> bool { - if let Some(panes_above) = self.pane_ids_directly_above(pane_id) { - panes_above.iter().all(|id| { - let p = self.panes.get(id).unwrap(); - if let Some(rows) = p.position_and_size().rows.as_percent() { - let current_fixed_rows = p.position_and_size().rows.as_usize(); - let will_reduce_by = - ((self.display_area.rows as f64 / 100.0) * increase_by) as usize; - rows - increase_by >= RESIZE_PERCENT - && current_fixed_rows.saturating_sub(will_reduce_by) >= p.min_height() - } else { - false - } - }) - } else { - false - } - } - fn can_reduce_pane_and_surroundings_right(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let pane = self.panes.get(pane_id).unwrap(); - if let Some(cols) = pane.position_and_size().cols.as_percent() { - let current_fixed_cols = pane.position_and_size().cols.as_usize(); - let will_reduce_by = ((self.display_area.cols as f64 / 100.0) * reduce_by) as usize; - let ids_left = self.pane_ids_directly_left_of(pane_id); - let flexible_left = self.ids_are_flexible(Direction::Horizontal, ids_left); - cols - reduce_by >= RESIZE_PERCENT - && flexible_left - && current_fixed_cols.saturating_sub(will_reduce_by) >= pane.min_width() - } else { - false - } - } - fn can_reduce_pane_and_surroundings_left(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let pane = self.panes.get(pane_id).unwrap(); - if let Some(cols) = pane.position_and_size().cols.as_percent() { - let current_fixed_cols = pane.position_and_size().cols.as_usize(); - let will_reduce_by = ((self.display_area.cols as f64 / 100.0) * reduce_by) as usize; - let ids_right = self.pane_ids_directly_right_of(pane_id); - let flexible_right = self.ids_are_flexible(Direction::Horizontal, ids_right); - cols - reduce_by >= RESIZE_PERCENT - && flexible_right - && current_fixed_cols.saturating_sub(will_reduce_by) >= pane.min_width() - } else { - false - } - } - fn can_reduce_pane_and_surroundings_down(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let pane = self.panes.get(pane_id).unwrap(); - if let Some(rows) = pane.position_and_size().rows.as_percent() { - let current_fixed_rows = pane.position_and_size().rows.as_usize(); - let will_reduce_by = ((self.display_area.rows as f64 / 100.0) * reduce_by) as usize; - let ids_above = self.pane_ids_directly_above(pane_id); - let flexible_above = self.ids_are_flexible(Direction::Vertical, ids_above); - rows - reduce_by >= RESIZE_PERCENT - && flexible_above - && current_fixed_rows.saturating_sub(will_reduce_by) >= pane.min_height() - } else { - false - } - } - fn can_reduce_pane_and_surroundings_up(&self, pane_id: &PaneId, reduce_by: f64) -> bool { - let pane = self.panes.get(pane_id).unwrap(); - if let Some(rows) = pane.position_and_size().rows.as_percent() { - let current_fixed_rows = pane.position_and_size().rows.as_usize(); - let will_reduce_by = ((self.display_area.rows as f64 / 100.0) * reduce_by) as usize; - let ids_below = self.pane_ids_directly_below(pane_id); - let flexible_below = self.ids_are_flexible(Direction::Vertical, ids_below); - rows - reduce_by >= RESIZE_PERCENT - && flexible_below - && current_fixed_rows.saturating_sub(will_reduce_by) >= pane.min_height() - } else { - false - } - } - fn try_increase_pane_and_surroundings_right( - &mut self, - pane_id: &PaneId, - reduce_by: f64, - ) -> bool { - if self.can_increase_pane_and_surroundings_right(pane_id, reduce_by) { - self.increase_pane_and_surroundings_right(pane_id, reduce_by); - self.relayout_tab(Direction::Horizontal); - return true; - } - false - } - fn try_increase_pane_and_surroundings_left( - &mut self, - pane_id: &PaneId, - reduce_by: f64, - ) -> bool { - if self.can_increase_pane_and_surroundings_left(pane_id, reduce_by) { - self.increase_pane_and_surroundings_left(pane_id, reduce_by); - self.relayout_tab(Direction::Horizontal); - return true; - } - false - } - fn try_increase_pane_and_surroundings_up(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_increase_pane_and_surroundings_up(pane_id, reduce_by) { - self.increase_pane_and_surroundings_up(pane_id, reduce_by); - self.relayout_tab(Direction::Vertical); - return true; - } - false - } - fn try_increase_pane_and_surroundings_down( - &mut self, - pane_id: &PaneId, - reduce_by: f64, - ) -> bool { - if self.can_increase_pane_and_surroundings_down(pane_id, reduce_by) { - self.increase_pane_and_surroundings_down(pane_id, reduce_by); - self.relayout_tab(Direction::Vertical); - return true; - } - false - } - fn try_increase_pane_and_surroundings_right_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_right = - self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_increase_pane_up = - self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_increase_pane_right && can_increase_pane_up { - let pane_above_with_right_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_right_aligned_border) = pane_above_with_right_aligned_border - { - self.try_reduce_pane_and_surroundings_right( - &pane_above_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_increase_pane_and_surroundings_left_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_left = - self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_increase_pane_up = - self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_increase_pane_left && can_increase_pane_up { - let pane_above_with_left_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_left_aligned_border) = pane_above_with_left_aligned_border { - self.try_reduce_pane_and_surroundings_left( - &pane_above_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_increase_pane_and_surroundings_right_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_right = - self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_increase_pane_down = - self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_increase_pane_right && can_increase_pane_down { - let pane_below_with_right_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_right_aligned_border) = pane_below_with_right_aligned_border - { - self.try_reduce_pane_and_surroundings_right( - &pane_below_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_increase_pane_and_surroundings_left_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_increase_pane_left = - self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_increase_pane_down = - self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_increase_pane_left && can_increase_pane_down { - let pane_below_with_left_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_left_aligned_border) = pane_below_with_left_aligned_border { - self.try_reduce_pane_and_surroundings_left( - &pane_below_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_right_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_right = - self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_reduce_pane_up = self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_reduce_pane_right && can_reduce_pane_up { - let pane_below_with_left_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_left_aligned_border) = pane_below_with_left_aligned_border { - self.try_increase_pane_and_surroundings_right( - &pane_below_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_left_and_up(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_left = - self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_reduce_pane_up = self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if can_reduce_pane_left && can_reduce_pane_up { - let pane_below_with_right_aligned_border = self - .viewport_pane_ids_directly_below(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); - if let Some(pane_below_with_right_aligned_border) = pane_below_with_right_aligned_border - { - self.try_increase_pane_and_surroundings_left( - &pane_below_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_right_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_right = - self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - let can_reduce_pane_down = - self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_reduce_pane_right && can_reduce_pane_down { - let pane_above_with_left_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() == pane.x() + pane.cols() - }); - self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_left_aligned_border) = pane_above_with_left_aligned_border { - self.try_increase_pane_and_surroundings_right( - &pane_above_with_left_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_left_and_down(&mut self, pane_id: &PaneId) -> bool { - let can_reduce_pane_left = - self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - let can_reduce_pane_down = - self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if can_reduce_pane_left && can_reduce_pane_down { - let pane_above_with_right_aligned_border = self - .viewport_pane_ids_directly_above(pane_id) - .iter() - .copied() - .find(|p_id| { - let pane = self.panes.get(p_id).unwrap(); - let active_pane = self.panes.get(pane_id).unwrap(); - active_pane.x() + active_pane.cols() == pane.x() - }); - self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); - self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); - if let Some(pane_above_with_right_aligned_border) = pane_above_with_right_aligned_border - { - self.try_increase_pane_and_surroundings_left( - &pane_above_with_right_aligned_border, - RESIZE_PERCENT, - ); - } - true - } else { - false - } - } - fn try_reduce_pane_and_surroundings_right(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_right(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_right(pane_id, reduce_by); - self.relayout_tab(Direction::Horizontal); - return true; - } - false - } - fn try_reduce_pane_and_surroundings_left(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_left(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_left(pane_id, reduce_by); - self.relayout_tab(Direction::Horizontal); - return true; - } - false - } - fn try_reduce_pane_and_surroundings_up(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_up(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_up(pane_id, reduce_by); - self.relayout_tab(Direction::Vertical); - return true; - } - false - } - fn try_reduce_pane_and_surroundings_down(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { - if self.can_reduce_pane_and_surroundings_down(pane_id, reduce_by) { - self.reduce_pane_and_surroundings_down(pane_id, reduce_by); - self.relayout_tab(Direction::Vertical); - return true; - } - false - } - fn ids_are_flexible(&self, direction: Direction, pane_ids: Option>) -> bool { - pane_ids.is_some() - && pane_ids.unwrap().iter().all(|id| { - let geom = self.panes[id].current_geom(); - let dimension = match direction { - Direction::Vertical => geom.rows, - Direction::Horizontal => geom.cols, - }; - !dimension.is_fixed() - }) - } pub fn relayout_tab(&mut self, direction: Direction) { - let mut resizer = PaneResizer::new(&mut self.panes); + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); let result = match direction { - Direction::Horizontal => resizer.layout(direction, self.display_area.cols), - Direction::Vertical => resizer.layout(direction, self.display_area.rows), + Direction::Horizontal => pane_grid.layout(direction, self.display_area.cols), + Direction::Vertical => pane_grid.layout(direction, self.display_area.rows), }; if let Err(e) = &result { log::error!("{:?} relayout of the tab failed: {}", direction, e); @@ -2286,8 +1072,8 @@ impl Tab { .iter_mut() .filter(|(pid, _)| !self.panes_to_hide.contains(pid)); let Size { rows, cols } = new_screen_size; - let mut resizer = PaneResizer::new(panes); - if resizer.layout(Direction::Horizontal, cols).is_ok() { + let mut pane_grid = PaneGrid::new(panes, self.display_area, self.viewport); + if pane_grid.layout(Direction::Horizontal, cols).is_ok() { let column_difference = cols as isize - self.display_area.cols as isize; // FIXME: Should the viewport be an Offset? self.viewport.cols = (self.viewport.cols as isize + column_difference) as usize; @@ -2295,7 +1081,7 @@ impl Tab { } else { log::error!("Failed to horizontally resize the tab!!!"); } - if resizer.layout(Direction::Vertical, rows).is_ok() { + if pane_grid.layout(Direction::Vertical, rows).is_ok() { let row_difference = rows as isize - self.display_area.rows as isize; self.viewport.rows = (self.viewport.rows as isize + row_difference) as usize; self.display_area.rows = rows; @@ -2306,100 +1092,60 @@ impl Tab { self.set_pane_frames(self.draw_pane_frames); } pub fn resize_left(&mut self, client_id: ClientId) { - // TODO: find out by how much we actually reduced and only reduce by that much + println!("tab resize_left active_panes: {:?}", self.active_panes); if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - if self.can_increase_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT) { - self.increase_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT); - } else if self.can_reduce_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT) { - self.reduce_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT); + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + pane_grid.resize_pane_left(&active_pane_id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } + // TODO: can we live without the set_pane_frames we dropped here through layout_tab and in the other + // resize methods? } - self.relayout_tab(Direction::Horizontal); } pub fn resize_right(&mut self, client_id: ClientId) { - // TODO: find out by how much we actually reduced and only reduce by that much if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - if self.can_increase_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT) { - self.increase_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT); - } else if self.can_reduce_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT) { - self.reduce_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT); + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + pane_grid.resize_pane_right(&active_pane_id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } } - self.relayout_tab(Direction::Horizontal); } pub fn resize_down(&mut self, client_id: ClientId) { - // TODO: find out by how much we actually reduced and only reduce by that much if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - if self.can_increase_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT) { - self.increase_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT); - } else if self.can_reduce_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT) { - self.reduce_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT); + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + pane_grid.resize_pane_down(&active_pane_id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } } - self.relayout_tab(Direction::Vertical); } pub fn resize_up(&mut self, client_id: ClientId) { - // TODO: find out by how much we actually reduced and only reduce by that much if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - if self.can_increase_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT) { - self.increase_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT); - } else if self.can_reduce_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT) { - self.reduce_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT); + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + pane_grid.resize_pane_up(&active_pane_id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } } - self.relayout_tab(Direction::Vertical); } pub fn resize_increase(&mut self, client_id: ClientId) { if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - if self.try_increase_pane_and_surroundings_right_and_down(&active_pane_id) { - return; + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + pane_grid.resize_increase(&active_pane_id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } - if self.try_increase_pane_and_surroundings_left_and_down(&active_pane_id) { - return; - } - if self.try_increase_pane_and_surroundings_right_and_up(&active_pane_id) { - return; - } - if self.try_increase_pane_and_surroundings_left_and_up(&active_pane_id) { - return; - } - - if self.try_increase_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT) { - return; - } - if self.try_increase_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT) { - return; - } - if self.try_increase_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT) { - return; - } - self.try_increase_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT); } } pub fn resize_decrease(&mut self, client_id: ClientId) { if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - if self.try_reduce_pane_and_surroundings_left_and_up(&active_pane_id) { - return; + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + pane_grid.resize_decrease(&active_pane_id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } - if self.try_reduce_pane_and_surroundings_right_and_up(&active_pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_right_and_down(&active_pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_left_and_down(&active_pane_id) { - return; - } - if self.try_reduce_pane_and_surroundings_left(&active_pane_id, RESIZE_PERCENT) { - return; - } - if self.try_reduce_pane_and_surroundings_right(&active_pane_id, RESIZE_PERCENT) { - return; - } - if self.try_reduce_pane_and_surroundings_up(&active_pane_id, RESIZE_PERCENT) { - return; - } - self.try_reduce_pane_and_surroundings_down(&active_pane_id, RESIZE_PERCENT); } } @@ -2411,17 +1157,8 @@ impl Tab { return; } let current_active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let pane_ids: Vec = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations - let active_pane_id_position = pane_ids - .iter() - .position(|id| id == ¤t_active_pane_id) - .unwrap(); - let next_active_pane_id = pane_ids - .get(active_pane_id_position + 1) - .or_else(|| pane_ids.get(0)) - .copied() - .unwrap(); - + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_active_pane_id = pane_grid.next_selectable_pane_id(¤t_active_pane_id); let connected_clients: Vec = self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, next_active_pane_id); @@ -2435,25 +1172,8 @@ impl Tab { return; } let active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); - panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { - if a_pane.y() == b_pane.y() { - a_pane.x().cmp(&b_pane.x()) - } else { - a_pane.y().cmp(&b_pane.y()) - } - }); - let active_pane_position = panes - .iter() - .position(|(id, _)| *id == &active_pane_id) // TODO: better - .unwrap(); - - let next_active_pane_id = panes - .get(active_pane_position + 1) - .or_else(|| panes.get(0)) - .map(|p| *p.0) - .unwrap(); - + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_active_pane_id = pane_grid.next_selectable_pane_id(&active_pane_id); let connected_clients: Vec = self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, next_active_pane_id); @@ -2467,25 +1187,8 @@ impl Tab { return; } let active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); - panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { - if a_pane.y() == b_pane.y() { - a_pane.x().cmp(&b_pane.x()) - } else { - a_pane.y().cmp(&b_pane.y()) - } - }); - let last_pane = panes.last().unwrap(); - let active_pane_position = panes - .iter() - .position(|(id, _)| *id == &active_pane_id) // TODO: better - .unwrap(); - - let next_active_pane_id = if active_pane_position == 0 { - *last_pane.0 - } else { - *panes.get(active_pane_position - 1).unwrap().0 - }; + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_active_pane_id = pane_grid.previous_selectable_pane_id(&active_pane_id); let connected_clients: Vec = self.connected_clients.iter().copied().collect(); for client_id in connected_clients { self.active_panes.insert(client_id, next_active_pane_id); @@ -2499,18 +1202,12 @@ impl Tab { if self.fullscreen_is_active { return false; } - let active_pane = self.get_active_pane(client_id); - let updated_active_pane = if let Some(active) = active_pane { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_left_of(active) && c.horizontally_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); + let active_pane_id = self.get_active_pane_id(client_id); + let updated_active_pane = if let Some(active_pane_id) = active_pane_id { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id); match next_index { - Some(&p) => { + Some(p) => { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = self @@ -2542,10 +1239,10 @@ impl Tab { return true; } - None => Some(active.pid()), + None => Some(active_pane_id), } } else { - Some(active_pane.unwrap().pid()) + active_pane_id }; match updated_active_pane { Some(updated_active_pane) => { @@ -2570,18 +1267,12 @@ impl Tab { if self.fullscreen_is_active { return; } - let active_pane = self.get_active_pane(client_id); - let updated_active_pane = if let Some(active) = active_pane { - let panes = self.get_selectable_panes(); - let next_index = panes - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_below(active) && c.vertically_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); + let active_pane_id = self.get_active_pane_id(client_id); + let updated_active_pane = if let Some(active_pane_id) = active_pane_id { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_below(&active_pane_id); match next_index { - Some(&p) => { + Some(p) => { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = self @@ -2600,10 +1291,10 @@ impl Tab { Some(p) } - None => Some(active.pid()), + None => Some(active_pane_id), } } else { - Some(active_pane.unwrap().pid()) + active_pane_id }; match updated_active_pane { Some(updated_active_pane) => { @@ -2631,18 +1322,12 @@ impl Tab { if self.fullscreen_is_active { return; } - let active_pane = self.get_active_pane(client_id); - let updated_active_pane = if let Some(active) = active_pane { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_above(active) && c.vertically_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); + let active_pane_id = self.get_active_pane_id(client_id); + let updated_active_pane = if let Some(active_pane_id) = active_pane_id { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_above(&active_pane_id); match next_index { - Some(&p) => { + Some(p) => { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = self @@ -2661,10 +1346,10 @@ impl Tab { Some(p) } - None => Some(active.pid()), + None => Some(active_pane_id), } } else { - Some(active_pane.unwrap().pid()) + active_pane_id }; match updated_active_pane { Some(updated_active_pane) => { @@ -2693,18 +1378,12 @@ impl Tab { if self.fullscreen_is_active { return false; } - let active_pane = self.get_active_pane(client_id); - let updated_active_pane = if let Some(active) = active_pane { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_right_of(active) && c.horizontally_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); + let active_pane_id = self.get_active_pane_id(client_id); + let updated_active_pane = if let Some(active_pane_id) = active_pane_id { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id); match next_index { - Some(&p) => { + Some(p) => { // render previously active pane so that its frame does not remain actively // colored let previously_active_pane = self @@ -2733,10 +1412,10 @@ impl Tab { } return true; } - None => Some(active.pid()), + None => Some(active_pane_id), } } else { - Some(active_pane.unwrap().pid()) + active_pane_id }; match updated_active_pane { Some(updated_active_pane) => { @@ -2766,47 +1445,29 @@ impl Tab { return; } let active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let mut panes: Vec<(&PaneId, &Box)> = self.get_selectable_panes().collect(); - panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { - if a_pane.y() == b_pane.y() { - a_pane.x().cmp(&b_pane.x()) - } else { - a_pane.y().cmp(&b_pane.y()) - } - }); - let active_pane_position = panes - .iter() - .position(|(id, _)| *id == &active_pane_id) // TODO: better - .unwrap(); + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let new_position_id = pane_grid.next_selectable_pane_id(&active_pane_id); + let current_position = self.panes.get(&active_pane_id).unwrap(); + let prev_geom = current_position.position_and_size(); + let prev_geom_override = current_position.geom_override(); - let new_position_id = panes - .get(active_pane_position + 1) - .or_else(|| panes.get(0)) - .map(|p| *p.0); - - if let Some(p) = new_position_id { - let current_position = self.panes.get(&active_pane_id).unwrap(); - let prev_geom = current_position.position_and_size(); - let prev_geom_override = current_position.geom_override(); - - let new_position = self.panes.get_mut(&p).unwrap(); - let next_geom = new_position.position_and_size(); - let next_geom_override = new_position.geom_override(); - new_position.set_geom(prev_geom); - if let Some(geom) = prev_geom_override { - new_position.get_geom_override(geom); - } - resize_pty!(new_position, self.os_api); - new_position.set_should_render(true); - - let current_position = self.panes.get_mut(&active_pane_id).unwrap(); - current_position.set_geom(next_geom); - if let Some(geom) = next_geom_override { - current_position.get_geom_override(geom); - } - resize_pty!(current_position, self.os_api); - current_position.set_should_render(true); + let new_position = self.panes.get_mut(&new_position_id).unwrap(); + let next_geom = new_position.position_and_size(); + let next_geom_override = new_position.geom_override(); + new_position.set_geom(prev_geom); + if let Some(geom) = prev_geom_override { + new_position.get_geom_override(geom); } + resize_pty!(new_position, self.os_api); + new_position.set_should_render(true); + + let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + current_position.set_geom(next_geom); + if let Some(geom) = next_geom_override { + current_position.get_geom_override(geom); + } + resize_pty!(current_position, self.os_api); + current_position.set_should_render(true); } pub fn move_active_pane_down(&mut self, client_id: ClientId) { if !self.has_selectable_panes() { @@ -2815,16 +1476,10 @@ impl Tab { if self.fullscreen_is_active { return; } - if let Some(active) = self.get_active_pane(client_id) { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_below(active) && c.vertically_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); - if let Some(&p) = next_index { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_below(&active_pane_id); + if let Some(p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); @@ -2857,16 +1512,10 @@ impl Tab { if self.fullscreen_is_active { return; } - if let Some(active) = self.get_active_pane(client_id) { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_above(active) && c.vertically_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); - if let Some(&p) = next_index { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_above(&active_pane_id); + if let Some(p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); @@ -2899,16 +1548,10 @@ impl Tab { if self.fullscreen_is_active { return; } - if let Some(active) = self.get_active_pane(client_id) { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_right_of(active) && c.horizontally_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); - if let Some(&p) = next_index { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id); + if let Some(p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); @@ -2941,16 +1584,10 @@ impl Tab { if self.fullscreen_is_active { return; } - if let Some(active) = self.get_active_pane(client_id) { - let terminals = self.get_selectable_panes(); - let next_index = terminals - .enumerate() - .filter(|(_, (_, c))| { - c.is_directly_left_of(active) && c.horizontally_overlaps_with(active) - }) - .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid); - if let Some(&p) = next_index { + if let Some(active_pane_id) = self.get_active_pane_id(client_id) { + let pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + let next_index = pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id); + if let Some(p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); let current_position = self.panes.get(active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); @@ -2976,131 +1613,6 @@ impl Tab { } } } - fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet { - terminals.iter().fold(HashSet::new(), |mut borders, t| { - let terminal = self.panes.get(t).unwrap(); - borders.insert(terminal.y()); - borders.insert(terminal.y() + terminal.rows()); - borders - }) - } - fn vertical_borders(&self, terminals: &[PaneId]) -> HashSet { - terminals.iter().fold(HashSet::new(), |mut borders, t| { - let terminal = self.panes.get(t).unwrap(); - borders.insert(terminal.x()); - borders.insert(terminal.x() + terminal.cols()); - borders - }) - } - - fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option> { - if let Some(terminal) = self.panes.get(&id) { - let upper_close_border = terminal.y(); - let lower_close_border = terminal.y() + terminal.rows(); - - if let Some(terminals_to_the_left) = self.pane_ids_directly_left_of(&id) { - let mut selectable_panes: Vec<_> = terminals_to_the_left - .into_iter() - .filter(|pid| self.panes[pid].selectable()) - .collect(); - let terminal_borders_to_the_left = self.horizontal_borders(&selectable_panes); - if terminal_borders_to_the_left.contains(&upper_close_border) - && terminal_borders_to_the_left.contains(&lower_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_horizontal_borders( - t, - upper_close_border, - lower_close_border, - ) - }); - return Some(selectable_panes); - } - } - } - None - } - fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option> { - if let Some(terminal) = self.panes.get(&id) { - let upper_close_border = terminal.y(); - let lower_close_border = terminal.y() + terminal.rows(); - - if let Some(terminals_to_the_right) = self.pane_ids_directly_right_of(&id) { - let mut selectable_panes: Vec<_> = terminals_to_the_right - .into_iter() - .filter(|pid| self.panes[pid].selectable()) - .collect(); - let terminal_borders_to_the_right = self.horizontal_borders(&selectable_panes); - if terminal_borders_to_the_right.contains(&upper_close_border) - && terminal_borders_to_the_right.contains(&lower_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_horizontal_borders( - t, - upper_close_border, - lower_close_border, - ) - }); - return Some(selectable_panes); - } - } - } - None - } - fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option> { - if let Some(terminal) = self.panes.get(&id) { - let left_close_border = terminal.x(); - let right_close_border = terminal.x() + terminal.cols(); - - if let Some(terminals_above) = self.pane_ids_directly_above(&id) { - let mut selectable_panes: Vec<_> = terminals_above - .into_iter() - .filter(|pid| self.panes[pid].selectable()) - .collect(); - let terminal_borders_above = self.vertical_borders(&selectable_panes); - if terminal_borders_above.contains(&left_close_border) - && terminal_borders_above.contains(&right_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_vertical_borders( - t, - left_close_border, - right_close_border, - ) - }); - return Some(selectable_panes); - } - } - } - None - } - fn panes_below_between_aligning_borders(&self, id: PaneId) -> Option> { - if let Some(terminal) = self.panes.get(&id) { - let left_close_border = terminal.x(); - let right_close_border = terminal.x() + terminal.cols(); - - if let Some(terminals_below) = self.pane_ids_directly_below(&id) { - let mut selectable_panes: Vec<_> = terminals_below - .into_iter() - .filter(|pid| self.panes[pid].selectable()) - .collect(); - let terminal_borders_below = self.vertical_borders(&selectable_panes); - if terminal_borders_below.contains(&left_close_border) - && terminal_borders_below.contains(&right_close_border) - { - selectable_panes.retain(|t| { - self.pane_is_between_vertical_borders( - t, - left_close_border, - right_close_border, - ) - }); - return Some(selectable_panes); - } - } - } - None - } fn close_down_to_max_terminals(&mut self) { if let Some(max_panes) = self.max_panes { let terminals = self.get_pane_ids(); @@ -3115,20 +1627,6 @@ impl Tab { pub fn get_pane_ids(&self) -> Vec { self.get_panes().map(|(&pid, _)| pid).collect() } - fn viewport_pane_ids_directly_above(&self, active_pane_id: &PaneId) -> Vec { - self.pane_ids_directly_above(active_pane_id) - .unwrap_or_default() - .into_iter() - .filter(|id| self.is_inside_viewport(id)) - .collect() - } - fn viewport_pane_ids_directly_below(&self, active_pane_id: &PaneId) -> Vec { - self.pane_ids_directly_below(active_pane_id) - .unwrap_or_default() - .into_iter() - .filter(|id| self.is_inside_viewport(id)) - .collect() - } pub fn set_pane_selectable(&mut self, id: PaneId, selectable: bool) { if let Some(pane) = self.panes.get_mut(&id) { pane.set_selectable(selectable); @@ -3167,58 +1665,22 @@ impl Tab { if self.fullscreen_is_active { self.unset_fullscreen(); } - if let Some(pane_to_close) = self.panes.get(&id) { - let freed_space = pane_to_close.position_and_size(); - if let (Some(freed_width), Some(freed_height)) = - (freed_space.cols.as_percent(), freed_space.rows.as_percent()) - { - if let Some((panes, direction)) = self.find_panes_to_grow(id) { - self.grow_panes(&panes, direction, (freed_width, freed_height)); - let pane = self.panes.remove(&id); - self.move_clients_out_of_pane(id); - self.relayout_tab(direction); - return pane; - } + let mut pane_grid = PaneGrid::new(&mut self.panes, self.display_area, self.viewport); + if pane_grid.fill_space_over_pane(id) { + // successfully filled space over pane + let closed_pane = self.panes.remove(&id); + self.move_clients_out_of_pane(id); + for pane in self.panes.values_mut() { + resize_pty!(pane, self.os_api); } - // if we reached here, this is either the last pane or there's some sort of - // configuration error (eg. we're trying to close a pane surrounded by fixed panes) - let pane = self.panes.remove(&id); + closed_pane + } else { + self.panes.remove(&id); + // this is a bit of a roundabout way to say: this is the last pane and so the tab + // should be destroyed self.active_panes.clear(); - self.resize_whole_tab(self.display_area); - return pane; + None } - None - } - fn find_panes_to_grow(&self, id: PaneId) -> Option<(Vec, Direction)> { - if let Some(panes) = self - .panes_to_the_left_between_aligning_borders(id) - .or_else(|| self.panes_to_the_right_between_aligning_borders(id)) - { - return Some((panes, Direction::Horizontal)); - } - - if let Some(panes) = self - .panes_above_between_aligning_borders(id) - .or_else(|| self.panes_below_between_aligning_borders(id)) - { - return Some((panes, Direction::Vertical)); - } - - None - } - fn grow_panes(&mut self, panes: &[PaneId], direction: Direction, (width, height): (f64, f64)) { - match direction { - Direction::Horizontal => { - for pane_id in panes { - self.increase_pane_width(pane_id, width); - } - } - Direction::Vertical => { - for pane_id in panes { - self.increase_pane_height(pane_id, height); - } - } - }; } pub fn close_focused_pane(&mut self, client_id: ClientId) { if let Some(active_pane_id) = self.get_active_pane_id(client_id) { @@ -3543,7 +2005,7 @@ impl Tab { } #[allow(clippy::borrowed_box)] -fn is_inside_viewport(viewport: &Viewport, pane: &Box) -> bool { +pub fn is_inside_viewport(viewport: &Viewport, pane: &Box) -> bool { let pane_position_and_size = pane.current_geom(); pane_position_and_size.y >= viewport.y && pane_position_and_size.y + pane_position_and_size.rows.as_usize() diff --git a/zellij-server/src/tab/pane_grid.rs b/zellij-server/src/tab/pane_grid.rs new file mode 100644 index 000000000..e1e2cbeb9 --- /dev/null +++ b/zellij-server/src/tab/pane_grid.rs @@ -0,0 +1,1745 @@ +use super::pane_resizer::PaneResizer; +use crate::tab::is_inside_viewport; +use crate::{panes::PaneId, tab::Pane}; +use std::cmp::Reverse; +use std::collections::{HashMap, HashSet}; +use zellij_utils::{ + input::layout::Direction, + pane_size::{Dimension, PaneGeom, Size, Viewport}, +}; + +use std::cell::RefCell; +use std::rc::Rc; + +const RESIZE_PERCENT: f64 = 5.0; +const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this + +// FIXME: This should be replaced by `RESIZE_PERCENT` at some point +const MIN_TERMINAL_HEIGHT: usize = 5; +const MIN_TERMINAL_WIDTH: usize = 5; +type BorderAndPaneIds = (usize, Vec); + +pub struct PaneGrid<'a> { + // panes: HashMap<&'a PaneId, &'a mut Box>, + panes: Rc>>>, + display_area: Size, // includes all panes (including eg. the status bar and tab bar in the default layout) + viewport: Viewport, // includes all non-UI panes +} + +impl<'a> PaneGrid<'a> { + pub fn new( + panes: impl IntoIterator)>, + display_area: Size, + viewport: Viewport, + ) -> Self { + let panes: HashMap<_, _> = panes.into_iter().map(|(p_id, p)| (*p_id, p)).collect(); + PaneGrid { + panes: Rc::new(RefCell::new(panes)), + display_area, + viewport, + } + } + + pub fn layout(&mut self, direction: Direction, space: usize) -> Result<(), String> { + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + pane_resizer.layout(direction, space) + } + pub fn resize_pane_left(&'a mut self, pane_id: &PaneId) { + // TODO: find out by how much we actually reduced and only reduce by that much + if self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { + self.increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + // let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + } else if self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { + self.reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + } + } + pub fn resize_pane_right(&mut self, pane_id: &PaneId) { + // TODO: find out by how much we actually reduced and only reduce by that much + if self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { + self.increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + // TODO: CONTINUE HERE - fix these to self.panes.clone() - then fix the rest of the + // functions to match, those functions that break out of the RC (eg. return panes) make + // return pane ids and handle them in function, a little work, but eh - we'll get + // through it! + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + } else if self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { + self.reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + } + } + pub fn resize_pane_down(&mut self, pane_id: &PaneId) { + // TODO: find out by how much we actually reduced and only reduce by that much + if self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT) { + self.increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + } else if self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT) { + self.reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + } + } + pub fn resize_pane_up(&mut self, pane_id: &PaneId) { + // TODO: find out by how much we actually reduced and only reduce by that much + if self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT) { + self.increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + } else if self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT) { + self.reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + } + } + pub fn resize_increase(&mut self, pane_id: &PaneId) { + if self.try_increase_pane_and_surroundings_right_and_down(pane_id) { + return; + } + if self.try_increase_pane_and_surroundings_left_and_down(pane_id) { + return; + } + if self.try_increase_pane_and_surroundings_right_and_up(pane_id) { + return; + } + if self.try_increase_pane_and_surroundings_left_and_up(pane_id) { + return; + } + + if self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { + return; + } + if self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT) { + return; + } + if self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { + return; + } + self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + } + pub fn resize_decrease(&mut self, pane_id: &PaneId) { + if self.try_reduce_pane_and_surroundings_left_and_up(pane_id) { + return; + } + if self.try_reduce_pane_and_surroundings_right_and_up(pane_id) { + return; + } + if self.try_reduce_pane_and_surroundings_right_and_down(pane_id) { + return; + } + if self.try_reduce_pane_and_surroundings_left_and_down(pane_id) { + return; + } + if self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT) { + return; + } + if self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT) { + return; + } + if self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT) { + return; + } + self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + } + fn can_increase_pane_and_surroundings_right(&self, pane_id: &PaneId, increase_by: f64) -> bool { + let panes = self.panes.borrow(); + if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(pane_id) { + panes_to_the_right.iter().all(|id| { + let p = panes.get(id).unwrap(); + if let Some(cols) = p.position_and_size().cols.as_percent() { + let current_fixed_cols = p.position_and_size().cols.as_usize(); + let will_reduce_by = + ((self.display_area.cols as f64 / 100.0) * increase_by) as usize; + cols - increase_by >= RESIZE_PERCENT + && current_fixed_cols.saturating_sub(will_reduce_by) >= p.min_width() + } else { + false + } + }) + } else { + false + } + } + fn can_increase_pane_and_surroundings_left(&self, pane_id: &PaneId, increase_by: f64) -> bool { + let panes = self.panes.borrow(); + if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(pane_id) { + panes_to_the_left.iter().all(|id| { + let p = panes.get(id).unwrap(); + if let Some(cols) = p.position_and_size().cols.as_percent() { + let current_fixed_cols = p.position_and_size().cols.as_usize(); + let will_reduce_by = + ((self.display_area.cols as f64 / 100.0) * increase_by) as usize; + cols - increase_by >= RESIZE_PERCENT + && current_fixed_cols.saturating_sub(will_reduce_by) >= p.min_width() + } else { + false + } + }) + } else { + false + } + } + fn can_increase_pane_and_surroundings_down(&self, pane_id: &PaneId, increase_by: f64) -> bool { + let panes = self.panes.borrow(); + if let Some(panes_below) = self.pane_ids_directly_below(pane_id) { + panes_below.iter().all(|id| { + let p = panes.get(id).unwrap(); + if let Some(rows) = p.position_and_size().rows.as_percent() { + let current_fixed_rows = p.position_and_size().rows.as_usize(); + let will_reduce_by = + ((self.display_area.rows as f64 / 100.0) * increase_by) as usize; + rows - increase_by >= RESIZE_PERCENT + && current_fixed_rows.saturating_sub(will_reduce_by) >= p.min_height() + } else { + false + } + }) + } else { + false + } + } + + fn can_increase_pane_and_surroundings_up(&self, pane_id: &PaneId, increase_by: f64) -> bool { + let panes = self.panes.borrow(); + if let Some(panes_above) = self.pane_ids_directly_above(pane_id) { + panes_above.iter().all(|id| { + let p = panes.get(id).unwrap(); + if let Some(rows) = p.position_and_size().rows.as_percent() { + let current_fixed_rows = p.position_and_size().rows.as_usize(); + let will_reduce_by = + ((self.display_area.rows as f64 / 100.0) * increase_by) as usize; + rows - increase_by >= RESIZE_PERCENT + && current_fixed_rows.saturating_sub(will_reduce_by) >= p.min_height() + } else { + false + } + }) + } else { + false + } + } + fn can_reduce_pane_and_surroundings_right(&self, pane_id: &PaneId, reduce_by: f64) -> bool { + let panes = self.panes.borrow(); + let pane = panes.get(pane_id).unwrap(); + if let Some(cols) = pane.position_and_size().cols.as_percent() { + let current_fixed_cols = pane.position_and_size().cols.as_usize(); + let will_reduce_by = ((self.display_area.cols as f64 / 100.0) * reduce_by) as usize; + let ids_left = self.pane_ids_directly_left_of(pane_id); + let flexible_left = self.ids_are_flexible(Direction::Horizontal, ids_left); + cols - reduce_by >= RESIZE_PERCENT + && flexible_left + && current_fixed_cols.saturating_sub(will_reduce_by) >= pane.min_width() + } else { + false + } + } + fn can_reduce_pane_and_surroundings_left(&self, pane_id: &PaneId, reduce_by: f64) -> bool { + let panes = self.panes.borrow(); + let pane = panes.get(pane_id).unwrap(); + if let Some(cols) = pane.position_and_size().cols.as_percent() { + let current_fixed_cols = pane.position_and_size().cols.as_usize(); + let will_reduce_by = ((self.display_area.cols as f64 / 100.0) * reduce_by) as usize; + let ids_right = self.pane_ids_directly_right_of(pane_id); + let flexible_right = self.ids_are_flexible(Direction::Horizontal, ids_right); + cols - reduce_by >= RESIZE_PERCENT + && flexible_right + && current_fixed_cols.saturating_sub(will_reduce_by) >= pane.min_width() + } else { + false + } + } + fn can_reduce_pane_and_surroundings_down(&self, pane_id: &PaneId, reduce_by: f64) -> bool { + let panes = self.panes.borrow(); + let pane = panes.get(pane_id).unwrap(); + if let Some(rows) = pane.position_and_size().rows.as_percent() { + let current_fixed_rows = pane.position_and_size().rows.as_usize(); + let will_reduce_by = ((self.display_area.rows as f64 / 100.0) * reduce_by) as usize; + let ids_above = self.pane_ids_directly_above(pane_id); + let flexible_above = self.ids_are_flexible(Direction::Vertical, ids_above); + rows - reduce_by >= RESIZE_PERCENT + && flexible_above + && current_fixed_rows.saturating_sub(will_reduce_by) >= pane.min_height() + } else { + false + } + } + fn can_reduce_pane_and_surroundings_up(&self, pane_id: &PaneId, reduce_by: f64) -> bool { + let panes = self.panes.borrow(); + let pane = panes.get(pane_id).unwrap(); + if let Some(rows) = pane.position_and_size().rows.as_percent() { + let current_fixed_rows = pane.position_and_size().rows.as_usize(); + let will_reduce_by = ((self.display_area.rows as f64 / 100.0) * reduce_by) as usize; + let ids_below = self.pane_ids_directly_below(pane_id); + let flexible_below = self.ids_are_flexible(Direction::Vertical, ids_below); + rows - reduce_by >= RESIZE_PERCENT + && flexible_below + && current_fixed_rows.saturating_sub(will_reduce_by) >= pane.min_height() + } else { + false + } + } + fn reduce_pane_height(&mut self, id: &PaneId, percent: f64) { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.reduce_height(percent); + } + fn increase_pane_height(&mut self, id: &PaneId, percent: f64) { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.increase_height(percent); + } + fn increase_pane_width(&mut self, id: &PaneId, percent: f64) { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.increase_width(percent); + } + fn reduce_pane_width(&mut self, id: &PaneId, percent: f64) { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.reduce_width(percent); + } + fn increase_pane_and_surroundings_up(&mut self, id: &PaneId, percent: f64) { + let mut terminals_above = self + .pane_ids_directly_above(id) + .expect("can't increase pane size up if there are no terminals above"); + let terminal_borders_above: HashSet = terminals_above + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().x()) + .collect(); + let (left_resize_border, terminals_to_the_left) = + self.top_aligned_contiguous_panes_to_the_left(id, &terminal_borders_above); + let (right_resize_border, terminals_to_the_right) = + self.top_aligned_contiguous_panes_to_the_right(id, &terminal_borders_above); + terminals_above.retain(|t| { + self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) + }); + self.increase_pane_height(id, percent); + for terminal_id in terminals_above { + self.reduce_pane_height(&terminal_id, percent); + } + for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { + self.increase_pane_height(terminal_id, percent); + } + } + fn increase_pane_and_surroundings_down(&mut self, id: &PaneId, percent: f64) { + let mut terminals_below = self + .pane_ids_directly_below(id) + .expect("can't increase pane size down if there are no terminals below"); + let terminal_borders_below: HashSet = terminals_below + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().x()) + .collect(); + let (left_resize_border, terminals_to_the_left) = + self.bottom_aligned_contiguous_panes_to_the_left(id, &terminal_borders_below); + let (right_resize_border, terminals_to_the_right) = + self.bottom_aligned_contiguous_panes_to_the_right(id, &terminal_borders_below); + terminals_below.retain(|t| { + self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) + }); + self.increase_pane_height(id, percent); + for terminal_id in terminals_below { + self.reduce_pane_height(&terminal_id, percent); + } + for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { + self.increase_pane_height(terminal_id, percent); + } + } + fn increase_pane_and_surroundings_right(&mut self, id: &PaneId, percent: f64) { + let mut terminals_to_the_right = self + .pane_ids_directly_right_of(id) + .expect("can't increase pane size right if there are no terminals to the right"); + let terminal_borders_to_the_right: HashSet = terminals_to_the_right + .iter() + .map(|t| { + return self.panes.borrow().get(t).unwrap().y(); + }) + .collect(); + let (top_resize_border, terminals_above) = + self.right_aligned_contiguous_panes_above(id, &terminal_borders_to_the_right); + let (bottom_resize_border, terminals_below) = + self.right_aligned_contiguous_panes_below(id, &terminal_borders_to_the_right); + terminals_to_the_right.retain(|t| { + self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) + }); + self.increase_pane_width(id, percent); + for terminal_id in terminals_to_the_right { + self.reduce_pane_width(&terminal_id, percent); + } + for terminal_id in terminals_above.iter().chain(&terminals_below) { + self.increase_pane_width(terminal_id, percent); + } + } + fn increase_pane_and_surroundings_left(&mut self, id: &PaneId, percent: f64) { + let mut terminals_to_the_left = self + .pane_ids_directly_left_of(id) + .expect("can't increase pane size right if there are no terminals to the right"); + let terminal_borders_to_the_left: HashSet = terminals_to_the_left + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().y()) + .collect(); + let (top_resize_border, terminals_above) = + self.left_aligned_contiguous_panes_above(id, &terminal_borders_to_the_left); + let (bottom_resize_border, terminals_below) = + self.left_aligned_contiguous_panes_below(id, &terminal_borders_to_the_left); + terminals_to_the_left.retain(|t| { + self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) + }); + self.increase_pane_width(id, percent); + for terminal_id in terminals_to_the_left { + self.reduce_pane_width(&terminal_id, percent); + } + for terminal_id in terminals_above.iter().chain(&terminals_below) { + self.increase_pane_width(terminal_id, percent); + } + } + fn reduce_pane_and_surroundings_up(&mut self, id: &PaneId, percent: f64) { + let mut terminals_below = self + .pane_ids_directly_below(id) + .expect("can't reduce pane size up if there are no terminals below"); + let terminal_borders_below: HashSet = terminals_below + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().x()) + .collect(); + let (left_resize_border, terminals_to_the_left) = + self.bottom_aligned_contiguous_panes_to_the_left(id, &terminal_borders_below); + let (right_resize_border, terminals_to_the_right) = + self.bottom_aligned_contiguous_panes_to_the_right(id, &terminal_borders_below); + terminals_below.retain(|t| { + self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) + }); + + // FIXME: This checks that we aren't violating the resize constraints of the aligned panes + // above and below this one. This should be moved to a `can_resize` function eventually. + for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { + let panes = self.panes.borrow(); + let pane = panes.get(terminal_id).unwrap(); + if pane.current_geom().rows.as_percent().unwrap() - percent < RESIZE_PERCENT { + return; + } + } + + self.reduce_pane_height(id, percent); + for terminal_id in terminals_below { + self.increase_pane_height(&terminal_id, percent); + } + for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { + self.reduce_pane_height(terminal_id, percent); + } + } + fn reduce_pane_and_surroundings_down(&mut self, id: &PaneId, percent: f64) { + let mut terminals_above = self + .pane_ids_directly_above(id) + .expect("can't reduce pane size down if there are no terminals above"); + let terminal_borders_above: HashSet = terminals_above + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().x()) + .collect(); + let (left_resize_border, terminals_to_the_left) = + self.top_aligned_contiguous_panes_to_the_left(id, &terminal_borders_above); + let (right_resize_border, terminals_to_the_right) = + self.top_aligned_contiguous_panes_to_the_right(id, &terminal_borders_above); + terminals_above.retain(|t| { + self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border) + }); + + // FIXME: This checks that we aren't violating the resize constraints of the aligned panes + // above and below this one. This should be moved to a `can_resize` function eventually. + for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { + let panes = self.panes.borrow(); + let pane = panes.get(terminal_id).unwrap(); + if pane.current_geom().rows.as_percent().unwrap() - percent < RESIZE_PERCENT { + return; + } + } + + self.reduce_pane_height(id, percent); + for terminal_id in terminals_above { + self.increase_pane_height(&terminal_id, percent); + } + for terminal_id in terminals_to_the_left.iter().chain(&terminals_to_the_right) { + self.reduce_pane_height(terminal_id, percent); + } + } + fn reduce_pane_and_surroundings_right(&mut self, id: &PaneId, percent: f64) { + let mut terminals_to_the_left = self + .pane_ids_directly_left_of(id) + .expect("can't reduce pane size right if there are no terminals to the left"); + let terminal_borders_to_the_left: HashSet = terminals_to_the_left + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().y()) + .collect(); + let (top_resize_border, terminals_above) = + self.left_aligned_contiguous_panes_above(id, &terminal_borders_to_the_left); + let (bottom_resize_border, terminals_below) = + self.left_aligned_contiguous_panes_below(id, &terminal_borders_to_the_left); + terminals_to_the_left.retain(|t| { + self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) + }); + + // FIXME: This checks that we aren't violating the resize constraints of the aligned panes + // above and below this one. This should be moved to a `can_resize` function eventually. + for terminal_id in terminals_above.iter().chain(&terminals_below) { + let panes = self.panes.borrow(); + let pane = panes.get(terminal_id).unwrap(); + if pane.current_geom().cols.as_percent().unwrap() - percent < RESIZE_PERCENT { + return; + } + } + + self.reduce_pane_width(id, percent); + for terminal_id in terminals_to_the_left { + self.increase_pane_width(&terminal_id, percent); + } + for terminal_id in terminals_above.iter().chain(&terminals_below) { + self.reduce_pane_width(terminal_id, percent); + } + } + fn reduce_pane_and_surroundings_left(&mut self, id: &PaneId, percent: f64) { + let mut terminals_to_the_right = self + .pane_ids_directly_right_of(id) + .expect("can't reduce pane size left if there are no terminals to the right"); + let terminal_borders_to_the_right: HashSet = terminals_to_the_right + .iter() + .map(|t| self.panes.borrow().get(t).unwrap().y()) + .collect(); + let (top_resize_border, terminals_above) = + self.right_aligned_contiguous_panes_above(id, &terminal_borders_to_the_right); + let (bottom_resize_border, terminals_below) = + self.right_aligned_contiguous_panes_below(id, &terminal_borders_to_the_right); + terminals_to_the_right.retain(|t| { + self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border) + }); + + // FIXME: This checks that we aren't violating the resize constraints of the aligned panes + // above and below this one. This should be moved to a `can_resize` function eventually. + for terminal_id in terminals_above.iter().chain(&terminals_below) { + let panes = self.panes.borrow(); + let pane = panes.get(terminal_id).unwrap(); + if pane.current_geom().cols.as_percent().unwrap() - percent < RESIZE_PERCENT { + return; + } + } + + self.reduce_pane_width(id, percent); + for terminal_id in terminals_to_the_right { + self.increase_pane_width(&terminal_id, percent); + } + for terminal_id in terminals_above.iter().chain(&terminals_below) { + self.reduce_pane_width(terminal_id, percent); + } + } + fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option> { + let panes = self.panes.borrow(); + let mut ids = vec![]; + let terminal_to_check = panes.get(id).unwrap(); + if terminal_to_check.x() == 0 { + return None; + } + // for (&pid, terminal) in self.get_panes() { + for (&pid, terminal) in panes.iter() { + if terminal.x() + terminal.cols() == terminal_to_check.x() { + ids.push(pid); + } + } + if ids.is_empty() { + None + } else { + Some(ids) + } + } + fn pane_ids_directly_right_of(&self, id: &PaneId) -> Option> { + let mut ids = vec![]; + let panes = self.panes.borrow(); + let terminal_to_check = panes.get(id).unwrap(); + // for (&pid, terminal) in self.get_panes() { + for (&pid, terminal) in panes.iter() { + if terminal.x() == terminal_to_check.x() + terminal_to_check.cols() { + ids.push(pid); + } + } + if ids.is_empty() { + None + } else { + Some(ids) + } + } + fn pane_ids_directly_below(&self, id: &PaneId) -> Option> { + let mut ids = vec![]; + let panes = self.panes.borrow(); + let terminal_to_check = panes.get(id).unwrap(); + // for (&pid, terminal) in self.get_panes() { + for (&pid, terminal) in panes.iter() { + if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() { + ids.push(pid); + } + } + if ids.is_empty() { + None + } else { + Some(ids) + } + } + fn pane_ids_directly_above(&self, id: &PaneId) -> Option> { + let mut ids = vec![]; + let panes = self.panes.borrow(); + let terminal_to_check = panes.get(id).unwrap(); + // for (&pid, terminal) in self.get_panes() { + for (&pid, terminal) in panes.iter() { + if terminal.y() + terminal.rows() == terminal_to_check.y() { + ids.push(pid); + } + } + if ids.is_empty() { + None + } else { + Some(ids) + } + } + fn pane_ids_top_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { + let panes = self.panes.borrow(); + let pane_to_check = panes.get(pane_id).unwrap(); + panes + .iter() + .filter(|(p_id, p)| *p_id != pane_id && p.y() == pane_to_check.y()) + .map(|(p_id, _p)| *p_id) + .collect() + } + fn pane_ids_bottom_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { + let panes = self.panes.borrow(); + let pane_to_check = panes.get(pane_id).unwrap(); + panes + .iter() + .filter(|(p_id, p)| { + *p_id != pane_id && p.y() + p.rows() == pane_to_check.y() + pane_to_check.rows() + }) + .map(|(p_id, _p)| *p_id) + .collect() + } + fn pane_ids_right_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { + let panes = self.panes.borrow(); + let pane_to_check = panes.get(pane_id).unwrap(); + panes + .iter() + .filter(|(p_id, p)| { + *p_id != pane_id && p.x() + p.cols() == pane_to_check.x() + pane_to_check.cols() + }) + .map(|(p_id, _p)| *p_id) + .collect() + } + fn pane_ids_left_aligned_with_pane_id(&self, pane_id: &PaneId) -> Vec { + let panes = self.panes.borrow(); + let pane_to_check = panes.get(pane_id).unwrap(); + panes + .iter() + .filter(|(p_id, p)| *p_id != pane_id && p.x() == pane_to_check.x()) + .map(|(p_id, _p)| *p_id) + .collect() + } + fn right_aligned_contiguous_panes_above( + &self, + id: &PaneId, + pane_borders_to_the_right: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut right_aligned_panes: Vec<_> = self + .pane_ids_right_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + // panes that are next to each other up to current + right_aligned_panes.sort_by_key(|a| Reverse(a.y())); + for pane in right_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.y() + pane.rows() == pane_to_check.y() { + result_panes.push(pane); + } + } + // top-most border aligned with a pane border to the right + let mut top_resize_border = 0; + for pane in &result_panes { + let bottom_pane_boundary = pane.y() + pane.rows(); + if pane_borders_to_the_right + .get(&bottom_pane_boundary) + .is_some() + && top_resize_border < bottom_pane_boundary + { + top_resize_border = bottom_pane_boundary; + } + } + result_panes.retain(|pane| pane.y() >= top_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let top_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.y() + } else { + top_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (top_resize_border, pane_ids) + } + fn right_aligned_contiguous_panes_below( + &self, + id: &PaneId, + pane_borders_to_the_right: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut right_aligned_panes: Vec<_> = self + .pane_ids_right_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + // panes that are next to each other up to current + right_aligned_panes.sort_by_key(|a| a.y()); + for pane in right_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.y() == pane_to_check.y() + pane_to_check.rows() { + result_panes.push(pane); + } + } + // bottom-most border aligned with a pane border to the right + let mut bottom_resize_border = self.viewport.y + self.viewport.rows; + for pane in &result_panes { + let top_pane_boundary = pane.y(); + if pane_borders_to_the_right + .get(&(top_pane_boundary)) + .is_some() + && top_pane_boundary < bottom_resize_border + { + bottom_resize_border = top_pane_boundary; + } + } + result_panes.retain(|pane| pane.y() + pane.rows() <= bottom_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let bottom_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.y() + pane_to_check.rows() + } else { + bottom_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (bottom_resize_border, pane_ids) + } + fn left_aligned_contiguous_panes_above( + &self, + id: &PaneId, + pane_borders_to_the_left: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut left_aligned_panes: Vec<_> = self + .pane_ids_left_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + // panes that are next to each other up to current + left_aligned_panes.sort_by_key(|a| Reverse(a.y())); + for pane in left_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.y() + pane.rows() == pane_to_check.y() { + result_panes.push(pane); + } + } + // top-most border aligned with a pane border to the right + let mut top_resize_border = 0; + for pane in &result_panes { + let bottom_pane_boundary = pane.y() + pane.rows(); + if pane_borders_to_the_left + .get(&bottom_pane_boundary) + .is_some() + && top_resize_border < bottom_pane_boundary + { + top_resize_border = bottom_pane_boundary; + } + } + result_panes.retain(|pane| pane.y() >= top_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let top_resize_border = if panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.y() + } else { + top_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (top_resize_border, pane_ids) + } + fn left_aligned_contiguous_panes_below( + &self, + id: &PaneId, + pane_borders_to_the_left: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut left_aligned_panes: Vec<_> = self + .pane_ids_left_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + // panes that are next to each other up to current + left_aligned_panes.sort_by_key(|a| a.y()); + for pane in left_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.y() == pane_to_check.y() + pane_to_check.rows() { + result_panes.push(pane); + } + } + // bottom-most border aligned with a pane border to the left + let mut bottom_resize_border = self.viewport.y + self.viewport.rows; + for pane in &result_panes { + let top_pane_boundary = pane.y(); + if pane_borders_to_the_left.get(&(top_pane_boundary)).is_some() + && top_pane_boundary < bottom_resize_border + { + bottom_resize_border = top_pane_boundary; + } + } + result_panes.retain(|pane| pane.y() + pane.rows() <= bottom_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let bottom_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.y() + pane_to_check.rows() + } else { + bottom_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (bottom_resize_border, pane_ids) + } + fn top_aligned_contiguous_panes_to_the_left( + &self, + id: &PaneId, + pane_borders_above: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut top_aligned_panes: Vec<_> = self + .pane_ids_top_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + // panes that are next to each other up to current + top_aligned_panes.sort_by_key(|a| Reverse(a.x())); + for pane in top_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.x() + pane.cols() == pane_to_check.x() { + result_panes.push(pane); + } + } + // leftmost border aligned with a pane border above + let mut left_resize_border = 0; + for pane in &result_panes { + let right_pane_boundary = pane.x() + pane.cols(); + if pane_borders_above.get(&right_pane_boundary).is_some() + && left_resize_border < right_pane_boundary + { + left_resize_border = right_pane_boundary + } + } + result_panes.retain(|pane| pane.x() >= left_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let left_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.x() + } else { + left_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (left_resize_border, pane_ids) + } + fn top_aligned_contiguous_panes_to_the_right( + &self, + id: &PaneId, + pane_borders_above: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut top_aligned_panes: Vec<_> = self + .pane_ids_top_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + // panes that are next to each other up to current + top_aligned_panes.sort_by_key(|a| a.x()); + for pane in top_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.x() == pane_to_check.x() + pane_to_check.cols() { + result_panes.push(pane); + } + } + // rightmost border aligned with a pane border above + let mut right_resize_border = self.viewport.x + self.viewport.cols; + for pane in &result_panes { + let left_pane_boundary = pane.x(); + if pane_borders_above.get(&left_pane_boundary).is_some() + && right_resize_border > left_pane_boundary + { + right_resize_border = left_pane_boundary; + } + } + result_panes.retain(|pane| pane.x() + pane.cols() <= right_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let right_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.x() + pane_to_check.cols() + } else { + right_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (right_resize_border, pane_ids) + } + fn bottom_aligned_contiguous_panes_to_the_left( + &self, + id: &PaneId, + pane_borders_below: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut bottom_aligned_panes: Vec<_> = self + .pane_ids_bottom_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + bottom_aligned_panes.sort_by_key(|a| Reverse(a.x())); + // panes that are next to each other up to current + for pane in bottom_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.x() + pane.cols() == pane_to_check.x() { + result_panes.push(pane); + } + } + // leftmost border aligned with a pane border above + let mut left_resize_border = 0; + for pane in &result_panes { + let right_pane_boundary = pane.x() + pane.cols(); + if pane_borders_below.get(&right_pane_boundary).is_some() + && left_resize_border < right_pane_boundary + { + left_resize_border = right_pane_boundary; + } + } + result_panes.retain(|terminal| terminal.x() >= left_resize_border); + // if there are no adjacent panes to resize, we use the border of the main pane we're + // resizing + let left_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.x() + } else { + left_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (left_resize_border, pane_ids) + } + fn bottom_aligned_contiguous_panes_to_the_right( + &self, + id: &PaneId, + pane_borders_below: &HashSet, + ) -> BorderAndPaneIds { + let panes = self.panes.borrow(); + let mut result_panes = vec![]; + let mut bottom_aligned_panes: Vec<_> = self + .pane_ids_bottom_aligned_with_pane_id(id) + .iter() + .map(|p_id| panes.get(p_id).unwrap()) + .collect(); + bottom_aligned_panes.sort_by_key(|a| a.x()); + // panes that are next to each other up to current + for pane in bottom_aligned_panes { + let pane_to_check = panes.get(id).unwrap(); + let pane_to_check = result_panes.last().unwrap_or(&pane_to_check); + if pane.x() == pane_to_check.x() + pane_to_check.cols() { + result_panes.push(pane); + } + } + // leftmost border aligned with a pane border above + let mut right_resize_border = self.viewport.x + self.viewport.cols; + for pane in &result_panes { + let left_pane_boundary = pane.x(); + if pane_borders_below.get(&left_pane_boundary).is_some() + && right_resize_border > left_pane_boundary + { + right_resize_border = left_pane_boundary; + } + } + result_panes.retain(|terminal| terminal.x() + terminal.cols() <= right_resize_border); + let right_resize_border = if result_panes.is_empty() { + let pane_to_check = panes.get(id).unwrap(); + pane_to_check.x() + pane_to_check.cols() + } else { + right_resize_border + }; + let pane_ids: Vec = result_panes.iter().map(|t| t.pid()).collect(); + (right_resize_border, pane_ids) + } + fn ids_are_flexible(&self, direction: Direction, pane_ids: Option>) -> bool { + let panes = self.panes.borrow(); + pane_ids.is_some() + && pane_ids.unwrap().iter().all(|id| { + let pane_to_check = panes.get(id).unwrap(); + let geom = pane_to_check.current_geom(); + let dimension = match direction { + Direction::Vertical => geom.rows, + Direction::Horizontal => geom.cols, + }; + !dimension.is_fixed() + }) + } + fn pane_is_between_vertical_borders( + &self, + id: &PaneId, + left_border_x: usize, + right_border_x: usize, + ) -> bool { + let panes = self.panes.borrow(); + let pane = panes.get(id).unwrap(); + pane.x() >= left_border_x && pane.x() + pane.cols() <= right_border_x + } + fn pane_is_between_horizontal_borders( + &self, + id: &PaneId, + top_border_y: usize, + bottom_border_y: usize, + ) -> bool { + let panes = self.panes.borrow(); + let pane = panes.get(id).unwrap(); + pane.y() >= top_border_y && pane.y() + pane.rows() <= bottom_border_y + } + fn try_increase_pane_and_surroundings_right( + &mut self, + pane_id: &PaneId, + reduce_by: f64, + ) -> bool { + if self.can_increase_pane_and_surroundings_right(pane_id, reduce_by) { + self.increase_pane_and_surroundings_right(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + return true; + } + false + } + fn try_increase_pane_and_surroundings_left( + &mut self, + pane_id: &PaneId, + reduce_by: f64, + ) -> bool { + if self.can_increase_pane_and_surroundings_left(pane_id, reduce_by) { + self.increase_pane_and_surroundings_left(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + return true; + } + false + } + fn try_increase_pane_and_surroundings_up(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { + if self.can_increase_pane_and_surroundings_up(pane_id, reduce_by) { + self.increase_pane_and_surroundings_up(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + return true; + } + false + } + fn try_increase_pane_and_surroundings_down( + &mut self, + pane_id: &PaneId, + reduce_by: f64, + ) -> bool { + if self.can_increase_pane_and_surroundings_down(pane_id, reduce_by) { + self.increase_pane_and_surroundings_down(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + return true; + } + false + } + fn try_increase_pane_and_surroundings_right_and_up(&mut self, pane_id: &PaneId) -> bool { + let can_increase_pane_right = + self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + let can_increase_pane_up = + self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if can_increase_pane_right && can_increase_pane_up { + let pane_above_with_right_aligned_border = self + .viewport_pane_ids_directly_above(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() + active_pane.cols() == pane.x() + }); + self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if let Some(pane_above_with_right_aligned_border) = pane_above_with_right_aligned_border + { + self.try_reduce_pane_and_surroundings_right( + &pane_above_with_right_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_increase_pane_and_surroundings_left_and_up(&mut self, pane_id: &PaneId) -> bool { + let can_increase_pane_left = + self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + let can_increase_pane_up = + self.can_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if can_increase_pane_left && can_increase_pane_up { + let pane_above_with_left_aligned_border = self + .viewport_pane_ids_directly_above(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() == pane.x() + pane.cols() + }); + self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + self.try_increase_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if let Some(pane_above_with_left_aligned_border) = pane_above_with_left_aligned_border { + self.try_reduce_pane_and_surroundings_left( + &pane_above_with_left_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_increase_pane_and_surroundings_right_and_down(&mut self, pane_id: &PaneId) -> bool { + let can_increase_pane_right = + self.can_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + let can_increase_pane_down = + self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if can_increase_pane_right && can_increase_pane_down { + let pane_below_with_right_aligned_border = self + .viewport_pane_ids_directly_below(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() + active_pane.cols() == pane.x() + }); + self.try_increase_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if let Some(pane_below_with_right_aligned_border) = pane_below_with_right_aligned_border + { + self.try_reduce_pane_and_surroundings_right( + &pane_below_with_right_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_increase_pane_and_surroundings_left_and_down(&mut self, pane_id: &PaneId) -> bool { + let can_increase_pane_left = + self.can_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + let can_increase_pane_down = + self.can_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if can_increase_pane_left && can_increase_pane_down { + let pane_below_with_left_aligned_border = self + .viewport_pane_ids_directly_below(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() == pane.x() + pane.cols() + }); + self.try_increase_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + self.try_increase_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if let Some(pane_below_with_left_aligned_border) = pane_below_with_left_aligned_border { + self.try_reduce_pane_and_surroundings_left( + &pane_below_with_left_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_reduce_pane_and_surroundings_right_and_up(&mut self, pane_id: &PaneId) -> bool { + let can_reduce_pane_right = + self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + let can_reduce_pane_up = self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if can_reduce_pane_right && can_reduce_pane_up { + let pane_below_with_left_aligned_border = self + .viewport_pane_ids_directly_below(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() == pane.x() + pane.cols() + }); + self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if let Some(pane_below_with_left_aligned_border) = pane_below_with_left_aligned_border { + self.try_increase_pane_and_surroundings_right( + &pane_below_with_left_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_reduce_pane_and_surroundings_left_and_up(&mut self, pane_id: &PaneId) -> bool { + let can_reduce_pane_left = + self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + let can_reduce_pane_up = self.can_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if can_reduce_pane_left && can_reduce_pane_up { + let pane_below_with_right_aligned_border = self + .viewport_pane_ids_directly_below(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() + active_pane.cols() == pane.x() + }); + self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + self.try_reduce_pane_and_surroundings_up(pane_id, RESIZE_PERCENT); + if let Some(pane_below_with_right_aligned_border) = pane_below_with_right_aligned_border + { + self.try_increase_pane_and_surroundings_left( + &pane_below_with_right_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_reduce_pane_and_surroundings_right_and_down(&mut self, pane_id: &PaneId) -> bool { + let can_reduce_pane_right = + self.can_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + let can_reduce_pane_down = + self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if can_reduce_pane_right && can_reduce_pane_down { + let pane_above_with_left_aligned_border = self + .viewport_pane_ids_directly_above(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() == pane.x() + pane.cols() + }); + self.try_reduce_pane_and_surroundings_right(pane_id, RESIZE_PERCENT); + self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if let Some(pane_above_with_left_aligned_border) = pane_above_with_left_aligned_border { + self.try_increase_pane_and_surroundings_right( + &pane_above_with_left_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_reduce_pane_and_surroundings_left_and_down(&mut self, pane_id: &PaneId) -> bool { + let can_reduce_pane_left = + self.can_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + let can_reduce_pane_down = + self.can_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if can_reduce_pane_left && can_reduce_pane_down { + let pane_above_with_right_aligned_border = self + .viewport_pane_ids_directly_above(pane_id) + .iter() + .copied() + .find(|p_id| { + let panes = self.panes.borrow(); + let pane = panes.get(p_id).unwrap(); + let active_pane = panes.get(pane_id).unwrap(); + active_pane.x() + active_pane.cols() == pane.x() + }); + self.try_reduce_pane_and_surroundings_left(pane_id, RESIZE_PERCENT); + self.try_reduce_pane_and_surroundings_down(pane_id, RESIZE_PERCENT); + if let Some(pane_above_with_right_aligned_border) = pane_above_with_right_aligned_border + { + self.try_increase_pane_and_surroundings_left( + &pane_above_with_right_aligned_border, + RESIZE_PERCENT, + ); + } + true + } else { + false + } + } + fn try_reduce_pane_and_surroundings_right(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { + if self.can_reduce_pane_and_surroundings_right(pane_id, reduce_by) { + self.reduce_pane_and_surroundings_right(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + return true; + } + false + } + fn try_reduce_pane_and_surroundings_left(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { + if self.can_reduce_pane_and_surroundings_left(pane_id, reduce_by) { + self.reduce_pane_and_surroundings_left(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Horizontal, self.display_area.cols); + return true; + } + false + } + fn try_reduce_pane_and_surroundings_up(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { + if self.can_reduce_pane_and_surroundings_up(pane_id, reduce_by) { + self.reduce_pane_and_surroundings_up(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + return true; + } + false + } + fn try_reduce_pane_and_surroundings_down(&mut self, pane_id: &PaneId, reduce_by: f64) -> bool { + if self.can_reduce_pane_and_surroundings_down(pane_id, reduce_by) { + self.reduce_pane_and_surroundings_down(pane_id, reduce_by); + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(Direction::Vertical, self.display_area.rows); + return true; + } + false + } + fn viewport_pane_ids_directly_above(&self, pane_id: &PaneId) -> Vec { + self.pane_ids_directly_above(pane_id) + .unwrap_or_default() + .into_iter() + .filter(|id| self.is_inside_viewport(id)) + .collect() + } + fn viewport_pane_ids_directly_below(&self, pane_id: &PaneId) -> Vec { + self.pane_ids_directly_below(pane_id) + .unwrap_or_default() + .into_iter() + .filter(|id| self.is_inside_viewport(id)) + .collect() + } + fn is_inside_viewport(&self, pane_id: &PaneId) -> bool { + is_inside_viewport(&self.viewport, self.panes.borrow().get(pane_id).unwrap()) + } + pub fn next_selectable_pane_id(&self, current_pane_id: &PaneId) -> PaneId { + let panes = self.panes.borrow(); + let mut panes: Vec<(&PaneId, &&mut Box)> = + panes.iter().filter(|(_, p)| p.selectable()).collect(); + panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { + if a_pane.y() == b_pane.y() { + a_pane.x().cmp(&b_pane.x()) + } else { + a_pane.y().cmp(&b_pane.y()) + } + }); + let active_pane_position = panes + .iter() + .position(|(id, _)| *id == current_pane_id) // TODO: better + .unwrap(); + + let next_active_pane_id = panes + .get(active_pane_position + 1) + .or_else(|| panes.get(0)) + .map(|p| *p.0) + .unwrap(); + next_active_pane_id + } + pub fn previous_selectable_pane_id(&self, current_pane_id: &PaneId) -> PaneId { + let panes = self.panes.borrow(); + let mut panes: Vec<(&PaneId, &&mut Box)> = + panes.iter().filter(|(_, p)| p.selectable()).collect(); + panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { + if a_pane.y() == b_pane.y() { + a_pane.x().cmp(&b_pane.x()) + } else { + a_pane.y().cmp(&b_pane.y()) + } + }); + let last_pane = panes.last().unwrap(); + let active_pane_position = panes + .iter() + .position(|(id, _)| *id == current_pane_id) // TODO: better + .unwrap(); + + let previous_active_pane_id = if active_pane_position == 0 { + *last_pane.0 + } else { + *panes.get(active_pane_position - 1).unwrap().0 + }; + previous_active_pane_id + } + pub fn next_selectable_pane_id_to_the_left(&self, current_pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let current_pane = panes.get(current_pane_id)?; + let panes: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let next_index = panes + .iter() + .enumerate() + .filter(|(_, (_, c))| { + c.is_directly_left_of(Box::as_ref(current_pane)) + && c.horizontally_overlaps_with(Box::as_ref(current_pane)) + }) + .max_by_key(|(_, (_, c))| c.active_at()) + .map(|(_, (pid, _))| pid) + .copied(); + next_index + } + pub fn next_selectable_pane_id_below(&self, current_pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let current_pane = panes.get(current_pane_id)?; + let panes: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let next_index = panes + .iter() + .enumerate() + .filter(|(_, (_, c))| { + c.is_directly_below(Box::as_ref(current_pane)) + && c.vertically_overlaps_with(Box::as_ref(current_pane)) + }) + .max_by_key(|(_, (_, c))| c.active_at()) + .map(|(_, (pid, _))| pid) + .copied(); + next_index + } + pub fn next_selectable_pane_id_above(&self, current_pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let current_pane = panes.get(current_pane_id)?; + let panes: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let next_index = panes + .iter() + .enumerate() + .filter(|(_, (_, c))| { + c.is_directly_above(Box::as_ref(current_pane)) + && c.vertically_overlaps_with(Box::as_ref(current_pane)) + }) + .max_by_key(|(_, (_, c))| c.active_at()) + .map(|(_, (pid, _))| pid) + .copied(); + next_index + } + pub fn next_selectable_pane_id_to_the_right(&self, current_pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let current_pane = panes.get(current_pane_id)?; + let panes: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let next_index = panes + .iter() + .enumerate() + .filter(|(_, (_, c))| { + c.is_directly_right_of(Box::as_ref(current_pane)) + && c.horizontally_overlaps_with(Box::as_ref(current_pane)) + }) + .max_by_key(|(_, (_, c))| c.active_at()) + .map(|(_, (pid, _))| pid) + .copied(); + next_index + } + fn horizontal_borders(&self, pane_ids: &[PaneId]) -> HashSet { + pane_ids.iter().fold(HashSet::new(), |mut borders, p| { + let panes = self.panes.borrow(); + let pane = panes.get(p).unwrap(); + borders.insert(pane.y()); + borders.insert(pane.y() + pane.rows()); + borders + }) + } + fn vertical_borders(&self, pane_ids: &[PaneId]) -> HashSet { + pane_ids.iter().fold(HashSet::new(), |mut borders, p| { + let panes = self.panes.borrow(); + let pane = panes.get(p).unwrap(); + borders.insert(pane.x()); + borders.insert(pane.x() + pane.cols()); + borders + }) + } + fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option> { + let panes = self.panes.borrow(); + if let Some(pane) = panes.get(&id) { + let upper_close_border = pane.y(); + let lower_close_border = pane.y() + pane.rows(); + + if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&id) { + let mut selectable_panes: Vec<_> = panes_to_the_left + .into_iter() + .filter(|pid| panes.get(pid).unwrap().selectable()) + .collect(); + let pane_borders_to_the_left = self.horizontal_borders(&selectable_panes); + if pane_borders_to_the_left.contains(&upper_close_border) + && pane_borders_to_the_left.contains(&lower_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_horizontal_borders( + t, + upper_close_border, + lower_close_border, + ) + }); + return Some(selectable_panes); + } + } + } + None + } + fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option> { + let panes = self.panes.borrow(); + if let Some(pane) = panes.get(&id) { + let upper_close_border = pane.y(); + let lower_close_border = pane.y() + pane.rows(); + + if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&id) { + let mut selectable_panes: Vec<_> = panes_to_the_right + .into_iter() + .filter(|pid| panes.get(pid).unwrap().selectable()) + .collect(); + let pane_borders_to_the_right = self.horizontal_borders(&selectable_panes); + if pane_borders_to_the_right.contains(&upper_close_border) + && pane_borders_to_the_right.contains(&lower_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_horizontal_borders( + t, + upper_close_border, + lower_close_border, + ) + }); + return Some(selectable_panes); + } + } + } + None + } + fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option> { + let panes = self.panes.borrow(); + if let Some(pane) = panes.get(&id) { + let left_close_border = pane.x(); + let right_close_border = pane.x() + pane.cols(); + + if let Some(panes_above) = self.pane_ids_directly_above(&id) { + let mut selectable_panes: Vec<_> = panes_above + .into_iter() + .filter(|pid| panes.get(pid).unwrap().selectable()) + .collect(); + let pane_borders_above = self.vertical_borders(&selectable_panes); + if pane_borders_above.contains(&left_close_border) + && pane_borders_above.contains(&right_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_vertical_borders( + t, + left_close_border, + right_close_border, + ) + }); + return Some(selectable_panes); + } + } + } + None + } + fn panes_below_between_aligning_borders(&self, id: PaneId) -> Option> { + let panes = self.panes.borrow(); + if let Some(pane) = panes.get(&id) { + let left_close_border = pane.x(); + let right_close_border = pane.x() + pane.cols(); + + if let Some(panes_below) = self.pane_ids_directly_below(&id) { + let mut selectable_panes: Vec<_> = panes_below + .into_iter() + .filter(|pid| panes[pid].selectable()) + .collect(); + let pane_borders_below = self.vertical_borders(&selectable_panes); + if pane_borders_below.contains(&left_close_border) + && pane_borders_below.contains(&right_close_border) + { + selectable_panes.retain(|t| { + self.pane_is_between_vertical_borders( + t, + left_close_border, + right_close_border, + ) + }); + return Some(selectable_panes); + } + } + } + None + } + fn find_panes_to_grow(&self, id: PaneId) -> Option<(Vec, Direction)> { + if let Some(panes) = self + .panes_to_the_left_between_aligning_borders(id) + .or_else(|| self.panes_to_the_right_between_aligning_borders(id)) + { + return Some((panes, Direction::Horizontal)); + } + + if let Some(panes) = self + .panes_above_between_aligning_borders(id) + .or_else(|| self.panes_below_between_aligning_borders(id)) + { + return Some((panes, Direction::Vertical)); + } + + None + } + fn grow_panes(&mut self, panes: &[PaneId], direction: Direction, (width, height): (f64, f64)) { + match direction { + Direction::Horizontal => { + for pane_id in panes { + self.increase_pane_width(pane_id, width); + } + } + Direction::Vertical => { + for pane_id in panes { + self.increase_pane_height(pane_id, height); + } + } + }; + } + pub fn fill_space_over_pane(&mut self, id: PaneId) -> bool { + // true => successfully filled space over pane + // false => didn't succeed, so didn't do anything + let (freed_width, freed_height) = { + let panes = self.panes.borrow_mut(); + let pane_to_close = panes.get(&id).unwrap(); + let freed_space = pane_to_close.position_and_size(); + let freed_width = freed_space.cols.as_percent().unwrap(); + let freed_height = freed_space.rows.as_percent().unwrap(); + (freed_width, freed_height) + }; + if let Some((panes_to_grow, direction)) = self.find_panes_to_grow(id) { + self.grow_panes(&panes_to_grow, direction, (freed_width, freed_height)); + let side_length = match direction { + Direction::Vertical => self.display_area.rows, + Direction::Horizontal => self.display_area.cols, + }; + { + let mut panes = self.panes.borrow_mut(); + (*panes).remove(&id); + } + let mut pane_resizer = PaneResizer::new(self.panes.clone()); + let _ = pane_resizer.layout(direction, side_length); + return true; + } + false + } + pub fn find_room_for_new_pane(&self) -> Option<(PaneId, Direction)> { + let panes = self.panes.borrow(); + let pane_sequence: Vec<(&PaneId, &&mut Box)> = + panes.iter().filter(|(_, p)| p.selectable()).collect(); + let (_largest_pane_size, pane_id_to_split) = pane_sequence.iter().fold( + (0, None), + |(current_largest_pane_size, current_pane_id_to_split), id_and_pane_to_check| { + let (id_of_pane_to_check, pane_to_check) = id_and_pane_to_check; + let pane_size = + (pane_to_check.rows() * CURSOR_HEIGHT_WIDTH_RATIO) * pane_to_check.cols(); + let pane_can_be_split = pane_to_check.cols() >= MIN_TERMINAL_WIDTH + && pane_to_check.rows() >= MIN_TERMINAL_HEIGHT + && ((pane_to_check.cols() > pane_to_check.min_width() * 2) + || (pane_to_check.rows() > pane_to_check.min_height() * 2)); + if pane_can_be_split && pane_size > current_largest_pane_size { + (pane_size, Some(*id_of_pane_to_check)) + } else { + (current_largest_pane_size, current_pane_id_to_split) + } + }, + ); + pane_id_to_split.and_then(|t_id_to_split| { + let pane_to_split = panes.get(t_id_to_split).unwrap(); + let direction = if pane_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO + > pane_to_split.cols() + && pane_to_split.rows() > pane_to_split.min_height() * 2 + { + Some(Direction::Horizontal) + } else if pane_to_split.cols() > pane_to_split.min_width() * 2 { + Some(Direction::Vertical) + } else { + None + }; + + direction.map(|direction| (*t_id_to_split, direction)) + }) + } +} + +pub fn split(direction: Direction, rect: &PaneGeom) -> Option<(PaneGeom, PaneGeom)> { + let space = match direction { + Direction::Vertical => rect.cols, + Direction::Horizontal => rect.rows, + }; + if let Some(p) = space.as_percent() { + let first_rect = match direction { + Direction::Vertical => PaneGeom { + cols: Dimension::percent(p / 2.0), + ..*rect + }, + Direction::Horizontal => PaneGeom { + rows: Dimension::percent(p / 2.0), + ..*rect + }, + }; + let second_rect = match direction { + Direction::Vertical => PaneGeom { + x: first_rect.x + 1, + cols: first_rect.cols, + ..*rect + }, + Direction::Horizontal => PaneGeom { + y: first_rect.y + 1, + rows: first_rect.rows, + ..*rect + }, + }; + Some((first_rect, second_rect)) + } else { + None + } +} diff --git a/zellij-server/src/ui/pane_resizer.rs b/zellij-server/src/tab/pane_resizer.rs similarity index 93% rename from zellij-server/src/ui/pane_resizer.rs rename to zellij-server/src/tab/pane_resizer.rs index 53455e3e4..7264c49a1 100644 --- a/zellij-server/src/ui/pane_resizer.rs +++ b/zellij-server/src/tab/pane_resizer.rs @@ -4,14 +4,16 @@ use cassowary::{ Expression, Solver, Variable, WeightedRelation::EQ, }; +use std::cell::RefCell; use std::collections::{HashMap, HashSet}; +use std::rc::Rc; use zellij_utils::{ input::layout::Direction, pane_size::{Constraint, Dimension, PaneGeom}, }; pub struct PaneResizer<'a> { - panes: HashMap<&'a PaneId, &'a mut Box>, + panes: Rc>>>, vars: HashMap, solver: Solver, } @@ -30,10 +32,9 @@ struct Span { type Grid = Vec>; impl<'a> PaneResizer<'a> { - pub fn new(panes: impl IntoIterator)>) -> Self { - let panes: HashMap<_, _> = panes.into_iter().collect(); + pub fn new(panes: Rc>>>) -> Self { let mut vars = HashMap::new(); - for &&k in panes.keys() { + for &k in panes.borrow().keys() { vars.insert(k, Variable::new()); } PaneResizer { @@ -122,8 +123,9 @@ impl<'a> PaneResizer<'a> { } fn apply_spans(&mut self, spans: Vec) { + let mut panes = self.panes.borrow_mut(); for span in spans { - let pane = self.panes.get_mut(&span.pid).unwrap(); + let pane = panes.get_mut(&span.pid).unwrap(); let new_geom = match span.direction { Direction::Horizontal => PaneGeom { x: span.pos, @@ -149,6 +151,7 @@ impl<'a> PaneResizer<'a> { // Select the spans running *perpendicular* to the direction of resize let spans: Vec = self .panes + .borrow() .values() .map(|p| self.get_span(!direction, p.as_ref())) .collect(); @@ -170,6 +173,7 @@ impl<'a> PaneResizer<'a> { let bwn = |v, (s, e)| s <= v && v < e; let mut spans: Vec<_> = self .panes + .borrow() .values() .filter(|p| { let s = self.get_span(!direction, p.as_ref()); @@ -186,7 +190,8 @@ impl<'a> PaneResizer<'a> { fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span { let pas = pane.current_geom(); - let size_var = self.vars[&pane.pid()]; + // let size_var = self.vars[&pane.pid()]; + let size_var = *self.vars.get(&pane.pid()).unwrap(); match direction { Direction::Horizontal => Span { pid: pane.pid(), diff --git a/zellij-server/src/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs similarity index 100% rename from zellij-server/src/unit/tab_tests.rs rename to zellij-server/src/tab/unit/tab_tests.rs diff --git a/zellij-server/src/ui/mod.rs b/zellij-server/src/ui/mod.rs index 1bf630712..71cdaab5d 100644 --- a/zellij-server/src/ui/mod.rs +++ b/zellij-server/src/ui/mod.rs @@ -2,4 +2,3 @@ pub mod boundaries; pub mod overlay; pub mod pane_boundaries_frame; pub mod pane_contents_and_ui; -pub mod pane_resizer;