summaryrefslogtreecommitdiffstats
path: root/zellij-server/src/ui
diff options
context:
space:
mode:
authorKunal Mohan <kunalmohan99@gmail.com>2021-05-16 16:42:50 +0530
committerKunal Mohan <kunalmohan99@gmail.com>2021-05-16 21:41:56 +0530
commit2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch)
tree5a332470e32b380816098594a89d3c3db1f6eb53 /zellij-server/src/ui
parent28212f54309d09263c6daa1801d49e8b4311be12 (diff)
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'zellij-server/src/ui')
-rw-r--r--zellij-server/src/ui/boundaries.rs630
-rw-r--r--zellij-server/src/ui/layout.rs237
-rw-r--r--zellij-server/src/ui/mod.rs3
-rw-r--r--zellij-server/src/ui/pane_resizer.rs531
4 files changed, 1401 insertions, 0 deletions
diff --git a/zellij-server/src/ui/boundaries.rs b/zellij-server/src/ui/boundaries.rs
new file mode 100644
index 000000000..984b13762
--- /dev/null
+++ b/zellij-server/src/ui/boundaries.rs
@@ -0,0 +1,630 @@
+use crate::tab::Pane;
+use ansi_term::Colour::{Fixed, RGB};
+use std::collections::HashMap;
+use zellij_tile::data::{InputMode, Palette, PaletteColor};
+use zellij_utils::shared::colors;
+
+use std::fmt::{Display, Error, Formatter};
+pub mod boundary_type {
+ pub const TOP_RIGHT: &str = "┐";
+ pub const VERTICAL: &str = "│";
+ pub const HORIZONTAL: &str = "─";
+ pub const TOP_LEFT: &str = "┌";
+ pub const BOTTOM_RIGHT: &str = "┘";
+ pub const BOTTOM_LEFT: &str = "└";
+ pub const VERTICAL_LEFT: &str = "┤";
+ pub const VERTICAL_RIGHT: &str = "├";
+ pub const HORIZONTAL_DOWN: &str = "┬";
+ pub const HORIZONTAL_UP: &str = "┴";
+ pub const CROSS: &str = "┼";
+}
+
+pub(crate) type BoundaryType = &'static str; // easy way to refer to boundary_type above
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct BoundarySymbol {
+ boundary_type: BoundaryType,
+ invisible: bool,
+ color: Option<PaletteColor>,
+}
+
+impl BoundarySymbol {
+ pub fn new(boundary_type: BoundaryType) -> Self {
+ BoundarySymbol {
+ boundary_type,
+ invisible: false,
+ color: Some(PaletteColor::EightBit(colors::GRAY)),
+ }
+ }
+ pub fn invisible(mut self) -> Self {
+ self.invisible = true;
+ self
+ }
+ pub fn color(&mut self, color: Option<PaletteColor>) -> Self {
+ self.color = color;
+ *self
+ }
+}
+
+impl Display for BoundarySymbol {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
+ match self.invisible {
+ true => write!(f, " "),
+ false => match self.color {
+ Some(color) => match color {
+ PaletteColor::Rgb((r, g, b)) => {
+ write!(f, "{}", RGB(r, g, b).paint(self.boundary_type))
+ }
+ PaletteColor::EightBit(color) => {
+ write!(f, "{}", Fixed(color).paint(self.boundary_type))
+ }
+ },
+ None => write!(f, "{}", self.boundary_type),
+ },
+ }
+ }
+}
+
+fn combine_symbols(
+ current_symbol: BoundarySymbol,
+ next_symbol: BoundarySymbol,
+) -> Option<BoundarySymbol> {
+ use boundary_type::*;
+ let invisible = current_symbol.invisible || next_symbol.invisible;
+ let color = current_symbol.color.or(next_symbol.color);
+ match (current_symbol.boundary_type, next_symbol.boundary_type) {
+ (CROSS, _) | (_, CROSS) => {
+ // (┼, *) or (*, ┼) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_RIGHT, TOP_RIGHT) => {
+ // (┐, ┐) => Some(┐)
+ let boundary_type = TOP_RIGHT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_RIGHT, VERTICAL) | (TOP_RIGHT, BOTTOM_RIGHT) | (TOP_RIGHT, VERTICAL_LEFT) => {
+ // (┐, │) => Some(┤)
+ // (┐, ┘) => Some(┤)
+ // (─, ┤) => Some(┤)
+ let boundary_type = VERTICAL_LEFT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_RIGHT, HORIZONTAL) | (TOP_RIGHT, TOP_LEFT) | (TOP_RIGHT, HORIZONTAL_DOWN) => {
+ // (┐, ─) => Some(┬)
+ // (┐, ┌) => Some(┬)
+ // (┐, ┬) => Some(┬)
+ let boundary_type = HORIZONTAL_DOWN;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_RIGHT, BOTTOM_LEFT) | (TOP_RIGHT, VERTICAL_RIGHT) | (TOP_RIGHT, HORIZONTAL_UP) => {
+ // (┐, └) => Some(┼)
+ // (┐, ├) => Some(┼)
+ // (┐, ┴) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL, HORIZONTAL) => {
+ // (─, ─) => Some(─)
+ let boundary_type = HORIZONTAL;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL, VERTICAL) | (HORIZONTAL, VERTICAL_LEFT) | (HORIZONTAL, VERTICAL_RIGHT) => {
+ // (─, │) => Some(┼)
+ // (─, ┤) => Some(┼)
+ // (─, ├) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL, TOP_LEFT) | (HORIZONTAL, HORIZONTAL_DOWN) => {
+ // (─, ┌) => Some(┬)
+ // (─, ┬) => Some(┬)
+ let boundary_type = HORIZONTAL_DOWN;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL, BOTTOM_RIGHT) | (HORIZONTAL, BOTTOM_LEFT) | (HORIZONTAL, HORIZONTAL_UP) => {
+ // (─, ┘) => Some(┴)
+ // (─, └) => Some(┴)
+ // (─, ┴) => Some(┴)
+ let boundary_type = HORIZONTAL_UP;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL, VERTICAL) => {
+ // (│, │) => Some(│)
+ let boundary_type = VERTICAL;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL, TOP_LEFT) | (VERTICAL, BOTTOM_LEFT) | (VERTICAL, VERTICAL_RIGHT) => {
+ // (│, ┌) => Some(├)
+ // (│, └) => Some(├)
+ // (│, ├) => Some(├)
+ let boundary_type = VERTICAL_RIGHT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL, BOTTOM_RIGHT) | (VERTICAL, VERTICAL_LEFT) => {
+ // (│, ┘) => Some(┤)
+ // (│, ┤) => Some(┤)
+ let boundary_type = VERTICAL_LEFT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL, HORIZONTAL_DOWN) | (VERTICAL, HORIZONTAL_UP) => {
+ // (│, ┬) => Some(┼)
+ // (│, ┴) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_LEFT, TOP_LEFT) => {
+ // (┌, ┌) => Some(┌)
+ let boundary_type = TOP_LEFT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_LEFT, BOTTOM_RIGHT) | (TOP_LEFT, VERTICAL_LEFT) | (TOP_LEFT, HORIZONTAL_UP) => {
+ // (┌, ┘) => Some(┼)
+ // (┌, ┤) => Some(┼)
+ // (┌, ┴) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_LEFT, BOTTOM_LEFT) | (TOP_LEFT, VERTICAL_RIGHT) => {
+ // (┌, └) => Some(├)
+ // (┌, ├) => Some(├)
+ let boundary_type = VERTICAL_RIGHT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (TOP_LEFT, HORIZONTAL_DOWN) => {
+ // (┌, ┬) => Some(┬)
+ let boundary_type = HORIZONTAL_DOWN;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_RIGHT, BOTTOM_RIGHT) => {
+ // (┘, ┘) => Some(┘)
+ let boundary_type = BOTTOM_RIGHT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_RIGHT, BOTTOM_LEFT) | (BOTTOM_RIGHT, HORIZONTAL_UP) => {
+ // (┘, └) => Some(┴)
+ // (┘, ┴) => Some(┴)
+ let boundary_type = HORIZONTAL_UP;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_RIGHT, VERTICAL_LEFT) => {
+ // (┘, ┤) => Some(┤)
+ let boundary_type = VERTICAL_LEFT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_RIGHT, VERTICAL_RIGHT) | (BOTTOM_RIGHT, HORIZONTAL_DOWN) => {
+ // (┘, ├) => Some(┼)
+ // (┘, ┬) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_LEFT, BOTTOM_LEFT) => {
+ // (└, └) => Some(└)
+ let boundary_type = BOTTOM_LEFT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_LEFT, VERTICAL_LEFT) | (BOTTOM_LEFT, HORIZONTAL_DOWN) => {
+ // (└, ┤) => Some(┼)
+ // (└, ┬) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_LEFT, VERTICAL_RIGHT) => {
+ // (└, ├) => Some(├)
+ let boundary_type = VERTICAL_RIGHT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (BOTTOM_LEFT, HORIZONTAL_UP) => {
+ // (└, ┴) => Some(┴)
+ let boundary_type = HORIZONTAL_UP;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL_LEFT, VERTICAL_LEFT) => {
+ // (┤, ┤) => Some(┤)
+ let boundary_type = VERTICAL_LEFT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL_LEFT, VERTICAL_RIGHT)
+ | (VERTICAL_LEFT, HORIZONTAL_DOWN)
+ | (VERTICAL_LEFT, HORIZONTAL_UP) => {
+ // (┤, ├) => Some(┼)
+ // (┤, ┬) => Some(┼)
+ // (┤, ┴) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL_RIGHT, VERTICAL_RIGHT) => {
+ // (├, ├) => Some(├)
+ let boundary_type = VERTICAL_RIGHT;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (VERTICAL_RIGHT, HORIZONTAL_DOWN) | (VERTICAL_RIGHT, HORIZONTAL_UP) => {
+ // (├, ┬) => Some(┼)
+ // (├, ┴) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL_DOWN, HORIZONTAL_DOWN) => {
+ // (┬, ┬) => Some(┬)
+ let boundary_type = HORIZONTAL_DOWN;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL_DOWN, HORIZONTAL_UP) => {
+ // (┬, ┴) => Some(┼)
+ let boundary_type = CROSS;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (HORIZONTAL_UP, HORIZONTAL_UP) => {
+ // (┴, ┴) => Some(┴)
+ let boundary_type = HORIZONTAL_UP;
+ Some(BoundarySymbol {
+ boundary_type,
+ invisible,
+ color,
+ })
+ }
+ (_, _) => combine_symbols(next_symbol, current_symbol),
+ }
+}
+
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub(crate) struct Coordinates {
+ x: usize,
+ y: usize,
+}
+
+impl Coordinates {
+ pub fn new(x: usize, y: usize) -> Self {
+ Coordinates { x, y }
+ }
+}
+
+pub(crate) trait Rect {
+ fn x(&self) -> usize;
+ fn y(&self) -> usize;
+ fn rows(&self) -> usize;
+ fn columns(&self) -> usize;
+ fn right_boundary_x_coords(&self) -> usize {
+ self.x() + self.columns()
+ }
+ fn bottom_boundary_y_coords(&self) -> usize {
+ self.y() + self.rows()
+ }
+ fn is_directly_right_of(&self, other: &Self) -> bool {
+ self.x() == other.x() + other.columns() + 1
+ }
+ fn is_directly_left_of(&self, other: &Self) -> bool {
+ self.x() + self.columns() + 1 == other.x()
+ }
+ fn is_directly_below(&self, other: &Self) -> bool {
+ self.y() == other.y() + other.rows() + 1
+ }
+ fn is_directly_above(&self, other: &Self) -> bool {
+ self.y() + self.rows() + 1 == other.y()
+ }
+ fn horizontally_overlaps_with(&self, other: &Self) -> bool {
+ (self.y() >= other.y() && self.y() <= (other.y() + other.rows()))
+ || ((self.y() + self.rows()) <= (other.y() + other.rows())
+ && (self.y() + self.rows()) > other.y())
+ || (self.y() <= other.y() && (self.y() + self.rows() >= (other.y() + other.rows())))
+ || (other.y() <= self.y() && (other.y() + other.rows() >= (self.y() + self.rows())))
+ }
+ fn get_horizontal_overlap_with(&self, other: &Self) -> usize {
+ std::cmp::min(self.y() + self.rows(), other.y() + other.rows())
+ - std::cmp::max(self.y(), other.y())
+ }
+ fn vertically_overlaps_with(&self, other: &Self) -> bool {
+ (self.x() >= other.x() && self.x() <= (other.x() + other.columns()))
+ || ((self.x() + self.columns()) <= (other.x() + other.columns())
+ && (self.x() + self.columns()) > other.x())
+ || (self.x() <= other.x()
+ && (self.x() + self.columns() >= (other.x() + other.columns())))
+ || (other.x() <= self.x()
+ && (other.x() + other.columns() >= (self.x() + self.columns())))
+ }
+ fn get_vertical_overlap_with(&self, other: &Self) -> usize {
+ std::cmp::min(self.x() + self.columns(), other.x() + other.columns())
+ - std::cmp::max(self.x(), other.x())
+ }
+}
+
+pub struct Boundaries {
+ columns: usize,
+ rows: usize,
+ // boundary_characters: HashMap<Coordinates, BoundaryType>,
+ boundary_characters: HashMap<Coordinates, BoundarySymbol>,
+}
+
+impl Boundaries {
+ pub fn new(columns: u16, rows: u16) -> Self {
+ let columns = columns as usize;
+ let rows = rows as usize;
+ Boundaries {
+ columns,
+ rows,
+ boundary_characters: HashMap::new(),
+ }
+ }
+ pub fn add_rect(&mut self, rect: &dyn Pane, input_mode: InputMode, palette: Option<Palette>) {
+ let color = match palette.is_some() {
+ true => match input_mode {
+ InputMode::Normal | InputMode::Locked => Some(palette.unwrap().green),
+ _ => Some(palette.unwrap().orange),
+ },
+ false => None,
+ };
+ if rect.x() > 0 {
+ let boundary_x_coords = rect.x() - 1;
+ let first_row_coordinates = self.rect_right_boundary_row_start(rect);
+ let last_row_coordinates = self.rect_right_boundary_row_end(rect);
+ for row in first_row_coordinates..last_row_coordinates {
+ let coordinates = Coordinates::new(boundary_x_coords, row);
+ let mut symbol_to_add = if row == first_row_coordinates && row != 0 {
+ BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
+ } else if row == last_row_coordinates - 1 && row != self.rows - 1 {
+ BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
+ } else {
+ BoundarySymbol::new(boundary_type::VERTICAL).color(color)
+ };
+ if rect.invisible_borders() {
+ symbol_to_add = symbol_to_add.invisible();
+ }
+ let next_symbol = self
+ .boundary_characters
+ .remove(&coordinates)
+ .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
+ .unwrap_or(symbol_to_add);
+ self.boundary_characters.insert(coordinates, next_symbol);
+ }
+ }
+ if rect.y() > 0 {
+ let boundary_y_coords = rect.y() - 1;
+ let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
+ let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
+ for col in first_col_coordinates..last_col_coordinates {
+ let coordinates = Coordinates::new(col, boundary_y_coords);
+ let mut symbol_to_add = if col == first_col_coordinates && col != 0 {
+ BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
+ } else if col == last_col_coordinates - 1 && col != self.columns - 1 {
+ BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
+ } else {
+ BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
+ };
+ if rect.invisible_borders() {
+ symbol_to_add = symbol_to_add.invisible();
+ }
+ let next_symbol = self
+ .boundary_characters
+ .remove(&coordinates)
+ .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
+ .unwrap_or(symbol_to_add);
+ self.boundary_characters.insert(coordinates, next_symbol);
+ }
+ }
+ if self.rect_right_boundary_is_before_screen_edge(rect) {
+ // let boundary_x_coords = self.rect_right_boundary_x_coords(rect);
+ let boundary_x_coords = rect.right_boundary_x_coords();
+ let first_row_coordinates = self.rect_right_boundary_row_start(rect);
+ let last_row_coordinates = self.rect_right_boundary_row_end(rect);
+ for row in first_row_coordinates..last_row_coordinates {
+ let coordinates = Coordinates::new(boundary_x_coords, row);
+ let mut symbol_to_add = if row == first_row_coordinates && row != 0 {
+ BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
+ } else if row == last_row_coordinates - 1 && row != self.rows - 1 {
+ BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
+ } else {
+ BoundarySymbol::new(boundary_type::VERTICAL).color(color)
+ };
+ if rect.invisible_borders() {
+ symbol_to_add = symbol_to_add.invisible();
+ }
+ let next_symbol = self
+ .boundary_characters
+ .remove(&coordinates)
+ .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
+ .unwrap_or(symbol_to_add);
+ self.boundary_characters.insert(coordinates, next_symbol);
+ }
+ }
+ if self.rect_bottom_boundary_is_before_screen_edge(rect) {
+ let boundary_y_coords = rect.bottom_boundary_y_coords();
+ let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
+ let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
+ for col in first_col_coordinates..last_col_coordinates {
+ let coordinates = Coordinates::new(col, boundary_y_coords);
+ let mut symbol_to_add = if col == first_col_coordinates && col != 0 {
+ BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
+ } else if col == last_col_coordinates - 1 && col != self.columns - 1 {
+ BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
+ } else {
+ BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
+ };
+ if rect.invisible_borders() {
+ symbol_to_add = symbol_to_add.invisible();
+ }
+ let next_symbol = self
+ .boundary_characters
+ .remove(&coordinates)
+ .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
+ .unwrap_or(symbol_to_add);
+ self.boundary_characters.insert(coordinates, next_symbol);
+ }
+ }
+ }
+ pub fn vte_output(&self) -> String {
+ let mut vte_output = String::new();
+ for (coordinates, boundary_character) in &self.boundary_characters {
+ vte_output.push_str(&format!(
+ "\u{1b}[{};{}H\u{1b}[m{}",
+ coordinates.y + 1,
+ coordinates.x + 1,
+ boundary_character
+ )); // goto row/col + boundary character
+ }
+ vte_output
+ }
+ fn rect_right_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
+ rect.x() + rect.columns() < self.columns
+ }
+ fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
+ rect.y() + rect.rows() < self.rows
+ }
+ fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize {
+ if rect.y() == 0 {
+ 0
+ } else {
+ rect.y() - 1
+ }
+ }
+ fn rect_right_boundary_row_end(&self, rect: &dyn Pane) -> usize {
+ let rect_bottom_row = rect.y() + rect.rows();
+ // we do this because unless we're on the screen edge, we'd like to go one extra row to
+ // connect to whatever boundary is beneath us
+ if rect_bottom_row == self.rows {
+ rect_bottom_row
+ } else {
+ rect_bottom_row + 1
+ }
+ }
+ fn rect_bottom_boundary_col_start(&self, rect: &dyn Pane) -> usize {
+ if rect.x() == 0 {
+ 0
+ } else {
+ rect.x() - 1
+ }
+ }
+ fn rect_bottom_boundary_col_end(&self, rect: &dyn Pane) -> usize {
+ let rect_right_col = rect.x() + rect.columns();
+ // we do this because unless we're on the screen edge, we'd like to go one extra column to
+ // connect to whatever boundary is right of us
+ if rect_right_col == self.columns {
+ rect_right_col
+ } else {
+ rect_right_col + 1
+ }
+ }
+}
diff --git a/zellij-server/src/ui/layout.rs b/zellij-server/src/ui/layout.rs
new file mode 100644
index 000000000..9edf50474
--- /dev/null
+++ b/zellij-server/src/ui/layout.rs
@@ -0,0 +1,237 @@
+use serde::{Deserialize, Serialize};
+use std::path::{Path, PathBuf};
+use std::{fs::File, io::prelude::*};
+
+use zellij_utils::pane_size::PositionAndSize;
+
+fn split_space_to_parts_vertically(
+ space_to_split: &PositionAndSize,
+ sizes: Vec<Option<SplitSize>>,
+) -> Vec<PositionAndSize> {
+ let mut split_parts = Vec::new();
+ let mut current_x_position = space_to_split.x;
+ let mut current_width = 0;
+ let max_width = space_to_split.columns - (sizes.len() - 1); // minus space for gaps
+
+ let mut parts_to_grow = Vec::new();
+
+ // First fit in the parameterized sizes
+ for size in sizes {
+ let (columns, max_columns) = match size {
+ Some(SplitSize::Percent(percent)) => {
+ ((max_width as f32 * (percent as f32 / 100.0)) as usize, None)
+ } // TODO: round properly
+ Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)),
+ None => {
+ parts_to_grow.push(current_x_position);
+ (
+ 1, // This is grown later on
+ None,
+ )
+ }
+ };
+ split_parts.push(PositionAndSize {
+ x: current_x_position,
+ y: space_to_split.y,
+ columns,
+ rows: space_to_split.rows,
+ max_columns,
+ ..Default::default()
+ });
+ current_width += columns;
+ current_x_position += columns + 1; // 1 for gap
+ }
+
+ if current_width > max_width {
+ panic!("Layout contained too many columns to fit onto the screen!");
+ }
+
+ let mut last_flexible_index = split_parts.len() - 1;
+ if let Some(new_columns) = (max_width - current_width).checked_div(parts_to_grow.len()) {
+ current_width = 0;
+ current_x_position = 0;
+ for (idx, part) in split_parts.iter_mut().enumerate() {
+ part.x = current_x_position;
+ if parts_to_grow.contains(&part.x) {