summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrooks J Rady <b.j.rady@gmail.com>2021-05-29 23:12:11 +0100
committerBrooks J Rady <b.j.rady@gmail.com>2021-05-29 23:12:11 +0100
commitf2c5ee44f7f2c61f50b043bd8e55f915a3667fce (patch)
tree3e0ab2bb70869352c47085cc857b6e86becb840a
parentfe299325eb54e6642d27a417a3922a757b4390e4 (diff)
Getting back to where we started... (Buggy Resizing)
-rw-r--r--Cargo.lock7
-rw-r--r--default-plugins/status-bar/src/main.rs2
-rw-r--r--default-plugins/tab-bar/src/main.rs2
-rw-r--r--zellij-server/Cargo.toml1
-rw-r--r--zellij-server/src/lib.rs2
-rw-r--r--zellij-server/src/panes/plugin_pane.rs32
-rw-r--r--zellij-server/src/panes/terminal_pane.rs27
-rw-r--r--zellij-server/src/screen.rs16
-rw-r--r--zellij-server/src/tab.rs40
-rw-r--r--zellij-server/src/ui/layout.rs29
-rw-r--r--zellij-server/src/ui/pane_resizer.rs680
-rw-r--r--zellij-server/src/wasm_vm.rs22
-rw-r--r--zellij-tile/src/shim.rs11
-rw-r--r--zellij-utils/src/errors.rs3
-rw-r--r--zellij-utils/src/pane_size.rs7
15 files changed, 307 insertions, 574 deletions
diff --git a/Cargo.lock b/Cargo.lock
index dc862d534..31e038a0d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -272,6 +272,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
name = "cc"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2345,6 +2351,7 @@ version = "0.13.0"
dependencies = [
"ansi_term 0.12.1",
"async-trait",
+ "cassowary",
"daemonize",
"insta",
"serde_json",
diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs
index cf9445d6a..83eb50c9a 100644
--- a/default-plugins/status-bar/src/main.rs
+++ b/default-plugins/status-bar/src/main.rs
@@ -135,7 +135,7 @@ impl ZellijPlugin for State {
fn load(&mut self) {
set_selectable(false);
set_invisible_borders(true);
- set_max_height(2);
+ set_fixed_height(2);
subscribe(&[EventType::ModeUpdate]);
}
diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs
index 46336be3d..7ff639e85 100644
--- a/default-plugins/tab-bar/src/main.rs
+++ b/default-plugins/tab-bar/src/main.rs
@@ -26,7 +26,7 @@ impl ZellijPlugin for State {
fn load(&mut self) {
set_selectable(false);
set_invisible_borders(true);
- set_max_height(1);
+ set_fixed_height(1);
subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]);
}
diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml
index 1168dac47..b04bd04d5 100644
--- a/zellij-server/Cargo.toml
+++ b/zellij-server/Cargo.toml
@@ -16,6 +16,7 @@ serde_json = "1.0"
unicode-width = "0.1.8"
wasmer = "1.0.0"
wasmer-wasi = "1.0.0"
+cassowary = "0.3.0"
zellij-utils = { path = "../zellij-utils/", version = "0.13.0" }
[dev-dependencies]
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 76b4b7b18..6e6392c2e 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -112,6 +112,8 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
daemonize::Daemonize::new()
.working_directory(std::env::current_dir().unwrap())
.umask(0o077)
+ // FIXME: My cherished `dbg!` was broken, so this is a hack to bring it back
+ .stderr(std::fs::File::create("dbg.log").unwrap())
.start()
.expect("could not daemonize the server process");
diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs
index e5c5e15de..b08d528fa 100644
--- a/zellij-server/src/panes/plugin_pane.rs
+++ b/zellij-server/src/panes/plugin_pane.rs
@@ -16,8 +16,6 @@ pub(crate) struct PluginPane {
pub position_and_size: PositionAndSize,
pub position_and_size_override: Option<PositionAndSize>,
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
- pub max_height: Option<usize>,
- pub max_width: Option<usize>,
pub active_at: Instant,
}
@@ -35,8 +33,6 @@ impl PluginPane {
position_and_size,
position_and_size_override: None,
send_plugin_instructions,
- max_height: None,
- max_width: None,
active_at: Instant::now(),
}
}
@@ -95,7 +91,9 @@ impl Pane for PluginPane {
fn adjust_input_to_terminal(&self, _input_bytes: Vec<u8>) -> Vec<u8> {
unimplemented!() // FIXME: Shouldn't need this implmented?
}
-
+ fn position_and_size(&self) -> PositionAndSize {
+ self.position_and_size
+ }
fn position_and_size_override(&self) -> Option<PositionAndSize> {
self.position_and_size_override
}
@@ -114,11 +112,13 @@ impl Pane for PluginPane {
fn set_invisible_borders(&mut self, invisible_borders: bool) {
self.invisible_borders = invisible_borders;
}
- fn set_max_height(&mut self, max_height: usize) {
- self.max_height = Some(max_height);
+ fn set_fixed_height(&mut self, fixed_height: usize) {
+ self.position_and_size.rows = fixed_height;
+ self.position_and_size.rows_fixed = true;
}
- fn set_max_width(&mut self, max_width: usize) {
- self.max_width = Some(max_width);
+ fn set_fixed_width(&mut self, fixed_width: usize) {
+ self.position_and_size.columns = fixed_width;
+ self.position_and_size.cols_fixed = true;
}
fn render(&mut self) -> Option<String> {
// if self.should_render {
@@ -204,11 +204,21 @@ impl Pane for PluginPane {
fn clear_scroll(&mut self) {
unimplemented!()
}
+ // FIXME: This need to be reevaluated and deleted if possible.
+ // `max` doesn't make sense when things are fixed...
fn max_height(&self) -> Option<usize> {
- self.max_height
+ if self.position_and_size.rows_fixed {
+ Some(self.position_and_size.rows)
+ } else {
+ None
+ }
}
fn max_width(&self) -> Option<usize> {
- self.max_width
+ if self.position_and_size.cols_fixed {
+ Some(self.position_and_size.columns)
+ } else {
+ None
+ }
}
fn invisible_borders(&self) -> bool {
self.invisible_borders
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 32910618d..2dbd11a02 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -27,8 +27,6 @@ pub struct TerminalPane {
pub selectable: bool,
pub position_and_size: PositionAndSize,
pub position_and_size_override: Option<PositionAndSize>,
- pub max_height: Option<usize>,
- pub max_width: Option<usize>,
pub active_at: Instant,
pub colors: Palette,
vte_parser: vte::Parser,
@@ -52,8 +50,7 @@ impl Pane for TerminalPane {
self.reflow_lines();
}
fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
- self.position_and_size.columns = position_and_size.columns;
- self.position_and_size.rows = position_and_size.rows;
+ self.position_and_size = *position_and_size;
self.reflow_lines();
}
fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
@@ -119,7 +116,9 @@ impl Pane for TerminalPane {
};
input_bytes
}
-
+ fn position_and_size(&self) -> PositionAndSize {
+ self.position_and_size
+ }
fn position_and_size_override(&self) -> Option<PositionAndSize> {
self.position_and_size_override
}
@@ -135,21 +134,17 @@ impl Pane for TerminalPane {
fn set_selectable(&mut self, selectable: bool) {
self.selectable = selectable;
}
- fn set_max_height(&mut self, max_height: usize) {
- self.max_height = Some(max_height);
+ fn set_fixed_height(&mut self, fixed_height: usize) {
+ self.position_and_size.rows = fixed_height;
+ self.position_and_size.rows_fixed = true;
}
- fn set_max_width(&mut self, max_width: usize) {
- self.max_width = Some(max_width);
+ fn set_fixed_width(&mut self, fixed_width: usize) {
+ self.position_and_size.columns = fixed_width;
+ self.position_and_size.cols_fixed = true;
}
fn set_invisible_borders(&mut self, _invisible_borders: bool) {
unimplemented!();
}
- fn max_height(&self) -> Option<usize> {
- self.max_height
- }
- fn max_width(&self) -> Option<usize> {
- self.max_width
- }
fn render(&mut self) -> Option<String> {
if self.should_render() {
let mut vte_output = String::new();
@@ -294,8 +289,6 @@ impl TerminalPane {
selectable: true,
position_and_size,
position_and_size_override: None,
- max_height: None,
- max_width: None,
vte_parser: vte::Parser::new(),
active_at: Instant::now(),
colors: palette,
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index 48be44bfc..ab34dfaf5 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -55,7 +55,8 @@ pub(crate) enum ScreenInstruction {
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
SetSelectable(PaneId, bool),
- SetMaxHeight(PaneId, usize),
+ SetFixedHeight(PaneId, usize),
+ SetFixedWidth(PaneId, usize),
SetInvisibleBorders(PaneId, bool),
ClosePane(PaneId),
ApplyLayout(Layout, Vec<RawFd>),
@@ -106,7 +107,8 @@ impl From<&ScreenInstruction> for ScreenContext {
}
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders,
- ScreenInstruction::SetMaxHeight(..) => ScreenContext::SetMaxHeight,
+ ScreenInstruction::SetFixedHeight(..) => ScreenContext::SetFixedHeight,
+ ScreenInstruction::SetFixedWidth(..) => ScreenContext::SetFixedWidth,
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
@@ -574,11 +576,17 @@ pub(crate) fn screen_thread_main(
.unwrap()
.set_pane_selectable(id, selectable);
}
- ScreenInstruction::SetMaxHeight(id, max_height) => {
+ ScreenInstruction::SetFixedHeight(id, fixed_height) => {
screen
.get_active_tab_mut()
.unwrap()
- .set_pane_max_height(id, max_height);
+ .set_pane_fixed_height(id, fixed_height);
+ }
+ ScreenInstruction::SetFixedWidth(id, fixed_width) => {
+ screen
+ .get_active_tab_mut()
+ .unwrap()
+ .set_pane_fixed_width(id, fixed_width);
}
ScreenInstruction::SetInvisibleBorders(id, invisible_borders) => {
screen
diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs
index 7daf20925..45f386c72 100644
--- a/zellij-server/src/tab.rs
+++ b/zellij-server/src/tab.rs
@@ -104,15 +104,15 @@ pub trait Pane {
fn handle_pty_bytes(&mut self, bytes: VteBytes);
fn cursor_coordinates(&self) -> Option<(usize, usize)>;
fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8>;
-
+ fn position_and_size(&self) -> PositionAndSize;
fn position_and_size_override(&self) -> Option<PositionAndSize>;
fn should_render(&self) -> bool;
fn set_should_render(&mut self, should_render: bool);
fn selectable(&self) -> bool;
fn set_selectable(&mut self, selectable: bool);
fn set_invisible_borders(&mut self, invisible_borders: bool);
- fn set_max_height(&mut self, max_height: usize);
- fn set_max_width(&mut self, max_width: usize);
+ fn set_fixed_height(&mut self, fixed_height: usize);
+ fn set_fixed_width(&mut self, fixed_width: usize);
fn render(&mut self) -> Option<String>;
fn pid(&self) -> PaneId;
fn reduce_height_down(&mut self, count: usize);
@@ -178,15 +178,6 @@ pub trait Pane {
std::cmp::min(self.x() + self.columns(), other.x() + other.columns())
- std::cmp::max(self.x(), other.x())
}
- fn position_and_size(&self) -> PositionAndSize {
- PositionAndSize {
- x: self.x(),
- y: self.y(),
- columns: self.columns(),
- rows: self.rows(),
- ..Default::default()
- }
- }
fn can_increase_height_by(&self, increase_by: usize) -> bool {
self.max_height()
.map(|max_height| self.rows() + increase_by <= max_height)
@@ -294,12 +285,6 @@ impl Tab {
match positions_and_size.next() {
Some((_, position_and_size)) => {
terminal_pane.reset_size_and_position_override();
- if let Some(max_rows) = position_and_size.max_rows {
- terminal_pane.set_max_height(max_rows);
- }
- if let Some(max_columns) = position_and_size.max_columns {
- terminal_pane.set_max_width(max_columns);
- }
terminal_pane.change_pos_and_size(&position_and_size);
self.os_api.set_terminal_size_using_fd(
*pid,
@@ -317,24 +302,18 @@ impl Tab {
}
let mut new_pids = new_pids.iter();
for (layout, position_and_size) in positions_and_size {
- // Just a regular terminal
+ // A plugin pane
if let Some(plugin) = &layout.plugin {
let (pid_tx, pid_rx) = channel();
self.senders
.send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone()))
.unwrap();
let pid = pid_rx.recv().unwrap();
- let mut new_plugin = PluginPane::new(
+ let new_plugin = PluginPane::new(
pid,
*position_and_size,
self.senders.to_plugin.as_ref().unwrap().clone(),
);
- if let Some(max_rows) = position_and_size.max_rows {
- new_plugin.set_max_height(max_rows);
- }
- if let Some(max_columns) = position_and_size.max_columns {
- new_plugin.set_max_width(max_columns);
- }
self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin));
// Send an initial mode update to the newly loaded plugin only!
self.senders
@@ -2121,9 +2100,14 @@ impl Tab {
pane.set_invisible_borders(invisible_borders);
}
}
- pub fn set_pane_max_height(&mut self, id: PaneId, max_height: usize) {
+ pub fn set_pane_fixed_height(&mut self, id: PaneId, fixed_height: usize) {
+ if let Some(pane) = self.panes.get_mut(&id) {
+ pane.set_fixed_height(fixed_height);
+ }
+ }
+ pub fn set_pane_fixed_width(&mut self, id: PaneId, fixed_width: usize) {
if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_max_height(max_height);
+ pane.set_fixed_width(fixed_width);
}
}
pub fn close_pane(&mut self, id: PaneId) {
diff --git a/zellij-server/src/ui/layout.rs b/zellij-server/src/ui/layout.rs
index a9af665fa..7a19d889a 100644
--- a/zellij-server/src/ui/layout.rs
+++ b/zellij-server/src/ui/layout.rs
@@ -19,17 +19,14 @@ fn split_space_to_parts_vertically(
// First fit in the parameterized sizes
for size in sizes {
- let (columns, max_columns) = match size {
+ let columns = match size {
Some(SplitSize::Percent(percent)) => {
- ((max_width as f32 * (percent as f32 / 100.0)) as usize, None)
+ (max_width as f32 * (percent as f32 / 100.0)) as usize
} // TODO: round properly
- Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)),
+ Some(SplitSize::Fixed(size)) => size as usize,
None => {
parts_to_grow.push(current_x_position);
- (
- 1, // This is grown later on
- None,
- )
+ 1 // This is grown later on
}
};
split_parts.push(PositionAndSize {
@@ -37,7 +34,6 @@ fn split_space_to_parts_vertically(
y: space_to_split.y,
columns,
rows: space_to_split.rows,
- max_columns,
..Default::default()
});
current_width += columns;
@@ -87,18 +83,14 @@ fn split_space_to_parts_horizontally(
let mut parts_to_grow = Vec::new();
for size in sizes {
- let (rows, max_rows) = match size {
- Some(SplitSize::Percent(percent)) => (
- (max_height as f32 * (percent as f32 / 100.0)) as usize,
- None,
- ), // TODO: round properly
- Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)),
+ let rows = match size {
+ Some(SplitSize::Percent(percent)) => {
+ (max_height as f32 * (percent as f32 / 100.0)) as usize
+ } // TODO: round properly
+ Some(SplitSize::Fixed(size)) => size as usize,
None => {
parts_to_grow.push(current_y_position);
- (
- 1, // This is grown later on
- None,
- )
+ 1 // This is grown later on
}
};
split_parts.push(PositionAndSize {
@@ -106,7 +98,6 @@ fn split_space_to_parts_horizontally(
y: current_y_position,
columns: space_to_split.columns,
rows,
- max_rows,
..Default::default()
});
current_height += rows;
diff --git a/zellij-server/src/ui/pane_resizer.rs b/zellij-server/src/ui/pane_resizer.rs
index b2001945e..73673a9c5 100644
--- a/zellij-server/src/ui/pane_resizer.rs
+++ b/zellij-server/src/ui/pane_resizer.rs
@@ -1,531 +1,247 @@
use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane};
+use cassowary::{
+ strength::{REQUIRED, STRONG},
+ Constraint, Solver, Variable,
+ WeightedRelation::*,
+};
use std::{
- cmp::Ordering,
collections::{BTreeMap, HashSet},
+ ops::Not,
};
use zellij_utils::pane_size::PositionAndSize;
-pub(crate) struct PaneResizer<'a> {
+const GAP_SIZE: usize = 1; // Panes are separated by this number of rows / columns
+
+pub struct PaneResizer<'a> {
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
+ vars: BTreeMap<PaneId, (Variable, Variable)>,
+ solver: Solver,
os_api: &'a mut Box<dyn ServerOsApi>,
}
+#[derive(Debug, Clone, Copy)]
+enum Direction {
+ Horizontal,
+ Vertical,
+}
+
+impl Not for Direction {
+ type Output = Self;
+
+ fn not(self) -> Self::Output {
+ match self {
+ Direction::Horizontal => Direction::Vertical,
+ Direction::Vertical => Direction::Horizontal,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+struct Span {
+ pid: PaneId,
+ direction: Direction,
+ fixed: bool,
+ pos: usize,
+ size: usize,
+ pos_var: Variable,
+ size_var: Variable,
+}
+
// TODO: currently there are some functions here duplicated with Tab
// all resizing functions should move here
+// FIXME:
+// 1. Rounding causes a loss of ratios, I need to store an internal f64 for
+// each pane as well as the displayed usize and add custom rounding logic.
+// 2. Vertical resizing doesn't seem to respect the space consumed by the tab
+// and status bars?
+// 3. A 2x2 layout and simultaneous vertical + horizontal resizing sometimes
+// leads to unsolvable constraints? Maybe related to 2 (and possibly 1).
+// I should sanity-check the `spans_in_boundary()` here!
+
impl<'a> PaneResizer<'a> {
pub fn new(
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
os_api: &'a mut Box<dyn ServerOsApi>,
) -> Self {
- PaneResizer { panes, os_api }
+ let mut vars = BTreeMap::new();
+ for &k in panes.keys() {
+ vars.insert(k, (Variable::new(), Variable::new()));
+ }
+ PaneResizer {
+ panes,
+ vars,
+ solver: Solver::new(),
+ os_api,
+ }
}
+
pub fn resize(
&mut self,
- mut current_size: PositionAndSize,
+ current_size: PositionAndSize,
new_size: PositionAndSize,
) -> Option<(isize, isize)> {
- // (column_difference, row_difference)
- let mut successfully_resized = false;
- let mut column_difference: isize = 0;
- let mut row_difference: isize = 0;
- match new_size.columns.cmp(&current_size.columns) {
- Ordering::Greater => {
- let increase_by = new_size.columns - current_size.columns;
- if let Some(panes_to_resize) = find_increasable_vertical_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.increase_panes_right_and_push_adjacents_right(
- panes_to_resize,
- increase_by,
- );
- column_difference = new_size.columns as isize - current_size.columns as isize;
- current_size.columns =
- (current_size.columns as isize + column_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Less => {
- let reduce_by = current_size.columns - new_size.columns;
- if let Some(panes_to_resize) = find_reducible_vertical_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by);
- column_difference = new_size.columns as isize - current_size.columns as isize;
- current_size.columns =
- (current_size.columns as isize + column_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Equal => (),
- }
- match new_size.rows.cmp(&current_size.rows) {
- Ordering::Greater => {
- let increase_by = new_size.rows - current_size.rows;
- if let Some(panes_to_resize) = find_increasable_horizontal_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by);
- row_difference = new_size.rows as isize - current_size.rows as isize;
- current_size.rows = (current_size.rows as isize + row_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Less => {
- let reduce_by = current_size.rows - new_size.rows;
- if let Some(panes_to_resize) = find_reducible_horizontal_chain(