mirror of
https://github.com/zellij-org/zellij.git
synced 2024-11-22 13:02:12 +03:00
feat(ui): overhauled resize and layout systems
* refactor(panes): move to parametric pane sizes * Fixed the simpler errors by casting to usize * The least I can do is pass the formatting check... * Move to stable toolchain * Well, it compiles? * And now it doesn't! ;) * Baseline functionality with the new Dimension type * Working POC for percent-based resizing * REVERT THIS COMMIT – DELETES TESTS * Perfected the discrete resize algorithm * Fixed fixed-size panes * Basic bidirectional resize * feat(resize): finalised parametric resize algorithm * Reduce the logging level a bit * Fixed nested layouts using percents * Bug squishing for implicit sizing * Here is a funky (read: rubbish) rounding approach * And now it's gone again! * Improve discretisation algorithm to fix rounding errors * Fix the last layout bug (maybe?) * Mixed explicit and implied percents work now * Let's pretend that didn't happen... * Make things a bit less crashy * Crash slightly more for now (to find bugs) * Manaually splitting of panes works now * Start moving to percent-based resizes * Everything but fullscreen seems to be working * Fix compilatation errors * Culled a massive amount of border code * Why not pause to please rustfmt? * Turns out I was still missing a few tests... * Bringing back even more tests! * Fix tests and pane boarders * Fix the resize system without gaps * Fix content offset * Fixed a bug with pane closing * Add a hack to fix setting of the viewport * Fix toggling between shared borders and frames * fix(tests): make e2e properly use PaneGeom * style(fmt): make rustfmt happy * Revert unintentional rounding of borders * Purge some old borderless stuff * Fix busted tab-bar shrinking * Update E2E tests * Finish implementing fullscreen! * Don't crash anymore? * Fix (almost) all tests * Fix a lack of tab-stops * All tests passing * I really can't be bothered to debug a CI issue * Tie up loose ends * Knock out some lingering FIXMEs * Continue to clean things up * Change some naming and address FIXMEs * Cull more code + FIXMEs * Refactor of the resize system + polish * Only draw frames when absolutely necessary * Fix the tab-bar crash * Fix rendering of boarders on reattach * Fix resizing at small pane sizes * Deduplicate code in the layout system * Update tab-bar WASM * Fixed the pinching of panes during resize * Unexpose needlessly public type * Add back a lost test * Re-add tab tests and get them to compile * All tabs need layouts * Start fixing tests + bug in main * Stabilize the resize algorithm rounding * All tests from main are now passing * Cull more dead code
This commit is contained in:
parent
1544de2665
commit
76a5bc8a05
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -4,11 +4,11 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.15.2"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
|
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli 0.24.0",
|
"gimli 0.25.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -227,16 +227,16 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.60"
|
version = "0.3.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282"
|
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object 0.25.3",
|
"object 0.26.0",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -715,9 +715,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.4.1"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb"
|
checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
@ -736,9 +736,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27"
|
checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -751,9 +751,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2"
|
checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@ -761,15 +761,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1"
|
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-executor"
|
name = "futures-executor"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79"
|
checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@ -778,9 +778,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1"
|
checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-lite"
|
name = "futures-lite"
|
||||||
@ -799,9 +799,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
|
checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
@ -812,21 +812,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282"
|
checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae"
|
checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
|
checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@ -897,9 +897,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.24.0"
|
version = "0.25.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
|
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo-timers"
|
name = "gloo-timers"
|
||||||
@ -1325,9 +1325,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.25.3"
|
version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7"
|
checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -1500,9 +1500,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.27"
|
version = "1.0.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
@ -1966,9 +1966,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.73"
|
version = "1.0.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -57,4 +57,3 @@ assets = [
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
disable_automatic_asset_installation = []
|
disable_automatic_asset_installation = []
|
||||||
parametric_resize_beta = []
|
|
||||||
|
@ -70,7 +70,7 @@ args = ["clippy", "--", "@@split(CARGO_MAKE_TASK_ARGS,;)"]
|
|||||||
# Release building and installing Zellij
|
# Release building and installing Zellij
|
||||||
[tasks.install]
|
[tasks.install]
|
||||||
workspace = false
|
workspace = false
|
||||||
dependencies = ["build-plugins-release", "wasm-opt-plugins", "build-release", "manpage"]
|
dependencies = ["wasm-opt-plugins", "build-release", "manpage"]
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = '''
|
script = '''
|
||||||
if is_dir ${CARGO_MAKE_TASK_ARGS}
|
if is_dir ${CARGO_MAKE_TASK_ARGS}
|
||||||
@ -89,6 +89,7 @@ env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = ["default-plugins/status-bar",
|
|||||||
run_task = { name = "build", fork = true }
|
run_task = { name = "build", fork = true }
|
||||||
|
|
||||||
[tasks.wasm-opt-plugins]
|
[tasks.wasm-opt-plugins]
|
||||||
|
dependencies = ["build-plugins-release"]
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = '''
|
script = '''
|
||||||
plugins = glob_array ${CARGO_TARGET_DIR}/wasm32-wasi/release/*.wasm
|
plugins = glob_array ${CARGO_TARGET_DIR}/wasm32-wasi/release/*.wasm
|
||||||
@ -134,6 +135,7 @@ args = ["build", "--verbose", "--target", "x86_64-unknown-linux-musl"]
|
|||||||
# Run e2e tests - we mark the e2e tests as "ignored" so they will not be run with the normal ones
|
# Run e2e tests - we mark the e2e tests as "ignored" so they will not be run with the normal ones
|
||||||
[tasks.e2e-test]
|
[tasks.e2e-test]
|
||||||
workspace = false
|
workspace = false
|
||||||
|
dependencies = ["build-e2e"]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["test", "--", "--ignored", "--nocapture", "--test-threads", "1", "@@split(CARGO_MAKE_TASK_ARGS,;)"]
|
args = ["test", "--", "--ignored", "--nocapture", "--test-threads", "1", "@@split(CARGO_MAKE_TASK_ARGS,;)"]
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -135,8 +135,6 @@ fn color_elements(palette: Palette) -> ColoredElements {
|
|||||||
impl ZellijPlugin for State {
|
impl ZellijPlugin for State {
|
||||||
fn load(&mut self) {
|
fn load(&mut self) {
|
||||||
set_selectable(false);
|
set_selectable(false);
|
||||||
set_invisible_borders(true);
|
|
||||||
set_fixed_height(2);
|
|
||||||
subscribe(&[
|
subscribe(&[
|
||||||
EventType::ModeUpdate,
|
EventType::ModeUpdate,
|
||||||
EventType::CopyToClipboard,
|
EventType::CopyToClipboard,
|
||||||
|
@ -5,9 +5,7 @@ use zellij_tile::prelude::*;
|
|||||||
use zellij_tile_utils::style;
|
use zellij_tile_utils::style;
|
||||||
|
|
||||||
fn get_current_title_len(current_title: &[LinePart]) -> usize {
|
fn get_current_title_len(current_title: &[LinePart]) -> usize {
|
||||||
current_title
|
current_title.iter().map(|p| p.len).sum()
|
||||||
.iter()
|
|
||||||
.fold(0, |acc, title_part| acc + title_part.len)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_tabs_in_tab_line(
|
fn populate_tabs_in_tab_line(
|
||||||
@ -144,20 +142,29 @@ fn add_next_tabs_msg(
|
|||||||
title_bar.push(right_more_message);
|
title_bar.push(right_more_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_line_prefix(session_name: Option<&str>, palette: Palette) -> LinePart {
|
fn tab_line_prefix(session_name: Option<&str>, palette: Palette, cols: usize) -> Vec<LinePart> {
|
||||||
let mut prefix_text = " Zellij ".to_string();
|
let prefix_text = " Zellij ".to_string();
|
||||||
if let Some(name) = session_name {
|
|
||||||
prefix_text.push_str(&format!("({}) ", name));
|
|
||||||
}
|
|
||||||
|
|
||||||
let prefix_text_len = prefix_text.chars().count();
|
let prefix_text_len = prefix_text.chars().count();
|
||||||
let prefix_styled_text = style!(palette.white, palette.cyan)
|
let prefix_styled_text = style!(palette.white, palette.cyan)
|
||||||
.bold()
|
.bold()
|
||||||
.paint(prefix_text);
|
.paint(prefix_text);
|
||||||
LinePart {
|
let mut parts = vec![LinePart {
|
||||||
part: format!("{}", prefix_styled_text),
|
part: format!("{}", prefix_styled_text),
|
||||||
len: prefix_text_len,
|
len: prefix_text_len,
|
||||||
|
}];
|
||||||
|
if let Some(name) = session_name {
|
||||||
|
let name_part = format!("({}) ", name);
|
||||||
|
let name_part_len = name_part.chars().count();
|
||||||
|
let name_part_styled_text = style!(palette.white, palette.cyan).bold().paint(name_part);
|
||||||
|
if cols.saturating_sub(prefix_text_len) >= name_part_len {
|
||||||
|
parts.push(LinePart {
|
||||||
|
part: format!("{}", name_part_styled_text),
|
||||||
|
len: name_part_len,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
parts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tab_separator(capabilities: PluginCapabilities) -> &'static str {
|
pub fn tab_separator(capabilities: PluginCapabilities) -> &'static str {
|
||||||
@ -176,7 +183,7 @@ pub fn tab_line(
|
|||||||
palette: Palette,
|
palette: Palette,
|
||||||
capabilities: PluginCapabilities,
|
capabilities: PluginCapabilities,
|
||||||
) -> Vec<LinePart> {
|
) -> Vec<LinePart> {
|
||||||
let mut tabs_to_render: Vec<LinePart> = vec![];
|
let mut tabs_to_render = Vec::new();
|
||||||
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
|
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
|
||||||
let mut tabs_before_active = all_tabs;
|
let mut tabs_before_active = all_tabs;
|
||||||
let active_tab = if !tabs_after_active.is_empty() {
|
let active_tab = if !tabs_after_active.is_empty() {
|
||||||
@ -184,14 +191,17 @@ pub fn tab_line(
|
|||||||
} else {
|
} else {
|
||||||
tabs_before_active.pop().unwrap()
|
tabs_before_active.pop().unwrap()
|
||||||
};
|
};
|
||||||
tabs_to_render.push(active_tab);
|
let mut prefix = tab_line_prefix(session_name, palette, cols);
|
||||||
|
let prefix_len = get_current_title_len(&prefix);
|
||||||
|
if prefix_len + active_tab.len <= cols {
|
||||||
|
tabs_to_render.push(active_tab);
|
||||||
|
}
|
||||||
|
|
||||||
let prefix = tab_line_prefix(session_name, palette);
|
|
||||||
populate_tabs_in_tab_line(
|
populate_tabs_in_tab_line(
|
||||||
&mut tabs_before_active,
|
&mut tabs_before_active,
|
||||||
&mut tabs_after_active,
|
&mut tabs_after_active,
|
||||||
&mut tabs_to_render,
|
&mut tabs_to_render,
|
||||||
cols.saturating_sub(prefix.len),
|
cols.saturating_sub(prefix_len),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut tab_line: Vec<LinePart> = vec![];
|
let mut tab_line: Vec<LinePart> = vec![];
|
||||||
@ -200,7 +210,7 @@ pub fn tab_line(
|
|||||||
&mut tabs_before_active,
|
&mut tabs_before_active,
|
||||||
&mut tabs_to_render,
|
&mut tabs_to_render,
|
||||||
&mut tab_line,
|
&mut tab_line,
|
||||||
cols.saturating_sub(prefix.len),
|
cols.saturating_sub(prefix_len),
|
||||||
palette,
|
palette,
|
||||||
tab_separator(capabilities),
|
tab_separator(capabilities),
|
||||||
);
|
);
|
||||||
@ -210,11 +220,11 @@ pub fn tab_line(
|
|||||||
add_next_tabs_msg(
|
add_next_tabs_msg(
|
||||||
&mut tabs_after_active,
|
&mut tabs_after_active,
|
||||||
&mut tab_line,
|
&mut tab_line,
|
||||||
cols.saturating_sub(prefix.len),
|
cols.saturating_sub(prefix_len),
|
||||||
palette,
|
palette,
|
||||||
tab_separator(capabilities),
|
tab_separator(capabilities),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
tab_line.insert(0, prefix);
|
prefix.append(&mut tab_line);
|
||||||
tab_line
|
prefix
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@ register_plugin!(State);
|
|||||||
impl ZellijPlugin for State {
|
impl ZellijPlugin for State {
|
||||||
fn load(&mut self) {
|
fn load(&mut self) {
|
||||||
set_selectable(false);
|
set_selectable(false);
|
||||||
set_invisible_borders(true);
|
|
||||||
set_fixed_height(1);
|
|
||||||
subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]);
|
subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "beta"
|
channel = "stable"
|
||||||
components = ["rustfmt", "clippy", "rust-analysis"]
|
components = ["rustfmt", "clippy", "rust-analysis"]
|
||||||
targets = ["wasm32-wasi"]
|
targets = ["wasm32-wasi"]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use ::insta::assert_snapshot;
|
use ::insta::assert_snapshot;
|
||||||
use zellij_utils::{pane_size::PositionAndSize, position::Position};
|
use zellij_utils::{pane_size::Size, position::Position};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
@ -71,19 +71,16 @@ pub fn normal_mouse_report(position: Position, button: u8) -> Vec<u8> {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn starts_with_one_terminal() {
|
pub fn starts_with_one_terminal() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("starts_with_one_terminal", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("starts_with_one_terminal", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Wait for app to load",
|
name: "Wait for app to load",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -97,20 +94,17 @@ pub fn starts_with_one_terminal() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn split_terminals_vertically() {
|
pub fn split_terminals_vertically() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -139,23 +133,16 @@ pub fn split_terminals_vertically() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
|
pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size { cols: 8, rows: 20 };
|
||||||
cols: 8,
|
|
||||||
rows: 20,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let last_snapshot = RemoteRunner::new(
|
let last_snapshot = RemoteRunner::new(
|
||||||
"cannot_split_terminals_vertically_when_active_terminal_is_too_small",
|
"cannot_split_terminals_vertically_when_active_terminal_is_too_small",
|
||||||
fake_win_size,
|
fake_win_size,
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2) {
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) {
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
// back to normal mode after split
|
// back to normal mode after split
|
||||||
@ -178,7 +165,7 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
|
|||||||
name: "Wait for text to appear",
|
name: "Wait for text to appear",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(5, 2) && remote_terminal.snapshot_contains("Hi!")
|
if remote_terminal.cursor_position_is(6, 2) && remote_terminal.snapshot_contains("Hi!")
|
||||||
{
|
{
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -192,19 +179,16 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn scrolling_inside_a_pane() {
|
pub fn scrolling_inside_a_pane() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("scrolling_inside_a_pane", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("scrolling_inside_a_pane", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -279,19 +263,16 @@ pub fn scrolling_inside_a_pane() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn toggle_pane_fullscreen() {
|
pub fn toggle_pane_fullscreen() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("toggle_pane_fullscreen", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("toggle_pane_fullscreen", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -321,7 +302,7 @@ pub fn toggle_pane_fullscreen() {
|
|||||||
name: "Wait for pane to become fullscreen",
|
name: "Wait for pane to become fullscreen",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(2, 2) {
|
if remote_terminal.cursor_position_is(3, 2) {
|
||||||
// cursor is in full screen pane now
|
// cursor is in full screen pane now
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -335,19 +316,16 @@ pub fn toggle_pane_fullscreen() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn open_new_tab() {
|
pub fn open_new_tab() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("open_new_tab", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("open_new_tab", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -377,7 +355,7 @@ pub fn open_new_tab() {
|
|||||||
name: "Wait for new tab to open",
|
name: "Wait for new tab to open",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.cursor_position_is(3, 2)
|
||||||
&& remote_terminal.tip_appears()
|
&& remote_terminal.tip_appears()
|
||||||
&& remote_terminal.snapshot_contains("Tab #2")
|
&& remote_terminal.snapshot_contains("Tab #2")
|
||||||
&& remote_terminal.status_bar_appears()
|
&& remote_terminal.status_bar_appears()
|
||||||
@ -395,19 +373,16 @@ pub fn open_new_tab() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn close_pane() {
|
pub fn close_pane() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("close_pane", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("close_pane", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -437,7 +412,7 @@ pub fn close_pane() {
|
|||||||
name: "Wait for pane to close",
|
name: "Wait for pane to close",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(2, 2) && remote_terminal.tip_appears() {
|
if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() {
|
||||||
// cursor is in the original pane
|
// cursor is in the original pane
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -451,19 +426,16 @@ pub fn close_pane() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn exit_zellij() {
|
pub fn exit_zellij() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("exit_zellij", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("exit_zellij", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Wait for app to load",
|
name: "Wait for app to load",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&QUIT);
|
remote_terminal.send_key(&QUIT);
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
@ -490,19 +462,16 @@ pub fn exit_zellij() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn closing_last_pane_exits_zellij() {
|
pub fn closing_last_pane_exits_zellij() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("closing_last_pane_exits_zellij", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("closing_last_pane_exits_zellij", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Close pane",
|
name: "Close pane",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE);
|
remote_terminal.send_key(&CLOSE_PANE_IN_PANE_MODE);
|
||||||
@ -528,19 +497,16 @@ pub fn closing_last_pane_exits_zellij() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn resize_pane() {
|
pub fn resize_pane() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("resize_pane", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("resize_pane", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -570,7 +536,7 @@ pub fn resize_pane() {
|
|||||||
name: "Wait for pane to be resized",
|
name: "Wait for pane to be resized",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(53, 2) && remote_terminal.tip_appears() {
|
if remote_terminal.cursor_position_is(57, 2) && remote_terminal.tip_appears() {
|
||||||
// pane has been resized
|
// pane has been resized
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -584,19 +550,16 @@ pub fn resize_pane() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn lock_mode() {
|
pub fn lock_mode() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("lock_mode", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("lock_mode", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Enter lock mode",
|
name: "Enter lock mode",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&LOCK_MODE);
|
remote_terminal.send_key(&LOCK_MODE);
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
@ -621,7 +584,7 @@ pub fn lock_mode() {
|
|||||||
name: "Wait for terminal to render sent keys",
|
name: "Wait for terminal to render sent keys",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(6, 2) {
|
if remote_terminal.cursor_position_is(7, 2) {
|
||||||
// text has been entered into the only terminal pane
|
// text has been entered into the only terminal pane
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -636,19 +599,16 @@ pub fn lock_mode() {
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn resize_terminal_window() {
|
pub fn resize_terminal_window() {
|
||||||
// this checks the resizing of the whole terminal window (reaction to SIGWINCH) and not just one pane
|
// this checks the resizing of the whole terminal window (reaction to SIGWINCH) and not just one pane
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot = RemoteRunner::new("resize_terminal_window", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("resize_terminal_window", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -675,7 +635,7 @@ pub fn resize_terminal_window() {
|
|||||||
name: "wait for terminal to be resized and app to be re-rendered",
|
name: "wait for terminal to be resized and app to be re-rendered",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.cursor_position_is(43, 2) && remote_terminal.tip_appears() {
|
if remote_terminal.cursor_position_is(53, 2) && remote_terminal.tip_appears() {
|
||||||
// size has been changed
|
// size has been changed
|
||||||
step_is_complete = true;
|
step_is_complete = true;
|
||||||
}
|
}
|
||||||
@ -689,98 +649,87 @@ pub fn resize_terminal_window() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn detach_and_attach_session() {
|
pub fn detach_and_attach_session() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let session_id = rand::thread_rng().gen_range(0..10000);
|
let last_snapshot = RemoteRunner::new("detach_and_attach_session", fake_win_size)
|
||||||
let session_name = format!("session_{}", session_id);
|
.add_step(Step {
|
||||||
let last_snapshot = RemoteRunner::new(
|
name: "Split pane to the right",
|
||||||
"detach_and_attach_session",
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
fake_win_size,
|
let mut step_is_complete = false;
|
||||||
Some(session_name),
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
)
|
{
|
||||||
.add_step(Step {
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
name: "Split pane to the right",
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
// back to normal mode after split
|
||||||
let mut step_is_complete = false;
|
remote_terminal.send_key(&ENTER);
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2) {
|
step_is_complete = true;
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
}
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
step_is_complete
|
||||||
// back to normal mode after split
|
},
|
||||||
remote_terminal.send_key(&ENTER);
|
})
|
||||||
step_is_complete = true;
|
.add_step(Step {
|
||||||
}
|
name: "Send some text to the active pane",
|
||||||
step_is_complete
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
},
|
let mut step_is_complete = false;
|
||||||
})
|
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
|
||||||
.add_step(Step {
|
// new pane has been opened and focused
|
||||||
name: "Send some text to the active pane",
|
remote_terminal.send_key(&"I am some text".as_bytes());
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
step_is_complete = true;
|
||||||
let mut step_is_complete = false;
|
}
|
||||||
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
|
step_is_complete
|
||||||
// new pane has been opened and focused
|
},
|
||||||
remote_terminal.send_key(&"I am some text".as_bytes());
|
})
|
||||||
step_is_complete = true;
|
.add_step(Step {
|
||||||
}
|
name: "Detach session",
|
||||||
step_is_complete
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
},
|
let mut step_is_complete = false;
|
||||||
})
|
if remote_terminal.cursor_position_is(77, 2) {
|
||||||
.add_step(Step {
|
remote_terminal.send_key(&SESSION_MODE);
|
||||||
name: "Detach session",
|
remote_terminal.send_key(&DETACH_IN_SESSION_MODE);
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
// text has been entered
|
||||||
let mut step_is_complete = false;
|
step_is_complete = true;
|
||||||
if remote_terminal.cursor_position_is(77, 2) {
|
}
|
||||||
remote_terminal.send_key(&SESSION_MODE);
|
step_is_complete
|
||||||
remote_terminal.send_key(&DETACH_IN_SESSION_MODE);
|
},
|
||||||
// text has been entered
|
})
|
||||||
step_is_complete = true;
|
.add_step(Step {
|
||||||
}
|
name: "Reattach session",
|
||||||
step_is_complete
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
},
|
let mut step_is_complete = false;
|
||||||
})
|
if !remote_terminal.status_bar_appears() {
|
||||||
.add_step(Step {
|
// we don't see the toolbar, so we can assume we've already detached
|
||||||
name: "Reattach session",
|
remote_terminal.attach_to_original_session();
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
step_is_complete = true;
|
||||||
let mut step_is_complete = false;
|
}
|
||||||
if !remote_terminal.status_bar_appears() {
|
step_is_complete
|
||||||
// we don't see the toolbar, so we can assume we've already detached
|
},
|
||||||
remote_terminal.attach_to_original_session();
|
})
|
||||||
step_is_complete = true;
|
.add_step(Step {
|
||||||
}
|
name: "Wait for session to be attached",
|
||||||
step_is_complete
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
},
|
let mut step_is_complete = false;
|
||||||
})
|
if remote_terminal.cursor_position_is(77, 2) {
|
||||||
.add_step(Step {
|
// we're back inside the session
|
||||||
name: "Wait for session to be attached",
|
step_is_complete = true;
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
}
|
||||||
let mut step_is_complete = false;
|
step_is_complete
|
||||||
if remote_terminal.cursor_position_is(77, 2) {
|
},
|
||||||
// we're back inside the session
|
})
|
||||||
step_is_complete = true;
|
.run_all_steps();
|
||||||
}
|
|
||||||
step_is_complete
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.run_all_steps();
|
|
||||||
assert_snapshot!(last_snapshot);
|
assert_snapshot!(last_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn accepts_basic_layout() {
|
pub fn accepts_basic_layout() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let layout_file_name = "three-panes-with-nesting.yaml";
|
let layout_file_name = "three-panes-with-nesting.yaml";
|
||||||
let last_snapshot = RemoteRunner::new_with_layout("accepts_basic_layout", fake_win_size, layout_file_name, None)
|
let last_snapshot = RemoteRunner::new_with_layout("accepts_basic_layout", fake_win_size, layout_file_name)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Wait for app to load",
|
name: "Wait for app to load",
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
@ -800,20 +749,17 @@ pub fn accepts_basic_layout() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn focus_pane_with_mouse() {
|
fn focus_pane_with_mouse() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
let mut step_is_complete = false;
|
let mut step_is_complete = false;
|
||||||
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
{
|
{
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
@ -853,103 +799,95 @@ fn focus_pane_with_mouse() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn scrolling_inside_a_pane_with_mouse() {
|
pub fn scrolling_inside_a_pane_with_mouse() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let last_snapshot =
|
let last_snapshot = RemoteRunner::new("scrolling_inside_a_pane_with_mouse", fake_win_size)
|
||||||
RemoteRunner::new("scrolling_inside_a_pane_with_mouse", fake_win_size, None)
|
.add_step(Step {
|
||||||
.add_step(Step {
|
name: "Split pane to the right",
|
||||||
name: "Split pane to the right",
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
let mut step_is_complete = false;
|
||||||
let mut step_is_complete = false;
|
if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2)
|
||||||
if remote_terminal.status_bar_appears()
|
{
|
||||||
&& remote_terminal.cursor_position_is(2, 2)
|
remote_terminal.send_key(&PANE_MODE);
|
||||||
{
|
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
||||||
remote_terminal.send_key(&PANE_MODE);
|
// back to normal mode after split
|
||||||
remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
|
remote_terminal.send_key(&ENTER);
|
||||||
// back to normal mode after split
|
step_is_complete = true;
|
||||||
remote_terminal.send_key(&ENTER);
|
}
|
||||||
step_is_complete = true;
|
step_is_complete
|
||||||
}
|
},
|
||||||
step_is_complete
|
})
|
||||||
},
|
.add_step(Step {
|
||||||
})
|
name: "Fill terminal with text",
|
||||||
.add_step(Step {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
name: "Fill terminal with text",
|
let mut step_is_complete = false;
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
|
||||||
let mut step_is_complete = false;
|
// cursor is in the newly opened second pane
|
||||||
if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
|
remote_terminal.send_key(&format!("{:0<56}", "line1 ").as_bytes());
|
||||||
// cursor is in the newly opened second pane
|
remote_terminal.send_key(&format!("{:0<58}", "line2 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<56}", "line1 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line3 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line2 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line4 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line3 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line5 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line4 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line6 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line5 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line7 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line6 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line8 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line7 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line9 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line8 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line10 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line9 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line11 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line10 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line12 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line11 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line13 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line12 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line14 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line13 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line15 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line14 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line16 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line15 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line17 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line16 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line18 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line17 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<58}", "line19 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line18 ").as_bytes());
|
remote_terminal.send_key(&format!("{:0<57}", "line20 ").as_bytes());
|
||||||
remote_terminal.send_key(&format!("{:0<58}", "line19 ").as_bytes());
|
step_is_complete = true;
|
||||||
remote_terminal.send_key(&format!("{:0<57}", "line20 ").as_bytes());
|
}
|
||||||
step_is_complete = true;
|
step_is_complete
|
||||||
}
|
},
|
||||||
step_is_complete
|
})
|
||||||
},
|
.add_step(Step {
|
||||||
})
|
name: "Scroll up inside pane",
|
||||||
.add_step(Step {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
name: "Scroll up inside pane",
|
let mut step_is_complete = false;
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
if remote_terminal.cursor_position_is(118, 20) {
|
||||||
let mut step_is_complete = false;
|
// all lines have been written to the pane
|
||||||
if remote_terminal.cursor_position_is(118, 20) {
|
remote_terminal.send_key(&normal_mouse_report(Position::new(2, 64), 64));
|
||||||
// all lines have been written to the pane
|
step_is_complete = true;
|
||||||
remote_terminal.send_key(&normal_mouse_report(Position::new(2, 64), 64));
|
}
|
||||||
step_is_complete = true;
|
step_is_complete
|
||||||
}
|
},
|
||||||
step_is_complete
|
})
|
||||||
},
|
.add_step(Step {
|
||||||
})
|
name: "Wait for scroll to finish",
|
||||||
.add_step(Step {
|
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
||||||
name: "Wait for scroll to finish",
|
let mut step_is_complete = false;
|
||||||
instruction: |remote_terminal: RemoteTerminal| -> bool {
|
if remote_terminal.cursor_position_is(118, 20)
|
||||||
let mut step_is_complete = false;
|
&& remote_terminal.snapshot_contains("line1 ")
|
||||||
if remote_terminal.cursor_position_is(118, 20)
|
{
|
||||||
&& remote_terminal.snapshot_contains("line1 ")
|
// scrolled up one line
|
||||||
{
|
step_is_complete = true;
|
||||||
// scrolled up one line
|
}
|
||||||
step_is_complete = true;
|
step_is_complete
|
||||||
}
|
},
|
||||||
step_is_complete
|
})
|
||||||
},
|
.run_all_steps();
|
||||||
})
|
|
||||||
.run_all_steps();
|
|
||||||
assert_snapshot!(last_snapshot);
|
assert_snapshot!(last_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
pub fn start_without_pane_frames() {
|
pub fn start_without_pane_frames() {
|
||||||
let fake_win_size = PositionAndSize {
|
let fake_win_size = Size {
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let last_snapshot = RemoteRunner::new_without_frames("no_pane_frames", fake_win_size, None)
|
let last_snapshot = RemoteRunner::new_without_frames("no_pane_frames", fake_win_size)
|
||||||
.add_step(Step {
|
.add_step(Step {
|
||||||
name: "Split pane to the right",
|
name: "Split pane to the right",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use zellij_tile::data::Palette;
|
use zellij_tile::data::Palette;
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
|
||||||
|
|
||||||
use zellij_server::panes::TerminalPane;
|
use zellij_server::panes::TerminalPane;
|
||||||
|
use zellij_utils::pane_size::{Dimension, PaneGeom, Size};
|
||||||
use zellij_utils::{vte, zellij_tile};
|
use zellij_utils::{vte, zellij_tile};
|
||||||
|
|
||||||
use ssh2::Session;
|
use ssh2::Session;
|
||||||
@ -15,6 +15,7 @@ const ZELLIJ_LAYOUT_PATH: &str = "/usr/src/zellij/fixtures/layouts";
|
|||||||
const CONNECTION_STRING: &str = "127.0.0.1:2222";
|
const CONNECTION_STRING: &str = "127.0.0.1:2222";
|
||||||
const CONNECTION_USERNAME: &str = "test";
|
const CONNECTION_USERNAME: &str = "test";
|
||||||
const CONNECTION_PASSWORD: &str = "test";
|
const CONNECTION_PASSWORD: &str = "test";
|
||||||
|
const SESSION_NAME: &str = "e2e-test";
|
||||||
|
|
||||||
fn ssh_connect() -> ssh2::Session {
|
fn ssh_connect() -> ssh2::Session {
|
||||||
let tcp = TcpStream::connect(CONNECTION_STRING).unwrap();
|
let tcp = TcpStream::connect(CONNECTION_STRING).unwrap();
|
||||||
@ -27,7 +28,7 @@ fn ssh_connect() -> ssh2::Session {
|
|||||||
sess
|
sess
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_remote_environment(channel: &mut ssh2::Channel, win_size: PositionAndSize) {
|
fn setup_remote_environment(channel: &mut ssh2::Channel, win_size: Size) {
|
||||||
let (columns, rows) = (win_size.cols as u32, win_size.rows as u32);
|
let (columns, rows) = (win_size.cols as u32, win_size.rows as u32);
|
||||||
channel
|
channel
|
||||||
.request_pty("xterm", None, Some((columns, rows, 0, 0)))
|
.request_pty("xterm", None, Some((columns, rows, 0, 0)))
|
||||||
@ -39,60 +40,51 @@ fn setup_remote_environment(channel: &mut ssh2::Channel, win_size: PositionAndSi
|
|||||||
channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_zellij(channel: &mut ssh2::Channel, session_name: Option<&String>) {
|
fn stop_zellij(channel: &mut ssh2::Channel) {
|
||||||
match session_name.as_ref() {
|
channel
|
||||||
Some(name) => {
|
.write_all("killall -KILL zellij\n".as_bytes())
|
||||||
channel
|
.unwrap();
|
||||||
.write_all(
|
|
||||||
format!("{} --session {}\n", ZELLIJ_EXECUTABLE_LOCATION, name).as_bytes(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
channel
|
|
||||||
.write_all(format!("{}\n", ZELLIJ_EXECUTABLE_LOCATION).as_bytes())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
channel.flush().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_zellij_without_frames(channel: &mut ssh2::Channel) {
|
fn start_zellij(channel: &mut ssh2::Channel) {
|
||||||
|
stop_zellij(channel);
|
||||||
channel
|
channel
|
||||||
.write_all(format!("{} options --no-pane-frames\n", ZELLIJ_EXECUTABLE_LOCATION).as_bytes())
|
.write_all(
|
||||||
|
format!(
|
||||||
|
"{} --session {}\n",
|
||||||
|
ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_zellij_with_layout(
|
fn start_zellij_without_frames(channel: &mut ssh2::Channel) {
|
||||||
channel: &mut ssh2::Channel,
|
stop_zellij(channel);
|
||||||
layout_path: &str,
|
channel
|
||||||
session_name: Option<&String>,
|
.write_all(
|
||||||
) {
|
format!(
|
||||||
match session_name.as_ref() {
|
"{} --session {} options --no-pane-frames\n",
|
||||||
Some(name) => {
|
ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME
|
||||||
channel
|
)
|
||||||
.write_all(
|
.as_bytes(),
|
||||||
format!(
|
)
|
||||||
"{} --layout-path {} --session {}\n",
|
.unwrap();
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, layout_path, name
|
channel.flush().unwrap();
|
||||||
)
|
}
|
||||||
.as_bytes(),
|
|
||||||
)
|
fn start_zellij_with_layout(channel: &mut ssh2::Channel, layout_path: &str) {
|
||||||
.unwrap();
|
stop_zellij(channel);
|
||||||
}
|
channel
|
||||||
None => {
|
.write_all(
|
||||||
channel
|
format!(
|
||||||
.write_all(
|
"{} --layout-path {} --session {}\n",
|
||||||
format!(
|
ZELLIJ_EXECUTABLE_LOCATION, layout_path, SESSION_NAME
|
||||||
"{} --layout-path {}\n",
|
)
|
||||||
ZELLIJ_EXECUTABLE_LOCATION, layout_path
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.unwrap();
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +111,6 @@ pub fn take_snapshot(terminal_output: &mut TerminalPane) -> String {
|
|||||||
|
|
||||||
pub struct RemoteTerminal<'a> {
|
pub struct RemoteTerminal<'a> {
|
||||||
channel: &'a mut ssh2::Channel,
|
channel: &'a mut ssh2::Channel,
|
||||||
session_name: Option<&'a String>,
|
|
||||||
cursor_x: usize,
|
cursor_x: usize,
|
||||||
cursor_y: usize,
|
cursor_y: usize,
|
||||||
current_snapshot: String,
|
current_snapshot: String,
|
||||||
@ -174,12 +165,7 @@ impl<'a> RemoteTerminal<'a> {
|
|||||||
pub fn attach_to_original_session(&mut self) {
|
pub fn attach_to_original_session(&mut self) {
|
||||||
self.channel
|
self.channel
|
||||||
.write_all(
|
.write_all(
|
||||||
format!(
|
format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME).as_bytes(),
|
||||||
"{} attach {}\n",
|
|
||||||
ZELLIJ_EXECUTABLE_LOCATION,
|
|
||||||
self.session_name.unwrap()
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.channel.flush().unwrap();
|
self.channel.flush().unwrap();
|
||||||
@ -198,33 +184,37 @@ pub struct RemoteRunner {
|
|||||||
vte_parser: vte::Parser,
|
vte_parser: vte::Parser,
|
||||||
terminal_output: TerminalPane,
|
terminal_output: TerminalPane,
|
||||||
channel: ssh2::Channel,
|
channel: ssh2::Channel,
|
||||||
session_name: Option<String>,
|
|
||||||
test_name: &'static str,
|
test_name: &'static str,
|
||||||
currently_running_step: Option<String>,
|
currently_running_step: Option<String>,
|
||||||
retries_left: usize,
|
retries_left: usize,
|
||||||
win_size: PositionAndSize,
|
win_size: Size,
|
||||||
layout_file_name: Option<&'static str>,
|
layout_file_name: Option<&'static str>,
|
||||||
without_frames: bool,
|
without_frames: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteRunner {
|
impl RemoteRunner {
|
||||||
pub fn new(
|
pub fn new(test_name: &'static str, win_size: Size) -> Self {
|
||||||
test_name: &'static str,
|
|
||||||
win_size: PositionAndSize,
|
|
||||||
session_name: Option<String>,
|
|
||||||
) -> Self {
|
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
let vte_parser = vte::Parser::new();
|
||||||
let terminal_output = TerminalPane::new(0, win_size, Palette::default(), 0); // 0 is the pane index
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
|
rows.set_inner(win_size.rows);
|
||||||
|
cols.set_inner(win_size.cols);
|
||||||
|
let pane_geom = PaneGeom {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
};
|
||||||
|
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij(&mut channel, session_name.as_ref());
|
start_zellij(&mut channel);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
terminal_output,
|
||||||
vte_parser,
|
vte_parser,
|
||||||
session_name,
|
|
||||||
test_name,
|
test_name,
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
@ -234,15 +224,21 @@ impl RemoteRunner {
|
|||||||
without_frames: false,
|
without_frames: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_without_frames(
|
pub fn new_without_frames(test_name: &'static str, win_size: Size) -> Self {
|
||||||
test_name: &'static str,
|
|
||||||
win_size: PositionAndSize,
|
|
||||||
session_name: Option<String>,
|
|
||||||
) -> Self {
|
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
let vte_parser = vte::Parser::new();
|
||||||
let terminal_output = TerminalPane::new(0, win_size, Palette::default(), 0); // 0 is the pane index
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
|
rows.set_inner(win_size.rows);
|
||||||
|
cols.set_inner(win_size.cols);
|
||||||
|
let pane_geom = PaneGeom {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
};
|
||||||
|
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij_without_frames(&mut channel);
|
start_zellij_without_frames(&mut channel);
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
@ -250,7 +246,6 @@ impl RemoteRunner {
|
|||||||
channel,
|
channel,
|
||||||
terminal_output,
|
terminal_output,
|
||||||
vte_parser,
|
vte_parser,
|
||||||
session_name,
|
|
||||||
test_name,
|
test_name,
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
@ -262,27 +257,31 @@ impl RemoteRunner {
|
|||||||
}
|
}
|
||||||
pub fn new_with_layout(
|
pub fn new_with_layout(
|
||||||
test_name: &'static str,
|
test_name: &'static str,
|
||||||
win_size: PositionAndSize,
|
win_size: Size,
|
||||||
layout_file_name: &'static str,
|
layout_file_name: &'static str,
|
||||||
session_name: Option<String>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name);
|
let remote_path = Path::new(ZELLIJ_LAYOUT_PATH).join(layout_file_name);
|
||||||
let sess = ssh_connect();
|
let sess = ssh_connect();
|
||||||
let mut channel = sess.channel_session().unwrap();
|
let mut channel = sess.channel_session().unwrap();
|
||||||
let vte_parser = vte::Parser::new();
|
let vte_parser = vte::Parser::new();
|
||||||
let terminal_output = TerminalPane::new(0, win_size, Palette::default(), 0); // 0 is the pane index
|
let mut rows = Dimension::fixed(win_size.rows);
|
||||||
|
let mut cols = Dimension::fixed(win_size.cols);
|
||||||
|
rows.set_inner(win_size.rows);
|
||||||
|
cols.set_inner(win_size.cols);
|
||||||
|
let pane_geom = PaneGeom {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
};
|
||||||
|
let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index
|
||||||
setup_remote_environment(&mut channel, win_size);
|
setup_remote_environment(&mut channel, win_size);
|
||||||
start_zellij_with_layout(
|
start_zellij_with_layout(&mut channel, &remote_path.to_string_lossy());
|
||||||
&mut channel,
|
|
||||||
&remote_path.to_string_lossy(),
|
|
||||||
session_name.as_ref(),
|
|
||||||
);
|
|
||||||
RemoteRunner {
|
RemoteRunner {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
channel,
|
channel,
|
||||||
terminal_output,
|
terminal_output,
|
||||||
vte_parser,
|
vte_parser,
|
||||||
session_name,
|
|
||||||
test_name,
|
test_name,
|
||||||
currently_running_step: None,
|
currently_running_step: None,
|
||||||
current_step_index: 0,
|
current_step_index: 0,
|
||||||
@ -307,7 +306,6 @@ impl RemoteRunner {
|
|||||||
cursor_y,
|
cursor_y,
|
||||||
current_snapshot,
|
current_snapshot,
|
||||||
channel: &mut self.channel,
|
channel: &mut self.channel,
|
||||||
session_name: self.session_name.as_ref(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn run_next_step(&mut self) {
|
pub fn run_next_step(&mut self) {
|
||||||
@ -319,7 +317,6 @@ impl RemoteRunner {
|
|||||||
cursor_y,
|
cursor_y,
|
||||||
current_snapshot,
|
current_snapshot,
|
||||||
channel: &mut self.channel,
|
channel: &mut self.channel,
|
||||||
session_name: self.session_name.as_ref(),
|
|
||||||
};
|
};
|
||||||
let instruction = next_step.instruction;
|
let instruction = next_step.instruction;
|
||||||
self.currently_running_step = Some(String::from(next_step.name));
|
self.currently_running_step = Some(String::from(next_step.name));
|
||||||
@ -332,32 +329,22 @@ impl RemoteRunner {
|
|||||||
self.steps.get(self.current_step_index).is_some()
|
self.steps.get(self.current_step_index).is_some()
|
||||||
}
|
}
|
||||||
fn restart_test(&mut self) -> String {
|
fn restart_test(&mut self) -> String {
|
||||||
let session_name = self.session_name.as_ref().map(|name| {
|
|
||||||
// this is so that we don't try to connect to the previous session if it's still stuck
|
|
||||||
// inside the container
|
|
||||||
format!("{}_{}", name, self.retries_left)
|
|
||||||
});
|
|
||||||
if let Some(layout_file_name) = self.layout_file_name.as_ref() {
|
if let Some(layout_file_name) = self.layout_file_name.as_ref() {
|
||||||
// let mut new_runner = RemoteRunner::new_with_layout(self.test_name, self.win_size, Path::new(&local_layout_path), session_name);
|
// let mut new_runner = RemoteRunner::new_with_layout(self.test_name, self.win_size, Path::new(&local_layout_path), session_name);
|
||||||
let mut new_runner = RemoteRunner::new_with_layout(
|
let mut new_runner =
|
||||||
self.test_name,
|
RemoteRunner::new_with_layout(self.test_name, self.win_size, layout_file_name);
|
||||||
self.win_size,
|
|
||||||
layout_file_name,
|
|
||||||
session_name,
|
|
||||||
);
|
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
new_runner.retries_left = self.retries_left - 1;
|
||||||
new_runner.replace_steps(self.steps.clone());
|
new_runner.replace_steps(self.steps.clone());
|
||||||
drop(std::mem::replace(self, new_runner));
|
drop(std::mem::replace(self, new_runner));
|
||||||
self.run_all_steps()
|
self.run_all_steps()
|
||||||
} else if self.without_frames {
|
} else if self.without_frames {
|
||||||
let mut new_runner =
|
let mut new_runner = RemoteRunner::new_without_frames(self.test_name, self.win_size);
|
||||||
RemoteRunner::new_without_frames(self.test_name, self.win_size, session_name);
|
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
new_runner.retries_left = self.retries_left - 1;
|
||||||
new_runner.replace_steps(self.steps.clone());
|
new_runner.replace_steps(self.steps.clone());
|
||||||
drop(std::mem::replace(self, new_runner));
|
drop(std::mem::replace(self, new_runner));
|
||||||
self.run_all_steps()
|
self.run_all_steps()
|
||||||
} else {
|
} else {
|
||||||
let mut new_runner = RemoteRunner::new(self.test_name, self.win_size, session_name);
|
let mut new_runner = RemoteRunner::new(self.test_name, self.win_size);
|
||||||
new_runner.retries_left = self.retries_left - 1;
|
new_runner.retries_left = self.retries_left - 1;
|
||||||
new_runner.replace_steps(self.steps.clone());
|
new_runner.replace_steps(self.steps.clone());
|
||||||
drop(std::mem::replace(self, new_runner));
|
drop(std::mem::replace(self, new_runner));
|
||||||
|
@ -3,23 +3,23 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
|
Zellij
|
||||||
────────
|
┌──────┐
|
||||||
$ Hi!█
|
│$ Hi!█│
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└──────┘
|
||||||
Ctrl +
|
Ctrl +
|
||||||
|
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
─ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
$ █
|
│$ █ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
||||||
|
@ -3,7 +3,7 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
│$ ││$ I am some text█ │
|
│$ ││$ I am some text█ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
|
@ -3,7 +3,7 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
│$ █ ││$ │
|
│$ █ ││$ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
─ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
$ nabc█
|
│$ nabc█ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
-- INTERFACE LOCKED --
|
-- INTERFACE LOCKED --
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1 Tab #2
|
Zellij (e2e-test) Tab #1 Tab #2
|
||||||
─ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
$ █
|
│$ █ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ───────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────────────────────────┐
|
┌ Pane #1 ───────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────────────────────┐
|
||||||
│$ ││$ █ │
|
│$ ││$ █ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
└────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────────┘
|
└────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
┌ Pane #1 ───────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────┐
|
||||||
│$ ││$ █ │
|
│$ ││$ █ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
└──────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
└────────────────────────────────────────────────┘└────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
||||||
|
@ -3,7 +3,7 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/1 ┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/1 ┐
|
||||||
│$ ││$ line1 00000000000000000000000000000000000000000000000000│
|
│$ ││$ line1 00000000000000000000000000000000000000000000000000│
|
||||||
│ ││line2 0000000000000000000000000000000000000000000000000000│
|
│ ││line2 0000000000000000000000000000000000000000000000000000│
|
||||||
|
@ -3,7 +3,7 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/1 ┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────── SCROLL: 1/1 ┐
|
||||||
│$ ││$ line1 00000000000000000000000000000000000000000000000000│
|
│$ ││$ line1 00000000000000000000000000000000000000000000000000│
|
||||||
│ ││line2 0000000000000000000000000000000000000000000000000000│
|
│ ││line2 0000000000000000000000000000000000000000000000000000│
|
||||||
|
@ -3,7 +3,7 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
|
||||||
│$ ││$ █ │
|
│$ ││$ █ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
|
@ -3,7 +3,7 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
$ │$ █
|
$ │$ █
|
||||||
│
|
│
|
||||||
│
|
│
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
─ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
$ █
|
│$ █ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
||||||
|
@ -3,27 +3,27 @@ source: src/tests/e2e/cases.rs
|
|||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
|
|
||||||
---
|
---
|
||||||
Zellij Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
─ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
$ █
|
│$ █ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||||
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
Ctrl + <g> LOCK <p> PANE <t> TAB <r> RESIZE <s> SCROLL <o> SESSION <q> QUIT
|
||||||
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
|
||||||
|
@ -116,7 +116,7 @@ pub fn start_client(
|
|||||||
|
|
||||||
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
|
let full_screen_ws = os_input.get_terminal_size_using_fd(0);
|
||||||
let client_attributes = ClientAttributes {
|
let client_attributes = ClientAttributes {
|
||||||
position_and_size: full_screen_ws,
|
size: full_screen_ws,
|
||||||
palette,
|
palette,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ pub fn start_client(
|
|||||||
client_attributes,
|
client_attributes,
|
||||||
Box::new(opts),
|
Box::new(opts),
|
||||||
Box::new(config_options.clone()),
|
Box::new(config_options.clone()),
|
||||||
layout,
|
layout.unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use zellij_utils::input::actions::Action;
|
use zellij_utils::input::actions::Action;
|
||||||
|
use zellij_utils::pane_size::Size;
|
||||||
use zellij_utils::{interprocess, libc, nix, signal_hook, termion, zellij_tile};
|
use zellij_utils::{interprocess, libc, nix, signal_hook, termion, zellij_tile};
|
||||||
|
|
||||||
use interprocess::local_socket::LocalSocketStream;
|
use interprocess::local_socket::LocalSocketStream;
|
||||||
@ -15,7 +16,6 @@ use zellij_tile::data::Palette;
|
|||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
errors::ErrorContext,
|
errors::ErrorContext,
|
||||||
ipc::{ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg},
|
ipc::{ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg},
|
||||||
pane_size::PositionAndSize,
|
|
||||||
shared::default_palette,
|
shared::default_palette,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_terminal_size_using_fd(fd: RawFd) -> PositionAndSize {
|
pub(crate) fn get_terminal_size_using_fd(fd: RawFd) -> Size {
|
||||||
// TODO: do this with the nix ioctl
|
// TODO: do this with the nix ioctl
|
||||||
use libc::ioctl;
|
use libc::ioctl;
|
||||||
use libc::TIOCGWINSZ;
|
use libc::TIOCGWINSZ;
|
||||||
@ -54,7 +54,10 @@ pub(crate) fn get_terminal_size_using_fd(fd: RawFd) -> PositionAndSize {
|
|||||||
unsafe {
|
unsafe {
|
||||||
ioctl(fd, TIOCGWINSZ.into(), &mut winsize)
|
ioctl(fd, TIOCGWINSZ.into(), &mut winsize)
|
||||||
};
|
};
|
||||||
PositionAndSize::from(winsize)
|
Size {
|
||||||
|
rows: winsize.ws_row as usize,
|
||||||
|
cols: winsize.ws_col as usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -69,7 +72,7 @@ pub struct ClientOsInputOutput {
|
|||||||
/// Zellij client requires.
|
/// Zellij client requires.
|
||||||
pub trait ClientOsApi: Send + Sync {
|
pub trait ClientOsApi: Send + Sync {
|
||||||
/// Returns the size of the terminal associated to file descriptor `fd`.
|
/// Returns the size of the terminal associated to file descriptor `fd`.
|
||||||
fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize;
|
fn get_terminal_size_using_fd(&self, fd: RawFd) -> Size;
|
||||||
/// Set the terminal associated to file descriptor `fd` to
|
/// Set the terminal associated to file descriptor `fd` to
|
||||||
/// [raw mode](https://en.wikipedia.org/wiki/Terminal_mode).
|
/// [raw mode](https://en.wikipedia.org/wiki/Terminal_mode).
|
||||||
fn set_raw_mode(&mut self, fd: RawFd);
|
fn set_raw_mode(&mut self, fd: RawFd);
|
||||||
@ -98,7 +101,7 @@ pub trait ClientOsApi: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientOsApi for ClientOsInputOutput {
|
impl ClientOsApi for ClientOsInputOutput {
|
||||||
fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize {
|
fn get_terminal_size_using_fd(&self, fd: RawFd) -> Size {
|
||||||
get_terminal_size_using_fd(fd)
|
get_terminal_size_using_fd(fd)
|
||||||
}
|
}
|
||||||
fn set_raw_mode(&mut self, fd: RawFd) {
|
fn set_raw_mode(&mut self, fd: RawFd) {
|
||||||
|
@ -2,7 +2,7 @@ use super::input_loop;
|
|||||||
use zellij_utils::input::actions::{Action, Direction};
|
use zellij_utils::input::actions::{Action, Direction};
|
||||||
use zellij_utils::input::config::Config;
|
use zellij_utils::input::config::Config;
|
||||||
use zellij_utils::input::options::Options;
|
use zellij_utils::input::options::Options;
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
use zellij_utils::pane_size::Size;
|
||||||
use zellij_utils::zellij_tile::data::Palette;
|
use zellij_utils::zellij_tile::data::Palette;
|
||||||
|
|
||||||
use crate::{os_input_output::ClientOsApi, ClientInstruction, CommandIsExecuting};
|
use crate::{os_input_output::ClientOsApi, ClientInstruction, CommandIsExecuting};
|
||||||
@ -93,7 +93,7 @@ impl FakeClientOsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientOsApi for FakeClientOsApi {
|
impl ClientOsApi for FakeClientOsApi {
|
||||||
fn get_terminal_size_using_fd(&self, _fd: RawFd) -> PositionAndSize {
|
fn get_terminal_size_using_fd(&self, _fd: RawFd) -> Size {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn set_raw_mode(&mut self, _fd: RawFd) {
|
fn set_raw_mode(&mut self, _fd: RawFd) {
|
||||||
|
@ -46,12 +46,7 @@ use zellij_utils::{
|
|||||||
/// Instructions related to server-side application
|
/// Instructions related to server-side application
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum ServerInstruction {
|
pub(crate) enum ServerInstruction {
|
||||||
NewClient(
|
NewClient(ClientAttributes, Box<CliArgs>, Box<Options>, LayoutFromYaml),
|
||||||
ClientAttributes,
|
|
||||||
Box<CliArgs>,
|
|
||||||
Box<Options>,
|
|
||||||
Option<LayoutFromYaml>,
|
|
||||||
),
|
|
||||||
Render(Option<String>),
|
Render(Option<String>),
|
||||||
UnblockInputThread,
|
UnblockInputThread,
|
||||||
ClientExit,
|
ClientExit,
|
||||||
@ -127,8 +122,6 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
|||||||
daemonize::Daemonize::new()
|
daemonize::Daemonize::new()
|
||||||
.working_directory(std::env::current_dir().unwrap())
|
.working_directory(std::env::current_dir().unwrap())
|
||||||
.umask(0o077)
|
.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()
|
.start()
|
||||||
.expect("could not daemonize the server process");
|
.expect("could not daemonize the server process");
|
||||||
|
|
||||||
@ -237,19 +230,12 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
match layout {
|
if !&layout.tabs.is_empty() {
|
||||||
None => {
|
for tab_layout in layout.tabs {
|
||||||
spawn_tabs(None);
|
spawn_tabs(Some(tab_layout.clone()));
|
||||||
}
|
|
||||||
Some(layout) => {
|
|
||||||
if !&layout.tabs.is_empty() {
|
|
||||||
for tab_layout in layout.tabs {
|
|
||||||
spawn_tabs(Some(tab_layout.clone()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spawn_tabs(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
spawn_tabs(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerInstruction::AttachClient(attrs, _, options) => {
|
ServerInstruction::AttachClient(attrs, _, options) => {
|
||||||
@ -258,7 +244,7 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
|||||||
let session_data = rlock.as_ref().unwrap();
|
let session_data = rlock.as_ref().unwrap();
|
||||||
session_data
|
session_data
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::TerminalResize(attrs.position_and_size))
|
.send_to_screen(ScreenInstruction::TerminalResize(attrs.size))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let default_mode = options.default_mode.unwrap_or_default();
|
let default_mode = options.default_mode.unwrap_or_default();
|
||||||
let mode_info =
|
let mode_info =
|
||||||
@ -326,7 +312,7 @@ fn init_session(
|
|||||||
to_server: SenderWithContext<ServerInstruction>,
|
to_server: SenderWithContext<ServerInstruction>,
|
||||||
client_attributes: ClientAttributes,
|
client_attributes: ClientAttributes,
|
||||||
session_state: Arc<RwLock<SessionState>>,
|
session_state: Arc<RwLock<SessionState>>,
|
||||||
layout: Option<LayoutFromYaml>,
|
layout: LayoutFromYaml,
|
||||||
) -> SessionMetaData {
|
) -> SessionMetaData {
|
||||||
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
|
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
|
||||||
let to_screen = SenderWithContext::new(to_screen);
|
let to_screen = SenderWithContext::new(to_screen);
|
||||||
|
@ -251,7 +251,9 @@ pub trait ServerOsApi: Send + Sync {
|
|||||||
|
|
||||||
impl ServerOsApi for ServerOsInputOutput {
|
impl ServerOsApi for ServerOsInputOutput {
|
||||||
fn set_terminal_size_using_fd(&self, fd: RawFd, cols: u16, rows: u16) {
|
fn set_terminal_size_using_fd(&self, fd: RawFd, cols: u16, rows: u16) {
|
||||||
set_terminal_size_using_fd(fd, cols, rows);
|
if cols > 0 && rows > 0 {
|
||||||
|
set_terminal_size_using_fd(fd, cols, rows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn spawn_terminal(&self, terminal_action: Option<TerminalAction>) -> (RawFd, Pid) {
|
fn spawn_terminal(&self, terminal_action: Option<TerminalAction>) -> (RawFd, Pid) {
|
||||||
let orig_termios = self.orig_termios.lock().unwrap();
|
let orig_termios = self.orig_termios.lock().unwrap();
|
||||||
|
@ -592,8 +592,13 @@ impl Grid {
|
|||||||
self.change_size(new_rows, new_columns);
|
self.change_size(new_rows, new_columns);
|
||||||
}
|
}
|
||||||
pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
|
pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
|
||||||
|
// Do nothing if this pane hasn't been given a proper size yet
|
||||||
|
if new_columns == 0 || new_rows == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.selection.reset();
|
self.selection.reset();
|
||||||
if new_columns != self.width {
|
if new_columns != self.width {
|
||||||
|
self.horizontal_tabstops = create_horizontal_tabstops(new_columns);
|
||||||
let mut cursor_canonical_line_index = self.cursor_canonical_line_index();
|
let mut cursor_canonical_line_index = self.cursor_canonical_line_index();
|
||||||
let cursor_index_in_canonical_line = self.cursor_index_in_canonical_line();
|
let cursor_index_in_canonical_line = self.cursor_index_in_canonical_line();
|
||||||
let mut viewport_canonical_lines = vec![];
|
let mut viewport_canonical_lines = vec![];
|
||||||
@ -635,6 +640,11 @@ impl Grid {
|
|||||||
} else {
|
} else {
|
||||||
canonical_line.columns.drain(..).collect()
|
canonical_line.columns.drain(..).collect()
|
||||||
};
|
};
|
||||||
|
// If the next character is wider than the grid (i.e. there is nothing in
|
||||||
|
// `next_wrap`, then just abort the resizing
|
||||||
|
if next_wrap.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
let row = Row::from_columns(next_wrap);
|
let row = Row::from_columns(next_wrap);
|
||||||
// if there are no more parts, this row is canonical as long as it originally
|
// if there are no more parts, this row is canonical as long as it originally
|
||||||
// was canonical (it might not have been for example if it's the first row in
|
// was canonical (it might not have been for example if it's the first row in
|
||||||
@ -956,10 +966,9 @@ impl Grid {
|
|||||||
self.cursor.x += count_to_move;
|
self.cursor.x += count_to_move;
|
||||||
}
|
}
|
||||||
pub fn replace_characters_in_line_after_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn replace_characters_in_line_after_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
self.viewport
|
if let Some(row) = self.viewport.get_mut(self.cursor.y) {
|
||||||
.get_mut(self.cursor.y)
|
row.replace_and_pad_end(self.cursor.x, self.width, replace_with);
|
||||||
.unwrap()
|
}
|
||||||
.replace_and_pad_end(self.cursor.x, self.width, replace_with);
|
|
||||||
self.output_buffer.update_line(self.cursor.y);
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
@ -1109,7 +1118,7 @@ impl Grid {
|
|||||||
self.scroll_region = None;
|
self.scroll_region = None;
|
||||||
}
|
}
|
||||||
pub fn set_scroll_region_to_viewport_size(&mut self) {
|
pub fn set_scroll_region_to_viewport_size(&mut self) {
|
||||||
self.scroll_region = Some((0, self.height - 1));
|
self.scroll_region = Some((0, self.height.saturating_sub(1)));
|
||||||
}
|
}
|
||||||
pub fn delete_lines_in_scroll_region(
|
pub fn delete_lines_in_scroll_region(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -2,33 +2,37 @@ use std::sync::mpsc::channel;
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::unimplemented;
|
use std::unimplemented;
|
||||||
|
|
||||||
use crate::panes::{PaneDecoration, PaneId};
|
use crate::panes::PaneId;
|
||||||
use crate::pty::VteBytes;
|
use crate::pty::VteBytes;
|
||||||
use crate::tab::Pane;
|
use crate::tab::Pane;
|
||||||
use crate::ui::pane_boundaries_frame::PaneBoundariesFrame;
|
use crate::ui::pane_boundaries_frame::PaneFrame;
|
||||||
use crate::wasm_vm::PluginInstruction;
|
use crate::wasm_vm::PluginInstruction;
|
||||||
|
use zellij_utils::pane_size::Offset;
|
||||||
use zellij_utils::shared::ansi_len;
|
use zellij_utils::shared::ansi_len;
|
||||||
use zellij_utils::zellij_tile::prelude::PaletteColor;
|
use zellij_utils::zellij_tile::prelude::PaletteColor;
|
||||||
use zellij_utils::{channels::SenderWithContext, pane_size::PositionAndSize};
|
use zellij_utils::{
|
||||||
|
channels::SenderWithContext,
|
||||||
|
pane_size::{Dimension, PaneGeom},
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct PluginPane {
|
pub(crate) struct PluginPane {
|
||||||
pub pid: u32,
|
pub pid: u32,
|
||||||
pub should_render: bool,
|
pub should_render: bool,
|
||||||
pub selectable: bool,
|
pub selectable: bool,
|
||||||
pub invisible_borders: bool,
|
pub geom: PaneGeom,
|
||||||
pub position_and_size: PositionAndSize,
|
pub geom_override: Option<PaneGeom>,
|
||||||
pub position_and_size_override: Option<PositionAndSize>,
|
pub content_offset: Offset,
|
||||||
pub content_position_and_size: PositionAndSize,
|
|
||||||
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||||
pub active_at: Instant,
|
pub active_at: Instant,
|
||||||
pub pane_title: String,
|
pub pane_title: String,
|
||||||
pane_decoration: PaneDecoration,
|
frame: bool,
|
||||||
|
frame_color: Option<PaletteColor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PluginPane {
|
impl PluginPane {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pid: u32,
|
pid: u32,
|
||||||
position_and_size: PositionAndSize,
|
position_and_size: PaneGeom,
|
||||||
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||||
title: String,
|
title: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -36,53 +40,16 @@ impl PluginPane {
|
|||||||
pid,
|
pid,
|
||||||
should_render: true,
|
should_render: true,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
invisible_borders: false,
|
geom: position_and_size,
|
||||||
position_and_size,
|
geom_override: None,
|
||||||
position_and_size_override: None,
|
|
||||||
send_plugin_instructions,
|
send_plugin_instructions,
|
||||||
active_at: Instant::now(),
|
active_at: Instant::now(),
|
||||||
pane_decoration: PaneDecoration::ContentOffset((0, 0)),
|
frame: false,
|
||||||
content_position_and_size: position_and_size,
|
frame_color: None,
|
||||||
|
content_offset: Offset::default(),
|
||||||
pane_title: title,
|
pane_title: title,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_content_x(&self) -> usize {
|
|
||||||
self.get_content_posision_and_size().x
|
|
||||||
}
|
|
||||||
pub fn get_content_y(&self) -> usize {
|
|
||||||
self.get_content_posision_and_size().y
|
|
||||||
}
|
|
||||||
pub fn get_content_columns(&self) -> usize {
|
|
||||||
// content columns might differ from the pane's columns if the pane has a frame
|
|
||||||
// in that case they would be 2 less
|
|
||||||
self.get_content_posision_and_size().cols
|
|
||||||
}
|
|
||||||
pub fn get_content_rows(&self) -> usize {
|
|
||||||
// content rows might differ from the pane's rows if the pane has a frame
|
|
||||||
// in that case they would be 2 less
|
|
||||||
self.get_content_posision_and_size().rows
|
|
||||||
}
|
|
||||||
pub fn get_content_posision_and_size(&self) -> PositionAndSize {
|
|
||||||
self.content_position_and_size
|
|
||||||
}
|
|
||||||
fn redistribute_space(&mut self) {
|
|
||||||
let position_and_size = self
|
|
||||||
.position_and_size_override
|
|
||||||
.unwrap_or_else(|| self.position_and_size());
|
|
||||||
match &mut self.pane_decoration {
|
|
||||||
PaneDecoration::BoundariesFrame(boundaries_frame) => {
|
|
||||||
boundaries_frame.change_pos_and_size(position_and_size);
|
|
||||||
self.content_position_and_size = boundaries_frame.content_position_and_size();
|
|
||||||
}
|
|
||||||
PaneDecoration::ContentOffset((content_columns_offset, content_rows_offset)) => {
|
|
||||||
self.content_position_and_size = position_and_size;
|
|
||||||
self.content_position_and_size.cols =
|
|
||||||
position_and_size.cols - *content_columns_offset;
|
|
||||||
self.content_position_and_size.rows = position_and_size.rows - *content_rows_offset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.set_should_render(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pane for PluginPane {
|
impl Pane for PluginPane {
|
||||||
@ -90,51 +57,46 @@ impl Pane for PluginPane {
|
|||||||
// with something like a get_pos_and_sz() method underpinning all of them. Alternatively and
|
// with something like a get_pos_and_sz() method underpinning all of them. Alternatively and
|
||||||
// preferably, just use an enum and not a trait object
|
// preferably, just use an enum and not a trait object
|
||||||
fn x(&self) -> usize {
|
fn x(&self) -> usize {
|
||||||
self.position_and_size_override
|
self.geom_override.unwrap_or(self.geom).x
|
||||||
.unwrap_or(self.position_and_size)
|
|
||||||
.x
|
|
||||||
}
|
}
|
||||||
fn y(&self) -> usize {
|
fn y(&self) -> usize {
|
||||||
self.position_and_size_override
|
self.geom_override.unwrap_or(self.geom).y
|
||||||
.unwrap_or(self.position_and_size)
|
|
||||||
.y
|
|
||||||
}
|
}
|
||||||
fn rows(&self) -> usize {
|
fn rows(&self) -> usize {
|
||||||
self.position_and_size_override
|
self.geom_override.unwrap_or(self.geom).rows.as_usize()
|
||||||
.unwrap_or(self.position_and_size)
|
|
||||||
.rows
|
|
||||||
}
|
}
|
||||||
fn columns(&self) -> usize {
|
fn cols(&self) -> usize {
|
||||||
self.position_and_size_override
|
self.geom_override.unwrap_or(self.geom).cols.as_usize()
|
||||||
.unwrap_or(self.position_and_size)
|
}
|
||||||
.cols
|
fn get_content_x(&self) -> usize {
|
||||||
|
self.x() + self.content_offset.left
|
||||||
|
}
|
||||||
|
fn get_content_y(&self) -> usize {
|
||||||
|
self.y() + self.content_offset.top
|
||||||
}
|
}
|
||||||
fn get_content_columns(&self) -> usize {
|
fn get_content_columns(&self) -> usize {
|
||||||
self.get_content_columns()
|
// content columns might differ from the pane's columns if the pane has a frame
|
||||||
|
// in that case they would be 2 less
|
||||||
|
self.cols()
|
||||||
|
.saturating_sub(self.content_offset.left + self.content_offset.right)
|
||||||
}
|
}
|
||||||
fn get_content_rows(&self) -> usize {
|
fn get_content_rows(&self) -> usize {
|
||||||
self.get_content_rows()
|
// content rows might differ from the pane's rows if the pane has a frame
|
||||||
|
// in that case they would be 2 less
|
||||||
|
self.rows()
|
||||||
|
.saturating_sub(self.content_offset.top + self.content_offset.bottom)
|
||||||
}
|
}
|
||||||
fn reset_size_and_position_override(&mut self) {
|
fn reset_size_and_position_override(&mut self) {
|
||||||
self.position_and_size_override = None;
|
self.geom_override = None;
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
|
fn set_geom(&mut self, position_and_size: PaneGeom) {
|
||||||
self.position_and_size = *position_and_size;
|
self.geom = position_and_size;
|
||||||
self.redistribute_space();
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
// FIXME: This is obviously a bit outdated and needs the x and y moved into `size`
|
fn get_geom_override(&mut self, pane_geom: PaneGeom) {
|
||||||
fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
|
self.geom_override = Some(pane_geom);
|
||||||
let position_and_size_override = PositionAndSize {
|
self.should_render = true;
|
||||||
x,
|
|
||||||
y,
|
|
||||||
rows: size.rows,
|
|
||||||
cols: size.cols,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
self.position_and_size_override = Some(position_and_size_override);
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
}
|
||||||
fn handle_pty_bytes(&mut self, _event: VteBytes) {
|
fn handle_pty_bytes(&mut self, _event: VteBytes) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
@ -143,13 +105,16 @@ impl Pane for PluginPane {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn adjust_input_to_terminal(&self, _input_bytes: Vec<u8>) -> Vec<u8> {
|
fn adjust_input_to_terminal(&self, _input_bytes: Vec<u8>) -> Vec<u8> {
|
||||||
unimplemented!() // FIXME: Shouldn't need this implmented?
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn position_and_size(&self) -> PositionAndSize {
|
fn position_and_size(&self) -> PaneGeom {
|
||||||
self.position_and_size
|
self.geom
|
||||||
}
|
}
|
||||||
fn position_and_size_override(&self) -> Option<PositionAndSize> {
|
fn current_geom(&self) -> PaneGeom {
|
||||||
self.position_and_size_override
|
self.geom_override.unwrap_or(self.geom)
|
||||||
|
}
|
||||||
|
fn geom_override(&self) -> Option<PaneGeom> {
|
||||||
|
self.geom_override
|
||||||
}
|
}
|
||||||
fn should_render(&self) -> bool {
|
fn should_render(&self) -> bool {
|
||||||
self.should_render
|
self.should_render
|
||||||
@ -157,28 +122,12 @@ impl Pane for PluginPane {
|
|||||||
fn set_should_render(&mut self, should_render: bool) {
|
fn set_should_render(&mut self, should_render: bool) {
|
||||||
self.should_render = should_render;
|
self.should_render = should_render;
|
||||||
}
|
}
|
||||||
fn set_should_render_boundaries(&mut self, should_render: bool) {
|
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
|
||||||
boundaries_frame.set_should_render(should_render);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn selectable(&self) -> bool {
|
fn selectable(&self) -> bool {
|
||||||
self.selectable
|
self.selectable
|
||||||
}
|
}
|
||||||
fn set_selectable(&mut self, selectable: bool) {
|
fn set_selectable(&mut self, selectable: bool) {
|
||||||
self.selectable = selectable;
|
self.selectable = selectable;
|
||||||
}
|
}
|
||||||
fn set_invisible_borders(&mut self, invisible_borders: bool) {
|
|
||||||
self.invisible_borders = invisible_borders;
|
|
||||||
}
|
|
||||||
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_fixed_width(&mut self, fixed_width: usize) {
|
|
||||||
self.position_and_size.cols = fixed_width;
|
|
||||||
self.position_and_size.cols_fixed = true;
|
|
||||||
}
|
|
||||||
fn render(&mut self) -> Option<String> {
|
fn render(&mut self) -> Option<String> {
|
||||||
// if self.should_render {
|
// if self.should_render {
|
||||||
if true {
|
if true {
|
||||||
@ -200,10 +149,16 @@ impl Pane for PluginPane {
|
|||||||
|
|
||||||
self.should_render = false;
|
self.should_render = false;
|
||||||
let contents = buf_rx.recv().unwrap();
|
let contents = buf_rx.recv().unwrap();
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
// FIXME: This is a hack that assumes all fixed-size panes are borderless. This
|
||||||
if let Some(boundaries_frame_vte) = boundaries_frame.render() {
|
// will eventually need fixing!
|
||||||
vte_output.push_str(&boundaries_frame_vte);
|
if self.frame && !(self.geom.rows.is_fixed() || self.geom.cols.is_fixed()) {
|
||||||
}
|
let frame = PaneFrame {
|
||||||
|
geom: self.current_geom().into(),
|
||||||
|
title: self.pane_title.clone(),
|
||||||
|
color: self.frame_color,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
vte_output.push_str(&frame.render());
|
||||||
}
|
}
|
||||||
for (index, line) in contents.lines().enumerate() {
|
for (index, line) in contents.lines().enumerate() {
|
||||||
let actual_len = ansi_len(line);
|
let actual_len = ansi_len(line);
|
||||||
@ -257,68 +212,44 @@ impl Pane for PluginPane {
|
|||||||
fn pid(&self) -> PaneId {
|
fn pid(&self) -> PaneId {
|
||||||
PaneId::Plugin(self.pid)
|
PaneId::Plugin(self.pid)
|
||||||
}
|
}
|
||||||
fn reduce_height_down(&mut self, count: usize) {
|
fn reduce_height(&mut self, percent: f64) {
|
||||||
self.position_and_size.y += count;
|
if let Some(p) = self.geom.rows.as_percent() {
|
||||||
self.position_and_size.rows -= count;
|
self.geom.rows = Dimension::percent(p - percent);
|
||||||
self.redistribute_space();
|
self.should_render = true;
|
||||||
self.should_render = true;
|
}
|
||||||
}
|
}
|
||||||
fn increase_height_down(&mut self, count: usize) {
|
fn increase_height(&mut self, percent: f64) {
|
||||||
self.position_and_size.rows += count;
|
if let Some(p) = self.geom.rows.as_percent() {
|
||||||
self.redistribute_space();
|
self.geom.rows = Dimension::percent(p + percent);
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn increase_height_up(&mut self, count: usize) {
|
fn reduce_width(&mut self, percent: f64) {
|
||||||
self.position_and_size.y -= count;
|
if let Some(p) = self.geom.cols.as_percent() {
|
||||||
self.position_and_size.rows += count;
|
self.geom.cols = Dimension::percent(p - percent);
|
||||||
self.redistribute_space();
|
self.should_render = true;
|
||||||
self.should_render = true;
|
}
|
||||||
}
|
}
|
||||||
fn reduce_height_up(&mut self, count: usize) {
|
fn increase_width(&mut self, percent: f64) {
|
||||||
self.position_and_size.rows -= count;
|
if let Some(p) = self.geom.cols.as_percent() {
|
||||||
self.redistribute_space();
|
self.geom.cols = Dimension::percent(p + percent);
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn reduce_width_right(&mut self, count: usize) {
|
|
||||||
self.position_and_size.x += count;
|
|
||||||
self.position_and_size.cols -= count;
|
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
fn reduce_width_left(&mut self, count: usize) {
|
|
||||||
self.position_and_size.cols -= count;
|
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
fn increase_width_left(&mut self, count: usize) {
|
|
||||||
self.position_and_size.x -= count;
|
|
||||||
self.position_and_size.cols += count;
|
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
fn increase_width_right(&mut self, count: usize) {
|
|
||||||
self.position_and_size.cols += count;
|
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
}
|
||||||
fn push_down(&mut self, count: usize) {
|
fn push_down(&mut self, count: usize) {
|
||||||
self.position_and_size.y += count;
|
self.geom.y += count;
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn push_right(&mut self, count: usize) {
|
fn push_right(&mut self, count: usize) {
|
||||||
self.position_and_size.x += count;
|
self.geom.x += count;
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn pull_left(&mut self, count: usize) {
|
fn pull_left(&mut self, count: usize) {
|
||||||
self.position_and_size.x -= count;
|
self.geom.x -= count;
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn pull_up(&mut self, count: usize) {
|
fn pull_up(&mut self, count: usize) {
|
||||||
self.position_and_size.y -= count;
|
self.geom.y -= count;
|
||||||
self.redistribute_space();
|
|
||||||
self.should_render = true;
|
self.should_render = true;
|
||||||
}
|
}
|
||||||
fn scroll_up(&mut self, _count: usize) {
|
fn scroll_up(&mut self, _count: usize) {
|
||||||
@ -330,25 +261,6 @@ impl Pane for PluginPane {
|
|||||||
fn clear_scroll(&mut self) {
|
fn clear_scroll(&mut self) {
|
||||||
//unimplemented!()
|
//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> {
|
|
||||||
if self.position_and_size.rows_fixed {
|
|
||||||
Some(self.position_and_size.rows)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn max_width(&self) -> Option<usize> {
|
|
||||||
if self.position_and_size.cols_fixed {
|
|
||||||
Some(self.position_and_size.cols)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn invisible_borders(&self) -> bool {
|
|
||||||
self.invisible_borders
|
|
||||||
}
|
|
||||||
|
|
||||||
fn active_at(&self) -> Instant {
|
fn active_at(&self) -> Instant {
|
||||||
self.active_at
|
self.active_at
|
||||||
@ -357,61 +269,14 @@ impl Pane for PluginPane {
|
|||||||
fn set_active_at(&mut self, time: Instant) {
|
fn set_active_at(&mut self, time: Instant) {
|
||||||
self.active_at = time;
|
self.active_at = time;
|
||||||
}
|
}
|
||||||
|
fn set_frame(&mut self, frame: bool) {
|
||||||
|
self.frame = frame;
|
||||||
|
}
|
||||||
|
fn set_content_offset(&mut self, offset: Offset) {
|
||||||
|
self.content_offset = offset;
|
||||||
|
}
|
||||||
fn set_boundary_color(&mut self, color: Option<PaletteColor>) {
|
fn set_boundary_color(&mut self, color: Option<PaletteColor>) {
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
self.frame_color = color;
|
||||||
boundaries_frame.set_color(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn offset_content_columns(&mut self, by: usize) {
|
|
||||||
if !self.selectable {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let PaneDecoration::ContentOffset(content_offset) = &mut self.pane_decoration {
|
|
||||||
content_offset.0 = by;
|
|
||||||
} else {
|
|
||||||
self.pane_decoration = PaneDecoration::ContentOffset((by, 0));
|
|
||||||
}
|
|
||||||
self.redistribute_space();
|
|
||||||
self.set_should_render(true);
|
|
||||||
}
|
|
||||||
fn offset_content_rows(&mut self, by: usize) {
|
|
||||||
if !self.selectable {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let PaneDecoration::ContentOffset(content_offset) = &mut self.pane_decoration {
|
|
||||||
content_offset.1 = by;
|
|
||||||
} else {
|
|
||||||
self.pane_decoration = PaneDecoration::ContentOffset((0, by));
|
|
||||||
}
|
|
||||||
self.redistribute_space();
|
|
||||||
self.set_should_render(true);
|
|
||||||
}
|
|
||||||
fn show_boundaries_frame(&mut self, should_render_only_title: bool) {
|
|
||||||
if !self.selectable {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let position_and_size = self
|
|
||||||
.position_and_size_override
|
|
||||||
.unwrap_or(self.position_and_size);
|
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
|
||||||
boundaries_frame.render_only_title(should_render_only_title);
|
|
||||||
self.content_position_and_size = boundaries_frame.content_position_and_size();
|
|
||||||
} else {
|
|
||||||
let mut boundaries_frame =
|
|
||||||
PaneBoundariesFrame::new(position_and_size, self.pane_title.clone());
|
|
||||||
boundaries_frame.render_only_title(should_render_only_title);
|
|
||||||
self.content_position_and_size = boundaries_frame.content_position_and_size();
|
|
||||||
self.pane_decoration = PaneDecoration::BoundariesFrame(boundaries_frame);
|
|
||||||
}
|
|
||||||
self.redistribute_space();
|
|
||||||
self.set_should_render(true);
|
|
||||||
}
|
|
||||||
fn remove_boundaries_frame(&mut self) {
|
|
||||||
if !self.selectable {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.pane_decoration = PaneDecoration::ContentOffset((0, 0));
|
|
||||||
self.redistribute_space();
|
|
||||||
self.set_should_render(true);
|
self.set_should_render(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,3 @@
|
|||||||
use zellij_utils::position::Position;
|
|
||||||
use zellij_utils::zellij_tile::prelude::PaletteColor;
|
|
||||||
use zellij_utils::{vte, zellij_tile};
|
|
||||||
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::os::unix::io::RawFd;
|
|
||||||
use std::time::{self, Instant};
|
|
||||||
use zellij_tile::data::Palette;
|
|
||||||
|
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
|
||||||
|
|
||||||
use crate::panes::AnsiCode;
|
use crate::panes::AnsiCode;
|
||||||
use crate::panes::{
|
use crate::panes::{
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
@ -18,10 +7,20 @@ use crate::panes::{
|
|||||||
};
|
};
|
||||||
use crate::pty::VteBytes;
|
use crate::pty::VteBytes;
|
||||||
use crate::tab::Pane;
|
use crate::tab::Pane;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::time::{self, Instant};
|
||||||
|
use zellij_utils::pane_size::Offset;
|
||||||
|
use zellij_utils::{
|
||||||
|
pane_size::{Dimension, PaneGeom},
|
||||||
|
position::Position,
|
||||||
|
vte,
|
||||||
|
zellij_tile::data::{Palette, PaletteColor},
|
||||||
|
};
|
||||||
|
|
||||||
pub const SELECTION_SCROLL_INTERVAL_MS: u64 = 10;
|
pub const SELECTION_SCROLL_INTERVAL_MS: u64 = 10;
|
||||||
|
|
||||||
use crate::ui::pane_boundaries_frame::PaneBoundariesFrame;
|
use crate::ui::pane_boundaries_frame::PaneFrame;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
|
||||||
pub enum PaneId {
|
pub enum PaneId {
|
||||||
@ -29,24 +28,22 @@ pub enum PaneId {
|
|||||||
Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
|
Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PaneDecoration {
|
// FIXME: This should hold an os_api handle so that terminal panes can set their own size via FD in
|
||||||
BoundariesFrame(PaneBoundariesFrame),
|
// their `reflow_lines()` method. Drop a Box<dyn ServerOsApi> in here somewhere.
|
||||||
ContentOffset((usize, usize)), // (columns, rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TerminalPane {
|
pub struct TerminalPane {
|
||||||
pub grid: Grid,
|
pub grid: Grid,
|
||||||
pub pid: RawFd,
|
pub pid: RawFd,
|
||||||
pub selectable: bool,
|
pub selectable: bool,
|
||||||
position_and_size: PositionAndSize,
|
pub geom: PaneGeom,
|
||||||
position_and_size_override: Option<PositionAndSize>,
|
pub geom_override: Option<PaneGeom>,
|
||||||
pub active_at: Instant,
|
pub active_at: Instant,
|
||||||
pub colors: Palette,
|
pub colors: Palette,
|
||||||
vte_parser: vte::Parser,
|
vte_parser: vte::Parser,
|
||||||
selection_scrolled_at: time::Instant,
|
selection_scrolled_at: time::Instant,
|
||||||
content_position_and_size: PositionAndSize,
|
content_offset: Offset,
|
||||||
pane_title: String,
|
pane_title: String,
|
||||||
pane_decoration: PaneDecoration,
|
frame: Option<PaneFrame>,
|
||||||
|
frame_color: Option<PaletteColor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pane for TerminalPane {
|
impl Pane for TerminalPane {
|
||||||
@ -59,32 +56,38 @@ impl Pane for TerminalPane {
|
|||||||
fn rows(&self) -> usize {
|
fn rows(&self) -> usize {
|
||||||
self.get_rows()
|
self.get_rows()
|
||||||
}
|
}
|
||||||
fn columns(&self) -> usize {
|
fn cols(&self) -> usize {
|
||||||
self.get_columns()
|
self.get_columns()
|
||||||
}
|
}
|
||||||
|
fn get_content_x(&self) -> usize {
|
||||||
|
self.get_x() + self.content_offset.left
|
||||||
|
}
|
||||||
|
fn get_content_y(&self) -> usize {
|
||||||
|
self.get_y() + self.content_offset.top
|
||||||
|
}
|
||||||
fn get_content_columns(&self) -> usize {
|
fn get_content_columns(&self) -> usize {
|
||||||
self.get_content_columns()
|
// content columns might differ from the pane's columns if the pane has a frame
|
||||||
|
// in that case they would be 2 less
|
||||||
|
self.get_columns()
|
||||||
|
.saturating_sub(self.content_offset.left + self.content_offset.right)
|
||||||
}
|
}
|
||||||
fn get_content_rows(&self) -> usize {
|
fn get_content_rows(&self) -> usize {
|
||||||
self.get_content_rows()
|
// content rows might differ from the pane's rows if the pane has a frame
|
||||||
|
// in that case they would be 2 less
|
||||||
|
self.get_rows()
|
||||||
|
.saturating_sub(self.content_offset.top + self.content_offset.bottom)
|
||||||
}
|
}
|
||||||
fn reset_size_and_position_override(&mut self) {
|
fn reset_size_and_position_override(&mut self) {
|
||||||
self.position_and_size_override = None;
|
self.geom_override = None;
|
||||||
self.redistribute_space();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
|
fn set_geom(&mut self, position_and_size: PaneGeom) {
|
||||||
self.position_and_size = *position_and_size;
|
self.geom = position_and_size;
|
||||||
self.redistribute_space();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
|
fn get_geom_override(&mut self, pane_geom: PaneGeom) {
|
||||||
self.position_and_size_override = Some(PositionAndSize {
|
self.geom_override = Some(pane_geom);
|
||||||
x,
|
self.reflow_lines();
|
||||||
y,
|
|
||||||
rows: size.rows,
|
|
||||||
cols: size.cols,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
}
|
||||||
fn handle_pty_bytes(&mut self, bytes: VteBytes) {
|
fn handle_pty_bytes(&mut self, bytes: VteBytes) {
|
||||||
for byte in bytes.iter() {
|
for byte in bytes.iter() {
|
||||||
@ -94,17 +97,10 @@ impl Pane for TerminalPane {
|
|||||||
}
|
}
|
||||||
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
|
||||||
// (x, y)
|
// (x, y)
|
||||||
let (x_offset, y_offset) = match &self.pane_decoration {
|
let Offset { top, left, .. } = self.content_offset;
|
||||||
PaneDecoration::BoundariesFrame(boundries_frame) => {
|
|
||||||
let (content_columns_offset, content_rows_offset) =
|
|
||||||
boundries_frame.content_offset();
|
|
||||||
(content_columns_offset, content_rows_offset)
|
|
||||||
}
|
|
||||||
PaneDecoration::ContentOffset(_) => (0, 0),
|
|
||||||
};
|
|
||||||
self.grid
|
self.grid
|
||||||
.cursor_coordinates()
|
.cursor_coordinates()
|
||||||
.map(|(x, y)| (x + x_offset, y + y_offset))
|
.map(|(x, y)| (x + left, y + top))
|
||||||
}
|
}
|
||||||
fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8> {
|
fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8> {
|
||||||
// there are some cases in which the terminal state means that input sent to it
|
// there are some cases in which the terminal state means that input sent to it
|
||||||
@ -157,11 +153,14 @@ impl Pane for TerminalPane {
|
|||||||
};
|
};
|
||||||
input_bytes
|
input_bytes
|
||||||
}
|
}
|
||||||
fn position_and_size(&self) -> PositionAndSize {
|
fn position_and_size(&self) -> PaneGeom {
|
||||||
self.position_and_size
|
self.geom
|
||||||
}
|
}
|
||||||
fn position_and_size_override(&self) -> Option<PositionAndSize> {
|
fn current_geom(&self) -> PaneGeom {
|
||||||
self.position_and_size_override
|
self.geom_override.unwrap_or(self.geom)
|
||||||
|
}
|
||||||
|
fn geom_override(&self) -> Option<PaneGeom> {
|
||||||
|
self.geom_override
|
||||||
}
|
}
|
||||||
fn should_render(&self) -> bool {
|
fn should_render(&self) -> bool {
|
||||||
self.grid.should_render
|
self.grid.should_render
|
||||||
@ -169,14 +168,10 @@ impl Pane for TerminalPane {
|
|||||||
fn set_should_render(&mut self, should_render: bool) {
|
fn set_should_render(&mut self, should_render: bool) {
|
||||||
self.grid.should_render = should_render;
|
self.grid.should_render = should_render;
|
||||||
}
|
}
|
||||||
fn set_should_render_boundaries(&mut self, should_render: bool) {
|
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
|
||||||
boundaries_frame.set_should_render(should_render);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn render_full_viewport(&mut self) {
|
fn render_full_viewport(&mut self) {
|
||||||
// this marks the pane for a full re-render, rather than just rendering the
|
// this marks the pane for a full re-render, rather than just rendering the
|
||||||
// diff as it usually does with the OutputBuffer
|
// diff as it usually does with the OutputBuffer
|
||||||
|
self.frame.replace(PaneFrame::default());
|
||||||
self.grid.render_full_viewport();
|
self.grid.render_full_viewport();
|
||||||
}
|
}
|
||||||
fn selectable(&self) -> bool {
|
fn selectable(&self) -> bool {
|
||||||
@ -185,17 +180,6 @@ impl Pane for TerminalPane {
|
|||||||
fn set_selectable(&mut self, selectable: bool) {
|
fn set_selectable(&mut self, selectable: bool) {
|
||||||
self.selectable = selectable;
|
self.selectable = selectable;
|
||||||
}
|
}
|
||||||
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_fixed_width(&mut self, fixed_width: usize) {
|
|
||||||
self.position_and_size.cols = fixed_width;
|
|
||||||
self.position_and_size.cols_fixed = true;
|
|
||||||
}
|
|
||||||
fn set_invisible_borders(&mut self, _invisible_borders: bool) {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
fn render(&mut self) -> Option<String> {
|
fn render(&mut self) -> Option<String> {
|
||||||
if self.should_render() {
|
if self.should_render() {
|
||||||
let mut vte_output = String::new();
|
let mut vte_output = String::new();
|
||||||
@ -254,11 +238,20 @@ impl Pane for TerminalPane {
|
|||||||
}
|
}
|
||||||
character_styles.clear();
|
character_styles.clear();
|
||||||
}
|
}
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
if let Some(last_frame) = &self.frame {
|
||||||
boundaries_frame.update_scroll(self.grid.scrollback_position_and_length());
|
let frame = PaneFrame {
|
||||||
boundaries_frame.update_title(self.grid.title.as_ref());
|
geom: self.current_geom().into(),
|
||||||
if let Some(boundaries_frame_vte) = boundaries_frame.render() {
|
title: self
|
||||||
vte_output.push_str(&boundaries_frame_vte);
|
.grid
|
||||||
|
.title
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| self.pane_title.clone()),
|
||||||
|
scroll_position: self.grid.scrollback_position_and_length(),
|
||||||
|
color: self.frame_color,
|
||||||
|
};
|
||||||
|
if &frame != last_frame {
|
||||||
|
vte_output.push_str(&frame.render());
|
||||||
|
self.frame = Some(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.set_should_render(false);
|
self.set_should_render(false);
|
||||||
@ -270,57 +263,45 @@ impl Pane for TerminalPane {
|
|||||||
fn pid(&self) -> PaneId {
|
fn pid(&self) -> PaneId {
|
||||||
PaneId::Terminal(self.pid)
|
PaneId::Terminal(self.pid)
|
||||||
}
|
}
|
||||||
fn reduce_height_down(&mut self, count: usize) {
|
fn reduce_height(&mut self, percent: f64) {
|
||||||
self.position_and_size.y += count;
|
if let Some(p) = self.geom.rows.as_percent() {
|
||||||
self.position_and_size.rows -= count;
|
self.geom.rows = Dimension::percent(p - percent);
|
||||||
self.redistribute_space();
|
self.set_should_render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn increase_height_down(&mut self, count: usize) {
|
fn increase_height(&mut self, percent: f64) {
|
||||||
self.position_and_size.rows += count;
|
if let Some(p) = self.geom.rows.as_percent() {
|
||||||
self.redistribute_space();
|
self.geom.rows = Dimension::percent(p + percent);
|
||||||
|
self.set_should_render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn increase_height_up(&mut self, count: usize) {
|
fn reduce_width(&mut self, percent: f64) {
|
||||||
self.position_and_size.y -= count;
|
if let Some(p) = self.geom.cols.as_percent() {
|
||||||
self.position_and_size.rows += count;
|
self.geom.cols = Dimension::percent(p - percent);
|
||||||
self.redistribute_space();
|
self.set_should_render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn reduce_height_up(&mut self, count: usize) {
|
fn increase_width(&mut self, percent: f64) {
|
||||||
self.position_and_size.rows -= count;
|
if let Some(p) = self.geom.cols.as_percent() {
|
||||||
self.redistribute_space();
|
self.geom.cols = Dimension::percent(p + percent);
|
||||||
}
|
self.set_should_render(true);
|
||||||
fn reduce_width_right(&mut self, count: usize) {
|
}
|
||||||
self.position_and_size.x += count;
|
|
||||||
self.position_and_size.cols -= count;
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
|
||||||
fn reduce_width_left(&mut self, count: usize) {
|
|
||||||
self.position_and_size.cols -= count;
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
|
||||||
fn increase_width_left(&mut self, count: usize) {
|
|
||||||
self.position_and_size.x -= count;
|
|
||||||
self.position_and_size.cols += count;
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
|
||||||
fn increase_width_right(&mut self, count: usize) {
|
|
||||||
self.position_and_size.cols += count;
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
}
|
||||||
fn push_down(&mut self, count: usize) {
|
fn push_down(&mut self, count: usize) {
|
||||||
self.position_and_size.y += count;
|
self.geom.y += count;
|
||||||
self.redistribute_space();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
fn push_right(&mut self, count: usize) {
|
fn push_right(&mut self, count: usize) {
|
||||||
self.position_and_size.x += count;
|
self.geom.x += count;
|
||||||
self.redistribute_space();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
fn pull_left(&mut self, count: usize) {
|
fn pull_left(&mut self, count: usize) {
|
||||||
self.position_and_size.x -= count;
|
self.geom.x -= count;
|
||||||
self.redistribute_space();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
fn pull_up(&mut self, count: usize) {
|
fn pull_up(&mut self, count: usize) {
|
||||||
self.position_and_size.y -= count;
|
self.geom.y -= count;
|
||||||
self.redistribute_space();
|
self.reflow_lines();
|
||||||
}
|
}
|
||||||
fn scroll_up(&mut self, count: usize) {
|
fn scroll_up(&mut self, count: usize) {
|
||||||
self.grid.move_viewport_up(count);
|
self.grid.move_viewport_up(count);
|
||||||
@ -392,73 +373,47 @@ impl Pane for TerminalPane {
|
|||||||
self.grid.get_selected_text()
|
self.grid.get_selected_text()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_frame(&mut self, frame: bool) {
|
||||||
|
self.frame = if frame {
|
||||||
|
Some(PaneFrame::default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_content_offset(&mut self, offset: Offset) {
|
||||||
|
self.content_offset = offset;
|
||||||
|
self.reflow_lines();
|
||||||
|
}
|
||||||
|
|
||||||
fn set_boundary_color(&mut self, color: Option<PaletteColor>) {
|
fn set_boundary_color(&mut self, color: Option<PaletteColor>) {
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
self.frame_color = color;
|
||||||
if boundaries_frame.color != color {
|
self.set_should_render(true);
|
||||||
boundaries_frame.set_color(color);
|
|
||||||
self.set_should_render(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn relative_position(&self, position_on_screen: &Position) -> Position {
|
|
||||||
let pane_position_and_size = self.get_content_posision_and_size();
|
|
||||||
position_on_screen.relative_to(&pane_position_and_size)
|
|
||||||
}
|
|
||||||
fn offset_content_columns(&mut self, by: usize) {
|
|
||||||
if let PaneDecoration::ContentOffset(content_offset) = &mut self.pane_decoration {
|
|
||||||
content_offset.0 = by;
|
|
||||||
} else {
|
|
||||||
self.pane_decoration = PaneDecoration::ContentOffset((by, 0));
|
|
||||||
}
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
|
||||||
fn offset_content_rows(&mut self, by: usize) {
|
|
||||||
if let PaneDecoration::ContentOffset(content_offset) = &mut self.pane_decoration {
|
|
||||||
content_offset.1 = by;
|
|
||||||
} else {
|
|
||||||
self.pane_decoration = PaneDecoration::ContentOffset((0, by));
|
|
||||||
}
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
|
||||||
fn show_boundaries_frame(&mut self, only_title: bool) {
|
|
||||||
let position_and_size = self
|
|
||||||
.position_and_size_override
|
|
||||||
.unwrap_or(self.position_and_size);
|
|
||||||
if let PaneDecoration::BoundariesFrame(boundaries_frame) = &mut self.pane_decoration {
|
|
||||||
boundaries_frame.render_only_title(only_title);
|
|
||||||
self.content_position_and_size = boundaries_frame.content_position_and_size();
|
|
||||||
} else {
|
|
||||||
let mut boundaries_frame =
|
|
||||||
PaneBoundariesFrame::new(position_and_size, self.pane_title.clone());
|
|
||||||
boundaries_frame.render_only_title(only_title);
|
|
||||||
self.content_position_and_size = boundaries_frame.content_position_and_size();
|
|
||||||
self.pane_decoration = PaneDecoration::BoundariesFrame(boundaries_frame);
|
|
||||||
}
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
|
||||||
fn remove_boundaries_frame(&mut self) {
|
|
||||||
self.pane_decoration = PaneDecoration::ContentOffset((0, 0));
|
|
||||||
self.redistribute_space();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalPane {
|
impl TerminalPane {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pid: RawFd,
|
pid: RawFd,
|
||||||
position_and_size: PositionAndSize,
|
position_and_size: PaneGeom,
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
pane_position: usize,
|
pane_index: usize,
|
||||||
) -> TerminalPane {
|
) -> TerminalPane {
|
||||||
let initial_pane_title = format!("Pane #{}", pane_position);
|
let initial_pane_title = format!("Pane #{}", pane_index);
|
||||||
let grid = Grid::new(position_and_size.rows, position_and_size.cols, palette);
|
let grid = Grid::new(
|
||||||
|
position_and_size.rows.as_usize(),
|
||||||
|
position_and_size.cols.as_usize(),
|
||||||
|
palette,
|
||||||
|
);
|
||||||
TerminalPane {
|
TerminalPane {
|
||||||
pane_decoration: PaneDecoration::ContentOffset((0, 0)),
|
frame: None,
|
||||||
content_position_and_size: position_and_size,
|
frame_color: None,
|
||||||
|
content_offset: Offset::default(),
|
||||||
pid,
|
pid,
|
||||||
grid,
|
grid,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
position_and_size,
|
geom: position_and_size,
|
||||||
position_and_size_override: None,
|
geom_override: None,
|
||||||
vte_parser: vte::Parser::new(),
|
vte_parser: vte::Parser::new(),
|
||||||
active_at: Instant::now(),
|
active_at: Instant::now(),
|
||||||
colors: palette,
|
colors: palette,
|
||||||
@ -467,52 +422,33 @@ impl TerminalPane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_x(&self) -> usize {
|
pub fn get_x(&self) -> usize {
|
||||||
match self.position_and_size_override.as_ref() {
|
match self.geom_override {
|
||||||
Some(position_and_size_override) => position_and_size_override.x,
|
Some(position_and_size_override) => position_and_size_override.x,
|
||||||
None => self.position_and_size.x,
|
None => self.geom.x,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_y(&self) -> usize {
|
pub fn get_y(&self) -> usize {
|
||||||
match self.position_and_size_override.as_ref() {
|
match self.geom_override {
|
||||||
Some(position_and_size_override) => position_and_size_override.y,
|
Some(position_and_size_override) => position_and_size_override.y,
|
||||||
None => self.position_and_size.y,
|
None => self.geom.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_columns(&self) -> usize {
|
pub fn get_columns(&self) -> usize {
|
||||||
match self.position_and_size_override.as_ref() {
|
match self.geom_override {
|
||||||
Some(position_and_size_override) => position_and_size_override.cols,
|
Some(position_and_size_override) => position_and_size_override.cols.as_usize(),
|
||||||
None => self.position_and_size.cols,
|
None => self.geom.cols.as_usize(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_rows(&self) -> usize {
|
pub fn get_rows(&self) -> usize {
|
||||||
match self.position_and_size_override.as_ref() {
|
match self.geom_override {
|
||||||
Some(position_and_size_override) => position_and_size_override.rows,
|
Some(position_and_size_override) => position_and_size_override.rows.as_usize(),
|
||||||
None => self.position_and_size.rows,
|
None => self.geom.rows.as_usize(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_content_x(&self) -> usize {
|
|
||||||
self.get_content_posision_and_size().x
|
|
||||||
}
|
|
||||||
pub fn get_content_y(&self) -> usize {
|
|
||||||
self.get_content_posision_and_size().y
|
|
||||||
}
|
|
||||||
pub fn get_content_columns(&self) -> usize {
|
|
||||||
// content columns might differ from the pane's columns if the pane has a frame
|
|
||||||
// in that case they would be 2 less
|
|
||||||
self.get_content_posision_and_size().cols
|
|
||||||
}
|
|
||||||
pub fn get_content_rows(&self) -> usize {
|
|
||||||
// content rows might differ from the pane's rows if the pane has a frame
|
|
||||||
// in that case they would be 2 less
|
|
||||||
self.get_content_posision_and_size().rows
|
|
||||||
}
|
|
||||||
pub fn get_content_posision_and_size(&self) -> PositionAndSize {
|
|
||||||
self.content_position_and_size
|
|
||||||
}
|
|
||||||
fn reflow_lines(&mut self) {
|
fn reflow_lines(&mut self) {
|
||||||
let rows = self.get_content_rows();
|
let rows = self.get_content_rows();
|
||||||
let columns = self.get_content_columns();
|
let cols = self.get_content_columns();
|
||||||
self.grid.change_size(rows, columns);
|
self.grid.change_size(rows, cols);
|
||||||
self.set_should_render(true);
|
self.set_should_render(true);
|
||||||
}
|
}
|
||||||
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
||||||
@ -522,24 +458,6 @@ impl TerminalPane {
|
|||||||
// (x, y)
|
// (x, y)
|
||||||
self.grid.cursor_coordinates()
|
self.grid.cursor_coordinates()
|
||||||
}
|
}
|
||||||
fn redistribute_space(&mut self) {
|
|
||||||
let position_and_size = self
|
|
||||||
.position_and_size_override
|
|
||||||
.unwrap_or_else(|| self.position_and_size());
|
|
||||||
match &mut self.pane_decoration {
|
|
||||||
PaneDecoration::BoundariesFrame(boundaries_frame) => {
|
|
||||||
boundaries_frame.change_pos_and_size(position_and_size);
|
|
||||||
self.content_position_and_size = boundaries_frame.content_position_and_size();
|
|
||||||
}
|
|
||||||
PaneDecoration::ContentOffset((content_columns_offset, content_rows_offset)) => {
|
|
||||||
self.content_position_and_size = position_and_size;
|
|
||||||
self.content_position_and_size.cols =
|
|
||||||
position_and_size.cols - *content_columns_offset;
|
|
||||||
self.content_position_and_size.rows = position_and_size.rows - *content_rows_offset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.reflow_lines();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
use super::super::TerminalPane;
|
use super::super::TerminalPane;
|
||||||
use crate::tab::Pane;
|
use crate::tab::Pane;
|
||||||
use ::insta::assert_snapshot;
|
use ::insta::assert_snapshot;
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
use zellij_utils::pane_size::PaneGeom;
|
||||||
use zellij_utils::zellij_tile::data::Palette;
|
use zellij_utils::zellij_tile::data::Palette;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn scrolling_inside_a_pane() {
|
pub fn scrolling_inside_a_pane() {
|
||||||
let fake_win_size = PositionAndSize {
|
let mut fake_win_size = PaneGeom::default();
|
||||||
cols: 121,
|
fake_win_size.cols.set_inner(121);
|
||||||
rows: 20,
|
fake_win_size.rows.set_inner(20);
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let pid = 1;
|
let pid = 1;
|
||||||
let palette = Palette::default();
|
let palette = Palette::default();
|
||||||
let mut terminal_pane = TerminalPane::new(pid, fake_win_size, palette, 0); // 0 is the pane index
|
let mut terminal_pane = TerminalPane::new(pid, fake_win_size, palette, 0); // 0 is the pane index
|
||||||
|
@ -60,7 +60,7 @@ pub(crate) struct Pty {
|
|||||||
task_handles: HashMap<RawFd, JoinHandle<()>>,
|
task_handles: HashMap<RawFd, JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pty_thread_main(mut pty: Pty, maybe_layout: Option<LayoutFromYaml>) {
|
pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
|
||||||
loop {
|
loop {
|
||||||
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
|
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
|
||||||
err_ctx.add_call(ContextType::Pty((&event).into()));
|
err_ctx.add_call(ContextType::Pty((&event).into()));
|
||||||
@ -87,16 +87,8 @@ pub(crate) fn pty_thread_main(mut pty: Pty, maybe_layout: Option<LayoutFromYaml>
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
PtyInstruction::NewTab(terminal_action, tab_layout) => {
|
PtyInstruction::NewTab(terminal_action, tab_layout) => {
|
||||||
if let Some(layout) = maybe_layout.clone() {
|
let merged_layout = layout.template.clone().insert_tab_layout(tab_layout);
|
||||||
let merged_layout = layout.template.insert_tab_layout(tab_layout);
|
pty.spawn_terminals_for_layout(merged_layout.into(), terminal_action.clone());
|
||||||
pty.spawn_terminals_for_layout(merged_layout.into(), terminal_action.clone());
|
|
||||||
} else {
|
|
||||||
let pid = pty.spawn_terminal(terminal_action.clone());
|
|
||||||
pty.bus
|
|
||||||
.senders
|
|
||||||
.send_to_screen(ScreenInstruction::NewTab(pid))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PtyInstruction::ClosePane(id) => {
|
PtyInstruction::ClosePane(id) => {
|
||||||
pty.close_pane(id);
|
pty.close_pane(id);
|
||||||
|
@ -5,6 +5,7 @@ use std::os::unix::io::RawFd;
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use zellij_utils::pane_size::Size;
|
||||||
use zellij_utils::{input::layout::Layout, position::Position, zellij_tile};
|
use zellij_utils::{input::layout::Layout, position::Position, zellij_tile};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -20,7 +21,6 @@ use zellij_utils::{
|
|||||||
errors::{ContextType, ScreenContext},
|
errors::{ContextType, ScreenContext},
|
||||||
input::{get_mode_info, options::Options},
|
input::{get_mode_info, options::Options},
|
||||||
ipc::ClientAttributes,
|
ipc::ClientAttributes,
|
||||||
pane_size::PositionAndSize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Instructions that can be sent to the [`Screen`].
|
/// Instructions that can be sent to the [`Screen`].
|
||||||
@ -58,12 +58,8 @@ pub(crate) enum ScreenInstruction {
|
|||||||
ToggleActiveTerminalFullscreen,
|
ToggleActiveTerminalFullscreen,
|
||||||
TogglePaneFrames,
|
TogglePaneFrames,
|
||||||
SetSelectable(PaneId, bool, usize),
|
SetSelectable(PaneId, bool, usize),
|
||||||
SetFixedHeight(PaneId, usize, usize),
|
|
||||||
SetFixedWidth(PaneId, usize, usize),
|
|
||||||
SetInvisibleBorders(PaneId, bool, usize),
|
|
||||||
ClosePane(PaneId),
|
ClosePane(PaneId),
|
||||||
ApplyLayout(Layout, Vec<RawFd>),
|
ApplyLayout(Layout, Vec<RawFd>),
|
||||||
NewTab(RawFd),
|
|
||||||
SwitchTabNext,
|
SwitchTabNext,
|
||||||
SwitchTabPrev,
|
SwitchTabPrev,
|
||||||
ToggleActiveSyncTab,
|
ToggleActiveSyncTab,
|
||||||
@ -71,7 +67,7 @@ pub(crate) enum ScreenInstruction {
|
|||||||
GoToTab(u32),
|
GoToTab(u32),
|
||||||
ToggleTab,
|
ToggleTab,
|
||||||
UpdateTabName(Vec<u8>),
|
UpdateTabName(Vec<u8>),
|
||||||
TerminalResize(PositionAndSize),
|
TerminalResize(Size),
|
||||||
ChangeMode(ModeInfo),
|
ChangeMode(ModeInfo),
|
||||||
LeftClick(Position),
|
LeftClick(Position),
|
||||||
MouseRelease(Position),
|
MouseRelease(Position),
|
||||||
@ -116,12 +112,8 @@ impl From<&ScreenInstruction> for ScreenContext {
|
|||||||
}
|
}
|
||||||
ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames,
|
ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames,
|
||||||
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
|
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
|
||||||
ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders,
|
|
||||||
ScreenInstruction::SetFixedHeight(..) => ScreenContext::SetFixedHeight,
|
|
||||||
ScreenInstruction::SetFixedWidth(..) => ScreenContext::SetFixedWidth,
|
|
||||||
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
|
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
|
||||||
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
|
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
|
||||||
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
|
|
||||||
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
|
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
|
||||||
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
|
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
|
||||||
ScreenInstruction::CloseTab => ScreenContext::CloseTab,
|
ScreenInstruction::CloseTab => ScreenContext::CloseTab,
|
||||||
@ -151,7 +143,7 @@ pub(crate) struct Screen {
|
|||||||
/// A map between this [`Screen`]'s tabs and their ID/key.
|
/// A map between this [`Screen`]'s tabs and their ID/key.
|
||||||
tabs: BTreeMap<usize, Tab>,
|
tabs: BTreeMap<usize, Tab>,
|
||||||
/// The full size of this [`Screen`].
|
/// The full size of this [`Screen`].
|
||||||
position_and_size: PositionAndSize,
|
size: Size,
|
||||||
/// The index of this [`Screen`]'s active [`Tab`].
|
/// The index of this [`Screen`]'s active [`Tab`].
|
||||||
active_tab_index: Option<usize>,
|
active_tab_index: Option<usize>,
|
||||||
tab_history: Vec<Option<usize>>,
|
tab_history: Vec<Option<usize>>,
|
||||||
@ -174,7 +166,7 @@ impl Screen {
|
|||||||
Screen {
|
Screen {
|
||||||
bus,
|
bus,
|
||||||
max_panes,
|
max_panes,
|
||||||
position_and_size: client_attributes.position_and_size,
|
size: client_attributes.size,
|
||||||
colors: client_attributes.palette,
|
colors: client_attributes.palette,
|
||||||
active_tab_index: None,
|
active_tab_index: None,
|
||||||
tabs: BTreeMap::new(),
|
tabs: BTreeMap::new(),
|
||||||
@ -185,32 +177,6 @@ impl Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`Tab`] in this [`Screen`], containing a single
|
|
||||||
/// [pane](crate::client::panes) with PTY file descriptor `pane_id`.
|
|
||||||
pub fn new_tab(&mut self, pane_id: RawFd) {
|
|
||||||
let tab_index = self.get_new_tab_index();
|
|
||||||
let position = self.tabs.len();
|
|
||||||
let tab = Tab::new(
|
|
||||||
tab_index,
|
|
||||||
position,
|
|
||||||
String::new(),
|
|
||||||
&self.position_and_size,
|
|
||||||
self.bus.os_input.as_ref().unwrap().clone(),
|
|
||||||
self.bus.senders.clone(),
|
|
||||||
self.max_panes,
|
|
||||||
Some(PaneId::Terminal(pane_id)),
|
|
||||||
self.mode_info.clone(),
|
|
||||||
self.colors,
|
|
||||||
self.session_state.clone(),
|
|
||||||
self.draw_pane_frames,
|
|
||||||
);
|
|
||||||
self.tab_history.push(self.active_tab_index);
|
|
||||||
self.active_tab_index = Some(tab_index);
|
|
||||||
self.tabs.insert(tab_index, tab);
|
|
||||||
self.update_tabs();
|
|
||||||
self.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the index where a new [`Tab`] should be created in this [`Screen`].
|
/// Returns the index where a new [`Tab`] should be created in this [`Screen`].
|
||||||
/// Currently, this is right after the last currently existing tab, or `0` if
|
/// Currently, this is right after the last currently existing tab, or `0` if
|
||||||
/// no tabs exist in this screen yet.
|
/// no tabs exist in this screen yet.
|
||||||
@ -308,8 +274,8 @@ impl Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize_to_screen(&mut self, new_screen_size: PositionAndSize) {
|
pub fn resize_to_screen(&mut self, new_screen_size: Size) {
|
||||||
self.position_and_size = new_screen_size;
|
self.size = new_screen_size;
|
||||||
for (_, tab) in self.tabs.iter_mut() {
|
for (_, tab) in self.tabs.iter_mut() {
|
||||||
tab.resize_whole_tab(new_screen_size);
|
tab.resize_whole_tab(new_screen_size);
|
||||||
}
|
}
|
||||||
@ -377,11 +343,10 @@ impl Screen {
|
|||||||
tab_index,
|
tab_index,
|
||||||
position,
|
position,
|
||||||
String::new(),
|
String::new(),
|
||||||
&self.position_and_size,
|
self.size,
|
||||||
self.bus.os_input.as_ref().unwrap().clone(),
|
self.bus.os_input.as_ref().unwrap().clone(),
|
||||||
self.bus.senders.clone(),
|
self.bus.senders.clone(),
|
||||||
self.max_panes,
|
self.max_panes,
|
||||||
None,
|
|
||||||
self.mode_info.clone(),
|
self.mode_info.clone(),
|
||||||
self.colors,
|
self.colors,
|
||||||
self.session_state.clone(),
|
self.session_state.clone(),
|
||||||
@ -657,43 +622,6 @@ pub(crate) fn screen_thread_main(
|
|||||||
|tab| tab.set_pane_selectable(id, selectable),
|
|tab| tab.set_pane_selectable(id, selectable),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ScreenInstruction::SetFixedHeight(id, fixed_height, tab_index) => {
|
|
||||||
screen.get_indexed_tab_mut(tab_index).map_or_else(
|
|
||||||
|| {
|
|
||||||
log::warn!(
|
|
||||||
"Tab index #{} not found, could not set fixed height for plugin #{:?}.",
|
|
||||||
tab_index,
|
|
||||||
id
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|tab| tab.set_pane_fixed_height(id, fixed_height),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ScreenInstruction::SetFixedWidth(id, fixed_width, tab_index) => {
|
|
||||||
screen.get_indexed_tab_mut(tab_index).map_or_else(
|
|
||||||
|| {
|
|
||||||
log::warn!(
|
|
||||||
"Tab index #{} not found, could not set fixed width for plugin #{:?}.",
|
|
||||||
tab_index,
|
|
||||||
id
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|tab| tab.set_pane_fixed_width(id, fixed_width),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ScreenInstruction::SetInvisibleBorders(id, invisible_borders, tab_index) => {
|
|
||||||
screen.get_indexed_tab_mut(tab_index).map_or_else(
|
|
||||||
|| {
|
|
||||||
log::warn!(
|
|
||||||
r#"Tab index #{} not found, could not set invisible borders for plugin #{:?}."#,
|
|
||||||
tab_index,
|
|
||||||
id
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|tab| tab.set_pane_invisible_borders(id, invisible_borders),
|
|
||||||
);
|
|
||||||
screen.render();
|
|
||||||
}
|
|
||||||
ScreenInstruction::ClosePane(id) => {
|
ScreenInstruction::ClosePane(id) => {
|
||||||
screen.get_active_tab_mut().unwrap().close_pane(id);
|
screen.get_active_tab_mut().unwrap().close_pane(id);
|
||||||
screen.render();
|
screen.render();
|
||||||
@ -711,14 +639,6 @@ pub(crate) fn screen_thread_main(
|
|||||||
}
|
}
|
||||||
screen.render();
|
screen.render();
|
||||||
}
|
}
|
||||||
ScreenInstruction::NewTab(pane_id) => {
|
|
||||||
screen.new_tab(pane_id);
|
|
||||||
screen
|
|
||||||
.bus
|
|
||||||
.senders
|
|
||||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ScreenInstruction::SwitchTabNext => {
|
ScreenInstruction::SwitchTabNext => {
|
||||||
screen.switch_tab_next();
|
screen.switch_tab_next();
|
||||||
screen
|
screen
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,4 @@
|
|||||||
use zellij_utils::pane_size::PositionAndSize;
|
use zellij_utils::{pane_size::Viewport, zellij_tile};
|
||||||
use zellij_utils::zellij_tile;
|
|
||||||
|
|
||||||
use crate::tab::Pane;
|
use crate::tab::Pane;
|
||||||
use ansi_term::Colour::{Fixed, RGB};
|
use ansi_term::Colour::{Fixed, RGB};
|
||||||
@ -39,10 +38,6 @@ impl BoundarySymbol {
|
|||||||
color: Some(PaletteColor::EightBit(colors::GRAY)),
|
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 {
|
pub fn color(&mut self, color: Option<PaletteColor>) -> Self {
|
||||||
self.color = color;
|
self.color = color;
|
||||||
*self
|
*self
|
||||||
@ -407,14 +402,14 @@ impl Coordinates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Boundaries {
|
pub struct Boundaries {
|
||||||
position_and_size: PositionAndSize,
|
viewport: Viewport,
|
||||||
boundary_characters: HashMap<Coordinates, BoundarySymbol>,
|
boundary_characters: HashMap<Coordinates, BoundarySymbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Boundaries {
|
impl Boundaries {
|
||||||
pub fn new(position_and_size: &PositionAndSize) -> Self {
|
pub fn new(viewport: Viewport) -> Self {
|
||||||
Boundaries {
|
Boundaries {
|
||||||
position_and_size: *position_and_size,
|
viewport,
|
||||||
boundary_characters: HashMap::new(),
|
boundary_characters: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,26 +424,22 @@ impl Boundaries {
|
|||||||
},
|
},
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
if rect.x() > self.position_and_size.x {
|
if rect.x() > self.viewport.x {
|
||||||
// left boundary
|
// left boundary
|
||||||
let boundary_x_coords = rect.x() - 1;
|
let boundary_x_coords = rect.x() - 1;
|
||||||
let first_row_coordinates = self.rect_right_boundary_row_start(rect);
|
let first_row_coordinates = self.rect_right_boundary_row_start(rect);
|
||||||
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
||||||
for row in first_row_coordinates..last_row_coordinates {
|
for row in first_row_coordinates..last_row_coordinates {
|
||||||
let coordinates = Coordinates::new(boundary_x_coords, row);
|
let coordinates = Coordinates::new(boundary_x_coords, row);
|
||||||
let mut symbol_to_add =
|
let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y {
|
||||||
if row == first_row_coordinates && row != self.position_and_size.y {
|
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
||||||
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
} else if row == last_row_coordinates - 1
|
||||||
} else if row == last_row_coordinates - 1
|
&& row != self.viewport.y + self.viewport.rows - 1
|
||||||
&& row != self.position_and_size.y + self.position_and_size.rows - 1
|
{
|
||||||
{
|
BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
|
||||||
BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
|
} else {
|
||||||
} else {
|
BoundarySymbol::new(boundary_type::VERTICAL).color(color)
|
||||||
BoundarySymbol::new(boundary_type::VERTICAL).color(color)
|
};
|
||||||
};
|
|
||||||
if rect.invisible_borders() {
|
|
||||||
symbol_to_add = symbol_to_add.invisible();
|
|
||||||
}
|
|
||||||
let next_symbol = self
|
let next_symbol = self
|
||||||
.boundary_characters
|
.boundary_characters
|
||||||
.remove(&coordinates)
|
.remove(&coordinates)
|
||||||
@ -457,26 +448,20 @@ impl Boundaries {
|
|||||||
self.boundary_characters.insert(coordinates, next_symbol);
|
self.boundary_characters.insert(coordinates, next_symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rect.y() > self.position_and_size.y {
|
if rect.y() > self.viewport.y {
|
||||||
// top boundary
|
// top boundary
|
||||||
let boundary_y_coords = rect.y() - 1;
|
let boundary_y_coords = rect.y() - 1;
|
||||||
let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
|
let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
|
||||||
let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
|
let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
|
||||||
for col in first_col_coordinates..last_col_coordinates {
|
for col in first_col_coordinates..last_col_coordinates {
|
||||||
let coordinates = Coordinates::new(col, boundary_y_coords);
|
let coordinates = Coordinates::new(col, boundary_y_coords);
|
||||||
let mut symbol_to_add = if col == first_col_coordinates
|
let symbol_to_add = if col == first_col_coordinates && col != self.viewport.x {
|
||||||
&& col != self.position_and_size.x
|
|
||||||
{
|
|
||||||
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
|
||||||
} else if col == last_col_coordinates - 1 && col != self.position_and_size.cols - 1
|
} else if col == last_col_coordinates - 1 && col != self.viewport.cols - 1 {
|
||||||
{
|
|
||||||
BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
|
BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
|
||||||
} else {
|
} else {
|
||||||
BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
|
BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
|
||||||
};
|
};
|
||||||
if rect.invisible_borders() {
|
|
||||||
symbol_to_add = symbol_to_add.invisible();
|
|
||||||
}
|
|
||||||
let next_symbol = self
|
let next_symbol = self
|
||||||
.boundary_characters
|
.boundary_characters
|
||||||
.remove(&coordinates)
|
.remove(&coordinates)
|
||||||
@ -492,19 +477,15 @@ impl Boundaries {
|
|||||||
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
let last_row_coordinates = self.rect_right_boundary_row_end(rect);
|
||||||
for row in first_row_coordinates..last_row_coordinates {
|
for row in first_row_coordinates..last_row_coordinates {
|
||||||
let coordinates = Coordinates::new(boundary_x_coords, row);
|
let coordinates = Coordinates::new(boundary_x_coords, row);
|
||||||
let mut symbol_to_add =
|
let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y {
|
||||||
if row == first_row_coordinates && row != self.position_and_size.y {
|
BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
|
||||||
BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
|
} else if row == last_row_coordinates - 1
|
||||||
} else if row == last_row_coordinates - 1
|
&& row != self.viewport.y + self.viewport.rows - 1
|
||||||
&& row != self.position_and_size.y + self.position_and_size.rows - 1
|
{
|
||||||
{
|
BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
|
||||||
BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
|
} else {
|
||||||
} else {
|
BoundarySymbol::new(boundary_type::VERTICAL).color(color)
|
||||||
BoundarySymbol::new(boundary_type::VERTICAL).color(color)
|
};
|
||||||
};
|
|
||||||
if rect.invisible_borders() {
|
|
||||||
symbol_to_add = symbol_to_add.invisible();
|
|
||||||
}
|
|
||||||
let next_symbol = self
|
let next_symbol = self
|
||||||
.boundary_characters
|
.boundary_characters
|
||||||
.remove(&coordinates)
|
.remove(&coordinates)
|
||||||
@ -520,19 +501,13 @@ impl Boundaries {
|
|||||||
let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
|
let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
|
||||||
for col in first_col_coordinates..last_col_coordinates {
|
for col in first_col_coordinates..last_col_coordinates {
|
||||||
let coordinates = Coordinates::new(col, boundary_y_coords);
|
let coordinates = Coordinates::new(col, boundary_y_coords);
|
||||||
let mut symbol_to_add = if col == first_col_coordinates
|
let symbol_to_add = if col == first_col_coordinates && col != self.viewport.x {
|
||||||
&& col != self.position_and_size.x
|
|
||||||
{
|
|
||||||
BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
|
BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
|
||||||
} else if col == last_col_coordinates - 1 && col != self.position_and_size.cols - 1
|
} else if col == last_col_coordinates - 1 && col != self.viewport.cols - 1 {
|
||||||
{
|
|
||||||
BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
|
BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
|
||||||
} else {
|
} else {
|
||||||
BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
|
BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
|
||||||
};
|
};
|
||||||
if rect.invisible_borders() {
|
|
||||||
symbol_to_add = symbol_to_add.invisible();
|
|
||||||
}
|
|
||||||
let next_symbol = self
|
let next_symbol = self
|
||||||
.boundary_characters
|
.boundary_characters
|
||||||
.remove(&coordinates)
|
.remove(&coordinates)
|
||||||
@ -555,16 +530,16 @@ impl Boundaries {
|
|||||||
vte_output
|
vte_output
|
||||||
}
|
}
|
||||||
fn rect_right_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
|
fn rect_right_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
|
||||||
rect.x() + rect.columns() < self.position_and_size.cols
|
rect.x() + rect.cols() < self.viewport.cols
|
||||||
}
|
}
|
||||||
fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
|
fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
|
||||||
rect.y() + rect.rows() < self.position_and_size.y + self.position_and_size.rows
|
rect.y() + rect.rows() < self.viewport.y + self.viewport.rows
|
||||||
}
|
}
|
||||||
fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize {
|
fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize {
|
||||||
if rect.y() > self.position_and_size.y {
|
if rect.y() > self.viewport.y {
|
||||||
rect.y() - 1
|
rect.y() - 1
|
||||||
} else {
|
} else {
|
||||||
self.position_and_size.y
|
self.viewport.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn rect_right_boundary_row_end(&self, rect: &dyn Pane) -> usize {
|
fn rect_right_boundary_row_end(&self, rect: &dyn Pane) -> usize {
|
||||||
@ -578,12 +553,12 @@ impl Boundaries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn rect_bottom_boundary_col_end(&self, rect: &dyn Pane) -> usize {
|
fn rect_bottom_boundary_col_end(&self, rect: &dyn Pane) -> usize {
|
||||||
rect.x() + rect.columns()
|
rect.x() + rect.cols()
|
||||||
}
|
}
|
||||||
fn is_fully_inside_screen(&self, rect: &dyn Pane) -> bool {
|
fn is_fully_inside_screen(&self, rect: &dyn Pane) -> bool {
|
||||||
rect.x() >= self.position_and_size.x
|
rect.x() >= self.viewport.x
|
||||||
&& rect.x() + rect.columns() <= self.position_and_size.x + self.position_and_size.cols
|
&& rect.x() + rect.cols() <= self.viewport.x + self.viewport.cols
|
||||||
&& rect.y() >= self.position_and_size.y
|
&& rect.y() >= self.viewport.y
|
||||||
&& rect.y() + rect.rows() <= self.position_and_size.y + self.position_and_size.rows
|
&& rect.y() + rect.rows() <= self.viewport.y + self.viewport.rows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
pub mod boundaries;
|
pub mod boundaries;
|
||||||
pub mod pane_boundaries_frame;
|
pub mod pane_boundaries_frame;
|
||||||
pub mod pane_resizer;
|
pub mod pane_resizer;
|
||||||
pub mod pane_resizer_beta;
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::ui::boundaries::boundary_type;
|
use crate::ui::boundaries::boundary_type;
|
||||||
use ansi_term::Colour::{Fixed, RGB};
|
use ansi_term::Colour::{Fixed, RGB};
|
||||||
use ansi_term::Style;
|
use ansi_term::Style;
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
use zellij_utils::pane_size::Viewport;
|
||||||
use zellij_utils::zellij_tile::prelude::PaletteColor;
|
use zellij_utils::zellij_tile::prelude::PaletteColor;
|
||||||
|
|
||||||
fn color_string(character: &str, color: Option<PaletteColor>) -> String {
|
fn color_string(character: &str, color: Option<PaletteColor>) -> String {
|
||||||
@ -18,93 +18,15 @@ fn color_string(character: &str, color: Option<PaletteColor>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PaneBoundariesFrame {
|
#[derive(Default, PartialEq)]
|
||||||
pub position_and_size: PositionAndSize,
|
pub struct PaneFrame {
|
||||||
base_title: String,
|
pub geom: Viewport,
|
||||||
title: String,
|
pub title: String,
|
||||||
scroll_position: (usize, usize), // (position, length)
|
pub scroll_position: (usize, usize), // (position, length)
|
||||||
pub color: Option<PaletteColor>,
|
pub color: Option<PaletteColor>,
|
||||||
draw_title_only: bool,
|
|
||||||
should_render: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaneBoundariesFrame {
|
impl PaneFrame {
|
||||||
pub fn new(position_and_size: PositionAndSize, title: String) -> Self {
|
|
||||||
PaneBoundariesFrame {
|
|
||||||
position_and_size,
|
|
||||||
color: None,
|
|
||||||
base_title: title.clone(),
|
|
||||||
title,
|
|
||||||
scroll_position: (0, 0),
|
|
||||||
draw_title_only: false,
|
|
||||||
should_render: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn frame_title_only(mut self) -> Self {
|
|
||||||
// TODO: remove this?
|
|
||||||
self.draw_title_only = true;
|
|
||||||
self.should_render = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn render_only_title(&mut self, should_render_only_title: bool) {
|
|
||||||
if should_render_only_title != self.draw_title_only {
|
|
||||||
self.should_render = true;
|
|
||||||
self.draw_title_only = should_render_only_title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn change_pos_and_size(&mut self, position_and_size: PositionAndSize) {
|
|
||||||
if position_and_size != self.position_and_size {
|
|
||||||
self.position_and_size = position_and_size;
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn set_color(&mut self, color: Option<PaletteColor>) {
|
|
||||||
if color != self.color {
|
|
||||||
self.color = color;
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn update_scroll(&mut self, scroll_position: (usize, usize)) {
|
|
||||||
if scroll_position != self.scroll_position {
|
|
||||||
self.scroll_position = scroll_position;
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn update_title(&mut self, title: Option<&String>) {
|
|
||||||
match title {
|
|
||||||
Some(title) => {
|
|
||||||
if title != &self.title {
|
|
||||||
self.title = title.clone();
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.title = self.base_title.clone();
|
|
||||||
self.should_render = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn content_position_and_size(&self) -> PositionAndSize {
|
|
||||||
if self.draw_title_only {
|
|
||||||
self.position_and_size.reduce_top_line()
|
|
||||||
} else {
|
|
||||||
self.position_and_size.reduce_outer_frame(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn content_offset(&self) -> (usize, usize) {
|
|
||||||
// (column_difference, row_difference)
|
|
||||||
let content_position_and_size = self.content_position_and_size();
|
|
||||||
let column_difference = content_position_and_size
|
|
||||||
.x
|
|
||||||
.saturating_sub(self.position_and_size.x);
|
|
||||||
let row_difference = content_position_and_size
|
|
||||||
.y
|
|
||||||
.saturating_sub(self.position_and_size.y);
|
|
||||||
(column_difference, row_difference)
|
|
||||||
}
|
|
||||||
pub fn set_should_render(&mut self, should_render: bool) {
|
|
||||||
self.should_render = should_render;
|
|
||||||
}
|
|
||||||
fn render_title_right_side(&self, max_length: usize) -> Option<String> {
|
fn render_title_right_side(&self, max_length: usize) -> Option<String> {
|
||||||
if self.scroll_position.0 > 0 || self.scroll_position.1 > 0 {
|
if self.scroll_position.0 > 0 || self.scroll_position.1 > 0 {
|
||||||
let prefix = " SCROLL: ";
|
let prefix = " SCROLL: ";
|
||||||
@ -156,17 +78,9 @@ impl PaneBoundariesFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn render_title(&self, vte_output: &mut String) {
|
fn render_title(&self, vte_output: &mut String) {
|
||||||
let total_title_length = self.position_and_size.cols - 2; // 2 for the left and right corners
|
let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
|
||||||
let left_boundary = if self.draw_title_only {
|
let left_boundary = boundary_type::TOP_LEFT;
|
||||||
boundary_type::HORIZONTAL
|
let right_boundary = boundary_type::TOP_RIGHT;
|
||||||
} else {
|
|
||||||
boundary_type::TOP_LEFT
|
|
||||||
};
|
|
||||||
let right_boundary = if self.draw_title_only {
|
|
||||||
boundary_type::HORIZONTAL
|
|
||||||
} else {
|
|
||||||
boundary_type::TOP_RIGHT
|
|
||||||
};
|
|
||||||
let left_side = self.render_title_left_side(total_title_length);
|
let left_side = self.render_title_left_side(total_title_length);
|
||||||
let right_side = left_side.as_ref().and_then(|left_side| {
|
let right_side = left_side.as_ref().and_then(|left_side| {
|
||||||
let space_left = total_title_length.saturating_sub(left_side.chars().count() + 1); // 1 for a middle separator
|
let space_left = total_title_length.saturating_sub(left_side.chars().count() + 1); // 1 for a middle separator
|
||||||
@ -205,73 +119,60 @@ impl PaneBoundariesFrame {
|
|||||||
};
|
};
|
||||||
vte_output.push_str(&format!(
|
vte_output.push_str(&format!(
|
||||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||||
self.position_and_size.y + 1, // +1 because goto is 1 indexed
|
self.geom.y + 1, // +1 because goto is 1 indexed
|
||||||
self.position_and_size.x + 1, // +1 because goto is 1 indexed
|
self.geom.x + 1, // +1 because goto is 1 indexed
|
||||||
color_string(&title_text, self.color),
|
color_string(&title_text, self.color),
|
||||||
)); // goto row/col + boundary character
|
)); // goto row/col + boundary character
|
||||||
}
|
}
|
||||||
pub fn render(&mut self) -> Option<String> {
|
pub fn render(&self) -> String {
|
||||||
if !self.should_render {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut vte_output = String::new();
|
let mut vte_output = String::new();
|
||||||
if self.draw_title_only {
|
for row in self.geom.y..(self.geom.y + self.geom.rows) {
|
||||||
self.render_title(&mut vte_output);
|
if row == self.geom.y {
|
||||||
} else {
|
// top row
|
||||||
for row in
|
self.render_title(&mut vte_output);
|
||||||
self.position_and_size.y..(self.position_and_size.y + self.position_and_size.rows)
|
} else if row == self.geom.y + self.geom.rows - 1 {
|
||||||
{
|
// bottom row
|
||||||
if row == self.position_and_size.y {
|
for col in self.geom.x..(self.geom.x + self.geom.cols) {
|
||||||
// top row
|
if col == self.geom.x {
|
||||||
self.render_title(&mut vte_output);
|
// bottom left corner
|
||||||
} else if row == self.position_and_size.y + self.position_and_size.rows - 1 {
|
vte_output.push_str(&format!(
|
||||||
// bottom row
|
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||||
for col in self.position_and_size.x
|
row + 1, // +1 because goto is 1 indexed
|
||||||
..(self.position_and_size.x + self.position_and_size.cols)
|
col + 1,
|
||||||
{
|
color_string(boundary_type::BOTTOM_LEFT, self.color),
|
||||||
if col == self.position_and_size.x {
|
)); // goto row/col + boundary character
|
||||||
// bottom left corner
|
} else if col == self.geom.x + self.geom.cols - 1 {
|
||||||
vte_output.push_str(&format!(
|
// bottom right corner
|
||||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
vte_output.push_str(&format!(
|
||||||
row + 1, // +1 because goto is 1 indexed
|
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||||
col + 1,
|
row + 1, // +1 because goto is 1 indexed
|
||||||
color_string(boundary_type::BOTTOM_LEFT, self.color),
|
col + 1,
|
||||||
)); // goto row/col + boundary character
|
color_string(boundary_type::BOTTOM_RIGHT, self.color),
|
||||||
} else if col == self.position_and_size.x + self.position_and_size.cols - 1
|
)); // goto row/col + boundary character
|
||||||
{
|
} else {
|
||||||
// bottom right corner
|
vte_output.push_str(&format!(
|
||||||
vte_output.push_str(&format!(
|
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
row + 1, // +1 because goto is 1 indexed
|
||||||
row + 1, // +1 because goto is 1 indexed
|
col + 1,
|
||||||
col + 1,
|
color_string(boundary_type::HORIZONTAL, self.color),
|
||||||
color_string(boundary_type::BOTTOM_RIGHT, self.color),
|
)); // goto row/col + boundary character
|
||||||
)); // goto row/col + boundary character
|
|
||||||
} else {
|
|
||||||
vte_output.push_str(&format!(
|
|
||||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
|
||||||
row + 1, // +1 because goto is 1 indexed
|
|
||||||
col + 1,
|
|
||||||
color_string(boundary_type::HORIZONTAL, self.color),
|
|
||||||
)); // goto row/col + boundary character
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
vte_output.push_str(&format!(
|
|
||||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
|
||||||
row + 1, // +1 because goto is 1 indexed
|
|
||||||
self.position_and_size.x + 1,
|
|
||||||
color_string(boundary_type::VERTICAL, self.color),
|
|
||||||
)); // goto row/col + boundary character
|
|
||||||
vte_output.push_str(&format!(
|
|
||||||
"\u{1b}[{};{}H\u{1b}[m{}",
|
|
||||||
row + 1, // +1 because goto is 1 indexed
|
|
||||||
self.position_and_size.x + self.position_and_size.cols,
|
|
||||||
color_string(boundary_type::VERTICAL, self.color),
|
|
||||||
)); // goto row/col + boundary character
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
vte_output.push_str(&format!(
|
||||||
|
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||||
|
row + 1, // +1 because goto is 1 indexed
|
||||||
|
self.geom.x + 1,
|
||||||
|
color_string(boundary_type::VERTICAL, self.color),
|
||||||
|
)); // goto row/col + boundary character
|
||||||
|
vte_output.push_str(&format!(
|
||||||
|
"\u{1b}[{};{}H\u{1b}[m{}",
|
||||||
|
row + 1, // +1 because goto is 1 indexed
|
||||||
|
self.geom.x + self.geom.cols,
|
||||||
|
color_string(boundary_type::VERTICAL, self.color),
|
||||||
|
)); // goto row/col + boundary character
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.should_render = false;
|
vte_output
|
||||||
Some(vte_output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,537 +1,253 @@
|
|||||||
use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane};
|
use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane};
|
||||||
use std::{
|
use cassowary::{
|
||||||
cmp::Ordering,
|
strength::{REQUIRED, STRONG},
|
||||||
collections::{BTreeMap, HashSet},
|
Expression, Solver, Variable,
|
||||||
|
WeightedRelation::EQ,
|
||||||
|
};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use zellij_utils::{
|
||||||
|
input::layout::Direction,
|
||||||
|
pane_size::{Constraint, Dimension, PaneGeom},
|
||||||
};
|
};
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
|
||||||
|
|
||||||
pub(crate) struct PaneResizer<'a> {
|
pub struct PaneResizer<'a> {
|
||||||
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
|
panes: HashMap<&'a PaneId, &'a mut Box<dyn Pane>>,
|
||||||
os_api: &'a mut Box<dyn ServerOsApi>,
|
os_api: &'a mut Box<dyn ServerOsApi>,
|
||||||
|
vars: HashMap<PaneId, Variable>,
|
||||||
|
solver: Solver,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: currently there are some functions here duplicated with Tab
|
// FIXME: Just hold a mutable Pane reference instead of the PaneId, fixed, pos, and size?
|
||||||
// all resizing functions should move here
|
// Do this after panes are no longer trait-objects!
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Span {
|
||||||
|
pid: PaneId,
|
||||||
|
direction: Direction,
|
||||||
|
pos: usize,
|
||||||
|
size: Dimension,
|
||||||
|
size_var: Variable,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Grid = Vec<Vec<Span>>;
|
||||||
|
|
||||||
impl<'a> PaneResizer<'a> {
|
impl<'a> PaneResizer<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
|
panes: impl Iterator<Item = (&'a PaneId, &'a mut Box<dyn Pane>)>,
|
||||||
os_api: &'a mut Box<dyn ServerOsApi>,
|
os_api: &'a mut Box<dyn ServerOsApi>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
PaneResizer { panes, os_api }
|
let panes: HashMap<_, _> = panes.collect();
|
||||||
|
let mut vars = HashMap::new();
|
||||||
|
for &&k in panes.keys() {
|
||||||
|
vars.insert(k, Variable::new());
|
||||||
|
}
|
||||||
|
PaneResizer {
|
||||||
|
panes,
|
||||||
|
os_api,
|
||||||
|
vars,
|
||||||
|
solver: Solver::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn resize(
|
|
||||||
&mut self,
|
pub fn layout(&mut self, direction: Direction, space: usize) -> Result<(), String> {
|
||||||
mut current_size: PositionAndSize,
|
self.solver.reset();
|
||||||
new_size: PositionAndSize,
|
let grid = self.solve(direction, space)?;
|
||||||
) -> Option<(isize, isize)> {
|
let spans = self.discretize_spans(grid, space)?;
|
||||||
// (column_difference, row_difference)
|
self.apply_spans(spans);
|
||||||
let mut successfully_resized = false;
|
Ok(())
|
||||||
let mut column_difference: isize = 0;
|
}
|
||||||
let mut row_difference: isize = 0;
|
|
||||||
match new_size.cols.cmp(¤t_size.cols) {
|
fn solve(&mut self, direction: Direction, space: usize) -> Result<Grid, String> {
|
||||||
Ordering::Greater => {
|
let grid: Grid = self
|
||||||
let increase_by = new_size.cols - current_size.cols;
|
.grid_boundaries(direction)
|
||||||
if let Some(panes_to_resize) = find_increasable_vertical_chain(
|
.into_iter()
|
||||||
self.panes,
|
.map(|b| self.spans_in_boundary(direction, b))
|
||||||
increase_by,
|
.collect();
|
||||||
current_size.cols,
|
|
||||||
current_size.rows,
|
let constraints: HashSet<_> = grid
|
||||||
) {
|
.iter()
|
||||||
self.increase_panes_right_and_push_adjacents_right(
|
.flat_map(|s| constrain_spans(space, s))
|
||||||
panes_to_resize,
|
.collect();
|
||||||
increase_by,
|
|
||||||
);
|
self.solver
|
||||||
column_difference = new_size.cols as isize - current_size.cols as isize;
|
.add_constraints(&constraints)
|
||||||
current_size.cols = (current_size.cols as isize + column_difference) as usize;
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
successfully_resized = true;
|
|
||||||
};
|
Ok(grid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discretize_spans(&mut self, mut grid: Grid, space: usize) -> Result<Vec<Span>, String> {
|
||||||
|
let mut rounded_sizes: HashMap<_, _> = grid
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|s| {
|
||||||
|
(
|
||||||
|
s.size_var,
|
||||||
|
stable_round(self.solver.get_value(s.size_var)) as isize,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Round f64 pane sizes to usize without gaps or overlap
|
||||||
|
let mut finalised = Vec::new();
|
||||||
|
for spans in grid.iter_mut() {
|
||||||
|
let rounded_size: isize = spans.iter().map(|s| rounded_sizes[&s.size_var]).sum();
|
||||||
|
let mut error = space as isize - rounded_size;
|
||||||
|
let mut flex_spans: Vec<_> = spans
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|s| !s.size.is_fixed() && !finalised.contains(&s.pid))
|
||||||
|
.collect();
|
||||||
|
flex_spans.sort_by_key(|s| rounded_sizes[&s.size_var]);
|
||||||
|
if error < 0 {
|
||||||
|
flex_spans.reverse();
|
||||||
}
|
}
|
||||||
Ordering::Less => {
|
for span in flex_spans {
|
||||||
let reduce_by = current_size.cols - new_size.cols;
|
rounded_sizes
|
||||||
if let Some(panes_to_resize) = find_reducible_vertical_chain(
|
.entry(span.size_var)
|
||||||
self.panes,
|
.and_modify(|s| *s += error.signum());
|
||||||
reduce_by,
|
error -= error.signum();
|
||||||
current_size.cols,
|
|
||||||
current_size.rows,
|
|
||||||
) {
|
|
||||||
self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by);
|
|
||||||
column_difference = new_size.cols as isize - current_size.cols as isize;
|
|
||||||
current_size.cols = (current_size.cols as isize + column_difference) as usize;
|
|
||||||
successfully_resized = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Ordering::Equal => (),
|
finalised.extend(spans.iter().map(|s| s.pid));
|
||||||
}
|
}
|
||||||
match new_size.rows.cmp(¤t_size.rows) {
|
|
||||||
Ordering::Greater => {
|
// Update span positions based on their rounded sizes
|
||||||
let increase_by = new_size.rows - current_size.rows;
|
for spans in grid.iter_mut() {
|
||||||
if let Some(panes_to_resize) = find_increasable_horizontal_chain(
|
let mut offset = 0;
|
||||||
self.panes,
|
for span in spans.iter_mut() {
|
||||||
increase_by,
|
span.pos = offset;
|
||||||
current_size.cols,
|
let sz = rounded_sizes[&span.size_var];
|
||||||
current_size.rows,
|
if sz < 1 {
|
||||||
) {
|
return Err("Ran out of room for spans".into());
|
||||||
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;
|
span.size.set_inner(sz as usize);
|
||||||
current_size.rows = (current_size.rows as isize + row_difference) as usize;
|
offset += span.size.as_usize();
|
||||||
successfully_resized = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Ordering::Less => {
|
|
||||||
let reduce_by = current_size.rows - new_size.rows;
|
|
||||||
if let Some(panes_to_resize) = find_reducible_horizontal_chain(
|
|
||||||
self.panes,
|
|
||||||
reduce_by,
|
|
||||||
current_size.cols,
|
|
||||||
current_size.rows,
|
|
||||||
) {
|
|
||||||
self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by);
|
|
||||||
row_difference = new_size.rows as isize - current_size.rows as isize;
|
|
||||||
current_size.rows = (current_size.rows as isize + row_difference) as usize;
|
|
||||||
successfully_resized = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ordering::Equal => (),
|
|
||||||
}
|
}
|
||||||
if successfully_resized {
|
|
||||||
Some((column_difference, row_difference))
|
Ok(grid.into_iter().flatten().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_spans(&mut self, spans: Vec<Span>) {
|
||||||
|
for span in spans {
|
||||||
|
let pane = self.panes.get_mut(&span.pid).unwrap();
|
||||||
|
let new_geom = match span.direction {
|
||||||
|
Direction::Horizontal => PaneGeom {
|
||||||
|
x: span.pos,
|
||||||
|
cols: span.size,
|
||||||
|
..pane.current_geom()
|
||||||
|
},
|
||||||
|
Direction::Vertical => PaneGeom {
|
||||||
|
y: span.pos,
|
||||||
|
rows: span.size,
|
||||||
|
..pane.current_geom()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if pane.geom_override().is_some() {
|
||||||
|
pane.get_geom_override(new_geom);
|
||||||
|
} else {
|
||||||
|
pane.set_geom(new_geom);
|
||||||
|
}
|
||||||
|
if let PaneId::Terminal(pid) = pane.pid() {
|
||||||
|
self.os_api.set_terminal_size_using_fd(
|
||||||
|
pid,
|
||||||
|
pane.get_content_columns() as u16,
|
||||||
|
pane.get_content_rows() as u16,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Functions like this should have unit tests!
|
||||||
|
fn grid_boundaries(&self, direction: Direction) -> Vec<(usize, usize)> {
|
||||||
|
// Select the spans running *perpendicular* to the direction of resize
|
||||||
|
let spans: Vec<Span> = self
|
||||||
|
.panes
|
||||||
|
.values()
|
||||||
|
.map(|p| self.get_span(!direction, p.as_ref()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut last_edge = 0;
|
||||||
|
let mut bounds = Vec::new();
|
||||||
|
let mut edges: Vec<usize> = spans.iter().map(|s| s.pos + s.size.as_usize()).collect();
|
||||||
|
edges.sort_unstable();
|
||||||
|
edges.dedup();
|
||||||
|
for next in edges {
|
||||||
|
let next_edge = next;
|
||||||
|
bounds.push((last_edge, next_edge));
|
||||||
|
last_edge = next_edge;
|
||||||
|
}
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec<Span> {
|
||||||
|
let bwn = |v, (s, e)| s <= v && v < e;
|
||||||
|
let mut spans: Vec<_> = self
|
||||||
|
.panes
|
||||||
|
.values()
|
||||||
|
.filter(|p| {
|
||||||
|
let s = self.get_span(!direction, p.as_ref());
|
||||||
|
let span_bounds = (s.pos, s.pos + s.size.as_usize());
|
||||||
|
bwn(span_bounds.0, boundary)
|
||||||
|
|| (bwn(boundary.0, span_bounds)
|
||||||
|
&& (bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1))
|
||||||
|
})
|
||||||
|
.map(|p| self.get_span(direction, p.as_ref()))
|
||||||
|
.collect();
|
||||||
|
spans.sort_unstable_by_key(|s| s.pos);
|
||||||
|
spans
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span {
|
||||||
|
let pas = pane.current_geom();
|
||||||
|
let size_var = self.vars[&pane.pid()];
|
||||||
|
match direction {
|
||||||
|
Direction::Horizontal => Span {
|
||||||
|
pid: pane.pid(),
|
||||||
|
direction,
|
||||||
|
pos: pas.x,
|
||||||
|
size: pas.cols,
|
||||||
|
size_var,
|
||||||
|
},
|
||||||
|
Direction::Vertical => Span {
|
||||||
|
pid: pane.pid(),
|
||||||
|
direction,
|
||||||
|
pos: pas.y,
|
||||||
|
size: pas.rows,
|
||||||
|
size_var,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constrain_spans(space: usize, spans: &[Span]) -> HashSet<cassowary::Constraint> {
|
||||||
|
let mut constraints = HashSet::new();
|
||||||
|
|
||||||
|
// Calculating "flexible" space (space not consumed by fixed-size spans)
|
||||||
|
let new_flex_space = spans.iter().fold(space, |a, s| {
|
||||||
|
if let Constraint::Fixed(sz) = s.size.constraint {
|
||||||
|
a.saturating_sub(sz)
|
||||||
} else {
|
} else {
|
||||||
None
|
a
|
||||||
}
|
|
||||||
}
|
|
||||||
fn reduce_panes_left_and_pull_adjacents_left(
|
|
||||||
&mut self,
|
|
||||||
panes_to_reduce: Vec<PaneId>,
|
|
||||||
reduce_by: usize,
|
|
||||||
) {
|
|
||||||
let mut pulled_panes: HashSet<PaneId> = HashSet::new();
|
|
||||||
for pane_id in panes_to_reduce {
|
|
||||||
let (pane_x, pane_y, pane_columns, pane_rows) = {
|
|
||||||
let pane = self.panes.get(&pane_id).unwrap();
|
|
||||||
(pane.x(), pane.y(), pane.columns(), pane.rows())
|
|
||||||
};
|
|
||||||
let panes_to_pull = self.panes.values_mut().filter(|p| {
|
|
||||||
p.x() >= pane_x + pane_columns
|
|
||||||
&& (p.y() <= pane_y && p.y() + p.rows() >= pane_y
|
|
||||||
|| p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows)
|
|
||||||
});
|
|
||||||
for pane in panes_to_pull {
|
|
||||||
if !pulled_panes.contains(&pane.pid()) {
|
|
||||||
pane.pull_left(reduce_by);
|
|
||||||
pulled_panes.insert(pane.pid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.reduce_pane_width_left(&pane_id, reduce_by);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn reduce_panes_up_and_pull_adjacents_up(
|
|
||||||
&mut self,
|
|
||||||
panes_to_reduce: Vec<PaneId>,
|
|
||||||
reduce_by: usize,
|
|
||||||
) {
|
|
||||||
let mut pulled_panes: HashSet<PaneId> = HashSet::new();
|
|
||||||
for pane_id in panes_to_reduce {
|
|
||||||
let (pane_x, pane_y, pane_columns, pane_rows) = {
|
|
||||||
let pane = self.panes.get(&pane_id).unwrap();
|
|
||||||
(pane.x(), pane.y(), pane.columns(), pane.rows())
|
|
||||||
};
|
|
||||||
let panes_to_pull = self.panes.values_mut().filter(|p| {
|
|
||||||
p.y() >= pane_y + pane_rows
|
|
||||||
&& (p.x() <= pane_x && p.x() + p.columns() >= pane_x
|
|
||||||
|| p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns)
|
|
||||||
});
|
|
||||||
for pane in panes_to_pull {
|
|
||||||
if !pulled_panes.contains(&pane.pid()) {
|
|
||||||
pane.pull_up(reduce_by);
|
|
||||||
pulled_panes.insert(pane.pid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.reduce_pane_height_up(&pane_id, reduce_by);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn increase_panes_down_and_push_down_adjacents(
|
|
||||||
&mut self,
|
|
||||||
panes_to_increase: Vec<PaneId>,
|
|
||||||
increase_by: usize,
|
|
||||||
) {
|
|
||||||
let mut pushed_panes: HashSet<PaneId> = HashSet::new();
|
|
||||||
for pane_id in panes_to_increase {
|
|
||||||
let (pane_x, pane_y, pane_columns, pane_rows) = {
|
|
||||||
let pane = self.panes.get(&pane_id).unwrap();
|
|
||||||
(pane.x(), pane.y(), pane.columns(), pane.rows())
|
|
||||||
};
|
|
||||||
let panes_to_push = self.panes.values_mut().filter(|p| {
|
|
||||||
p.y() >= pane_y + pane_rows
|
|
||||||
&& (p.x() <= pane_x && p.x() + p.columns() >= pane_x
|
|
||||||
|| p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns)
|
|
||||||
});
|
|
||||||
for pane in panes_to_push {
|
|
||||||
if !pushed_panes.contains(&pane.pid()) {
|
|
||||||
pane.push_down(increase_by);
|
|
||||||
pushed_panes.insert(pane.pid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.increase_pane_height_down(&pane_id, increase_by);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn increase_panes_right_and_push_adjacents_right(
|
|
||||||
&mut self,
|
|
||||||
panes_to_increase: Vec<PaneId>,
|
|
||||||
increase_by: usize,
|
|
||||||
) {
|
|
||||||
let mut pushed_panes: HashSet<PaneId> = HashSet::new();
|
|
||||||
for pane_id in panes_to_increase {
|
|
||||||
let (pane_x, pane_y, pane_columns, pane_rows) = {
|
|
||||||
let pane = self.panes.get(&pane_id).unwrap();
|
|
||||||
(pane.x(), pane.y(), pane.columns(), pane.rows())
|
|
||||||
};
|
|
||||||
let panes_to_push = self.panes.values_mut().filter(|p| {
|
|
||||||
p.x() >= pane_x + pane_columns
|
|
||||||
&& (p.y() <= pane_y && p.y() + p.rows() >= pane_y
|
|
||||||
|| p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows)
|
|
||||||
});
|
|
||||||
for pane in panes_to_push {
|
|
||||||
if !pushed_panes.contains(&pane.pid()) {
|
|
||||||
pane.push_right(increase_by);
|
|
||||||
pushed_panes.insert(pane.pid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.increase_pane_width_right(&pane_id, increase_by);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) {
|
|
||||||
let pane = self.panes.get_mut(id).unwrap();
|
|
||||||
pane.reduce_height_up(count);
|
|
||||||
if let PaneId::Terminal(pid) = id {
|
|
||||||
self.os_api.set_terminal_size_using_fd(
|
|
||||||
*pid,
|
|
||||||
pane.get_content_columns() as u16,
|
|
||||||
pane.get_content_rows() as u16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) {
|
|
||||||
let pane = self.panes.get_mut(id).unwrap();
|
|
||||||
pane.increase_height_down(count);
|
|
||||||
if let PaneId::Terminal(pid) = pane.pid() {
|
|
||||||
self.os_api.set_terminal_size_using_fd(
|
|
||||||
pid,
|
|
||||||
pane.get_content_columns() as u16,
|
|
||||||
pane.get_content_rows() as u16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) {
|
|
||||||
let pane = self.panes.get_mut(id).unwrap();
|
|
||||||
pane.increase_width_right(count);
|
|
||||||
if let PaneId::Terminal(pid) = pane.pid() {
|
|
||||||
self.os_api.set_terminal_size_using_fd(
|
|
||||||
pid,
|
|
||||||
pane.get_content_columns() as u16,
|
|
||||||
pane.get_content_rows() as u16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) {
|
|
||||||
let pane = self.panes.get_mut(id).unwrap();
|
|
||||||
pane.reduce_width_left(count);
|
|
||||||
if let PaneId::Terminal(pid) = pane.pid() {
|
|
||||||
self.os_api.set_terminal_size_using_fd(
|
|
||||||
pid,
|
|
||||||
pane.get_content_columns() as u16,
|
|
||||||
pane.get_content_rows() as u16,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spans must use all of the available space
|
||||||
|
let full_size = spans
|
||||||
|
.iter()
|
||||||
|
.fold(Expression::from_constant(0.0), |acc, s| acc + s.size_var);
|
||||||
|
constraints.insert(full_size | EQ(REQUIRED) | space as f64);
|
||||||
|
|
||||||
|
// Try to maintain ratios and lock non-flexible sizes
|
||||||
|
for span in spans {
|
||||||
|
match span.size.constraint {
|
||||||
|
Constraint::Fixed(s) => constraints.insert(span.size_var | EQ(REQUIRED) | s as f64),
|
||||||
|
Constraint::Percent(p) => constraints
|
||||||
|
.insert((span.size_var / new_flex_space as f64) | EQ(STRONG) | (p / 100.0)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraints
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_next_increasable_horizontal_pane(
|
fn stable_round(x: f64) -> f64 {
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
((x * 100.0).round() / 100.0).round()
|
||||||
right_of: &dyn Pane,
|
|
||||||
increase_by: usize,
|
|
||||||
) -> Option<PaneId> {
|
|
||||||
let next_pane_candidates = panes.values().filter(
|
|
||||||
|p| p.x() == right_of.x() + right_of.columns() && p.horizontally_overlaps_with(right_of), // TODO: the name here is wrong, it should be vertically_overlaps_with
|
|
||||||
);
|
|
||||||
let resizable_candidates =
|
|
||||||
next_pane_candidates.filter(|p| p.can_increase_height_by(increase_by));
|
|
||||||
resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
|
|
||||||
Some(next_pane) => {
|
|
||||||
let next_pane = panes.get(&next_pane).unwrap();
|
|
||||||
if next_pane.y() < p.y() {
|
|
||||||
next_pane_id
|
|
||||||
} else {
|
|
||||||
Some(p.pid())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Some(p.pid()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_next_increasable_vertical_pane(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
below: &dyn Pane,
|
|
||||||
increase_by: usize,
|
|
||||||
) -> Option<PaneId> {
|
|
||||||
let next_pane_candidates = panes.values().filter(
|
|
||||||
|p| p.y() == below.y() + below.rows() && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with
|
|
||||||
);
|
|
||||||
let resizable_candidates =
|
|
||||||
next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by));
|
|
||||||
resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
|
|
||||||
Some(next_pane) => {
|
|
||||||
let next_pane = panes.get(&next_pane).unwrap();
|
|
||||||
if next_pane.x() < p.x() {
|
|
||||||
next_pane_id
|
|
||||||
} else {
|
|
||||||
Some(p.pid())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Some(p.pid()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_next_reducible_vertical_pane(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
below: &dyn Pane,
|
|
||||||
reduce_by: usize,
|
|
||||||
) -> Option<PaneId> {
|
|
||||||
let next_pane_candidates = panes.values().filter(
|
|
||||||
|p| p.y() == below.y() + below.rows() && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with
|
|
||||||
);
|
|
||||||
let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by));
|
|
||||||
resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
|
|
||||||
Some(next_pane) => {
|
|
||||||
let next_pane = panes.get(&next_pane).unwrap();
|
|
||||||
if next_pane.x() < p.x() {
|
|
||||||
next_pane_id
|
|
||||||
} else {
|
|
||||||
Some(p.pid())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Some(p.pid()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_next_reducible_horizontal_pane(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
right_of: &dyn Pane,
|
|
||||||
reduce_by: usize,
|
|
||||||
) -> Option<PaneId> {
|
|
||||||
let next_pane_candidates = panes.values().filter(
|
|
||||||
|p| p.x() == right_of.x() + right_of.columns() && p.horizontally_overlaps_with(right_of), // TODO: the name here is wrong, it should be vertically_overlaps_with
|
|
||||||
);
|
|
||||||
let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by));
|
|
||||||
resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
|
|
||||||
Some(next_pane) => {
|
|
||||||
let next_pane = panes.get(&next_pane).unwrap();
|
|
||||||
if next_pane.y() < p.y() {
|
|
||||||
next_pane_id
|
|
||||||
} else {
|
|
||||||
Some(p.pid())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Some(p.pid()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_increasable_horizontal_chain(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
increase_by: usize,
|
|
||||||
screen_width: usize,
|
|
||||||
screen_height: usize, // TODO: this is the previous size (make this clearer)
|
|
||||||
) -> Option<Vec<PaneId>> {
|
|
||||||
let mut horizontal_coordinate = 0;
|
|
||||||
loop {
|
|
||||||
if horizontal_coordinate == screen_height {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match panes
|
|
||||||
.values()
|
|
||||||
.find(|p| p.x() == 0 && p.y() == horizontal_coordinate)
|
|
||||||
{
|
|
||||||
Some(leftmost_pane) => {
|
|
||||||
if !leftmost_pane.can_increase_height_by(increase_by) {
|
|
||||||
horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut panes_to_resize = vec![];
|
|
||||||
let mut current_pane = leftmost_pane;
|
|
||||||
loop {
|
|
||||||
panes_to_resize.push(current_pane.pid());
|
|
||||||
if current_pane.x() + current_pane.columns() == screen_width {
|
|
||||||
return Some(panes_to_resize);
|
|
||||||
}
|
|
||||||
match find_next_increasable_horizontal_pane(
|
|
||||||
panes,
|
|
||||||
current_pane.as_ref(),
|
|
||||||
increase_by,
|
|
||||||
) {
|
|
||||||
Some(next_pane_id) => {
|
|
||||||
current_pane = panes.get(&next_pane_id).unwrap();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_increasable_vertical_chain(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
increase_by: usize,
|
|
||||||
screen_width: usize,
|
|
||||||
screen_height: usize, // TODO: this is the previous size (make this clearer)
|
|
||||||
) -> Option<Vec<PaneId>> {
|
|
||||||
let mut vertical_coordinate = 0;
|
|
||||||
loop {
|
|
||||||
if vertical_coordinate == screen_width {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match panes
|
|
||||||
.values()
|
|
||||||
.find(|p| p.y() == 0 && p.x() == vertical_coordinate)
|
|
||||||
{
|
|
||||||
Some(topmost_pane) => {
|
|
||||||
if !topmost_pane.can_increase_width_by(increase_by) {
|
|
||||||
vertical_coordinate = topmost_pane.x() + topmost_pane.columns();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut panes_to_resize = vec![];
|
|
||||||
let mut current_pane = topmost_pane;
|
|
||||||
loop {
|
|
||||||
panes_to_resize.push(current_pane.pid());
|
|
||||||
if current_pane.y() + current_pane.rows() == screen_height {
|
|
||||||
return Some(panes_to_resize);
|
|
||||||
}
|
|
||||||
match find_next_increasable_vertical_pane(
|
|
||||||
panes,
|
|
||||||
current_pane.as_ref(),
|
|
||||||
increase_by,
|
|
||||||
) {
|
|
||||||
Some(next_pane_id) => {
|
|
||||||
current_pane = panes.get(&next_pane_id).unwrap();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
vertical_coordinate = topmost_pane.x() + topmost_pane.columns();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_reducible_horizontal_chain(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
reduce_by: usize,
|
|
||||||
screen_width: usize,
|
|
||||||
screen_height: usize, // TODO: this is the previous size (make this clearer)
|
|
||||||
) -> Option<Vec<PaneId>> {
|
|
||||||
let mut horizontal_coordinate = 0;
|
|
||||||
loop {
|
|
||||||
if horizontal_coordinate == screen_height {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match panes
|
|
||||||
.values()
|
|
||||||
.find(|p| p.x() == 0 && p.y() == horizontal_coordinate)
|
|
||||||
{
|
|
||||||
Some(leftmost_pane) => {
|
|
||||||
if !leftmost_pane.can_reduce_height_by(reduce_by) {
|
|
||||||
horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut panes_to_resize = vec![];
|
|
||||||
let mut current_pane = leftmost_pane;
|
|
||||||
loop {
|
|
||||||
panes_to_resize.push(current_pane.pid());
|
|
||||||
if current_pane.x() + current_pane.columns() == screen_width {
|
|
||||||
return Some(panes_to_resize);
|
|
||||||
}
|
|
||||||
match find_next_reducible_horizontal_pane(
|
|
||||||
panes,
|
|
||||||
current_pane.as_ref(),
|
|
||||||
reduce_by,
|
|
||||||
) {
|
|
||||||
Some(next_pane_id) => {
|
|
||||||
current_pane = panes.get(&next_pane_id).unwrap();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_reducible_vertical_chain(
|
|
||||||
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
|
|
||||||
increase_by: usize,
|
|
||||||
screen_width: usize,
|
|
||||||
screen_height: usize, // TODO: this is the previous size (make this clearer)
|
|
||||||
) -> Option<Vec<PaneId>> {
|
|
||||||
let mut vertical_coordinate = 0;
|
|
||||||
loop {
|
|
||||||
if vertical_coordinate == screen_width {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match panes
|
|
||||||
.values()
|
|
||||||
.find(|p| p.y() == 0 && p.x() == vertical_coordinate)
|
|
||||||
{
|
|
||||||
Some(topmost_pane) => {
|
|
||||||
if !topmost_pane.can_reduce_width_by(increase_by) {
|
|
||||||
vertical_coordinate = topmost_pane.x() + topmost_pane.columns();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut panes_to_resize = vec![];
|
|
||||||
let mut current_pane = topmost_pane;
|
|
||||||
loop {
|
|
||||||
panes_to_resize.push(current_pane.pid());
|
|
||||||
if current_pane.y() + current_pane.rows() == screen_height {
|
|
||||||
return Some(panes_to_resize);
|
|
||||||
}
|
|
||||||
match find_next_reducible_vertical_pane(
|
|
||||||
panes,
|
|
||||||
current_pane.as_ref(),
|
|
||||||
increase_by,
|
|
||||||
) {
|
|
||||||
Some(next_pane_id) => {
|
|
||||||
current_pane = panes.get(&next_pane_id).unwrap();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
vertical_coordinate = topmost_pane.x() + topmost_pane.columns();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,248 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use crate::{os_input_output::ServerOsApi, panes::PaneId, tab::Pane};
|
|
||||||
use cassowary::{
|
|
||||||
strength::{REQUIRED, STRONG},
|
|
||||||
Constraint, Solver, Variable,
|
|
||||||
WeightedRelation::*,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::{BTreeMap, HashSet},
|
|
||||||
ops::Not,
|
|
||||||
};
|
|
||||||
use zellij_utils::pane_size::PositionAndSize;
|
|
||||||
|
|
||||||
const GAP_SIZE: usize = 1; // Panes are separated by this number of rows / columns
|
|
||||||
|
|
||||||
pub struct PaneResizer<'a> {
|
|
||||||
panes: &'a mut BTreeMap<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 {
|
|
||||||
let mut vars = BTreeMap::new();
|
|
||||||
for &k in panes.keys() {
|
|
||||||
vars.insert(k, (Variable::new(), Variable::new()));
|
|
||||||
}
|
|
||||||
PaneResizer {
|
|
||||||
panes,
|
|
||||||
vars,
|
|
||||||
solver: Solver::new(),
|
|
||||||
os_api,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(
|
|
||||||
&mut self,
|
|
||||||
current_size: PositionAndSize,
|
|
||||||
new_size: PositionAndSize,
|
|
||||||
) -> Option<(isize, isize)> {
|
|
||||||
let col_delta = new_size.cols as isize - current_size.cols as isize;
|
|
||||||
let row_delta = new_size.rows as isize - current_size.rows as isize;
|
|
||||||
if col_delta != 0 {
|
|
||||||
let spans = self.solve_direction(Direction::Horizontal, new_size.cols)?;
|
|
||||||
self.collapse_spans(&spans);
|
|
||||||
}
|
|
||||||
self.solver.reset();
|
|
||||||
if row_delta != 0 {
|
|
||||||
let spans = self.solve_direction(Direction::Vertical, new_size.rows)?;
|
|
||||||
self.collapse_spans(&spans);
|
|
||||||
}
|
|
||||||
Some((col_delta, row_delta))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn solve_direction(&mut self, direction: Direction, space: usize) -> Option<Vec<Span>> {
|
|
||||||
let mut grid = Vec::new();
|
|
||||||
for boundary in self.grid_boundaries(direction) {
|
|
||||||
grid.push(self.spans_in_boundary(direction, boundary));
|
|
||||||
}
|
|
||||||
|
|
||||||
let constraints: Vec<_> = grid
|
|
||||||
.iter()
|
|
||||||
.flat_map(|s| constrain_spans(space, s))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// FIXME: This line needs to be restored before merging!
|
|
||||||
//self.solver.add_constraints(&constraints).ok()?;
|
|
||||||
self.solver.add_constraints(&constraints).unwrap();
|
|
||||||
Some(grid.into_iter().flatten().collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grid_boundaries(&self, direction: Direction) -> Vec<(usize, usize)> {
|
|
||||||
// Select the spans running *perpendicular* to the direction of resize
|
|
||||||
let spans: Vec<Span> = self
|
|
||||||
.panes
|
|
||||||
.values()
|
|
||||||
.map(|p| self.get_span(!direction, p.as_ref()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut last_edge = 0;
|
|
||||||
let mut bounds = Vec::new();
|
|
||||||
loop {
|
|
||||||
let mut spans_on_edge: Vec<&Span> =
|
|
||||||
spans.iter().filter(|p| p.pos == last_edge).collect();
|
|
||||||
spans_on_edge.sort_unstable_by_key(|s| s.size);
|
|
||||||
if let Some(next) = spans_on_edge.first() {
|
|
||||||
let next_edge = last_edge + next.size;
|
|
||||||
bounds.push((last_edge, next_edge));
|
|
||||||
last_edge = next_edge + GAP_SIZE;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spans_in_boundary(&self, direction: Direction, boundary: (usize, usize)) -> Vec<Span> {
|
|
||||||
let (start, end) = boundary;
|
|
||||||
let bwn = |v| start <= v && v < end;
|
|
||||||
let mut spans: Vec<_> = self
|
|
||||||
.panes
|
|
||||||
.values()
|
|
||||||
.filter(|p| {
|
|
||||||
let s = self.get_span(!direction, p.as_ref());
|
|
||||||
bwn(s.pos) || bwn(s.pos + s.size)
|
|
||||||
})
|
|
||||||
.map(|p| self.get_span(direction, p.as_ref()))
|
|
||||||
.collect();
|
|
||||||
spans.sort_unstable_by_key(|s| s.pos);
|
|
||||||
spans
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_span(&self, direction: Direction, pane: &dyn Pane) -> Span {
|
|
||||||
let pas = pane.position_and_size();
|
|
||||||
let (pos_var, size_var) = self.vars[&pane.pid()];
|
|
||||||
match direction {
|
|
||||||
Direction::Horizontal => Span {
|
|
||||||
pid: pane.pid(),
|
|
||||||
direction,
|
|
||||||
fixed: pas.cols_fixed,
|
|
||||||
pos: pas.x,
|
|
||||||
size: pas.cols,
|
|
||||||
pos_var,
|
|
||||||
size_var,
|
|
||||||
},
|
|
||||||
Direction::Vertical => Span {
|
|
||||||
pid: pane.pid(),
|
|
||||||
direction,
|
|
||||||
fixed: pas.rows_fixed,
|
|
||||||
pos: pas.y,
|
|
||||||
size: pas.rows,
|
|
||||||
pos_var,
|
|
||||||
size_var,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collapse_spans(&mut self, spans: &[Span]) {
|
|
||||||
for span in spans {
|
|
||||||
let solver = &self.solver; // Hand-holding the borrow-checker
|
|
||||||
let pane = self.panes.get_mut(&span.pid).unwrap();
|
|
||||||
let fetch_usize = |v| solver.get_value(v).round() as usize;
|
|
||||||
match span.direction {
|
|
||||||
Direction::Horizontal => pane.change_pos_and_size(&PositionAndSize {
|
|
||||||
x: fetch_usize(span.pos_var),
|
|
||||||
cols: fetch_usize(span.size_var),
|
|
||||||
..pane.position_and_size()
|
|
||||||
}),
|
|
||||||
Direction::Vertical => pane.change_pos_and_size(&PositionAndSize {
|
|
||||||
y: fetch_usize(span.pos_var),
|
|
||||||
rows: fetch_usize(span.size_var),
|
|
||||||
..pane.position_and_size()
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
if let PaneId::Terminal(pid) = pane.pid() {
|
|
||||||
self.os_api.set_terminal_size_using_fd(
|
|
||||||
pid,
|
|
||||||
pane.columns() as u16,
|
|
||||||
pane.rows() as u16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constrain_spans(space: usize, spans: &[Span]) -> HashSet<Constraint> {
|
|
||||||
let mut constraints = HashSet::new();
|
|
||||||
|
|
||||||
// The first span needs to start at 0
|
|
||||||
constraints.insert(spans[0].pos_var | EQ(REQUIRED) | 0.0);
|
|
||||||
|
|
||||||
// Calculating "flexible" space (space not consumed by fixed-size spans)
|
|
||||||
let gap_space = GAP_SIZE * (spans.len() - 1);
|
|
||||||
let old_flex_space = spans
|
|
||||||
.iter()
|
|
||||||
.fold(0, |a, s| if !s.fixed { a + s.size } else { a });
|
|
||||||
let new_flex_space = spans.iter().fold(
|
|
||||||
space - gap_space,
|
|
||||||
|a, s| if s.fixed { a - s.size } else { a },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Keep spans stuck together
|
|
||||||
for pair in spans.windows(2) {
|
|
||||||
let (ls, rs) = (pair[0], pair[1]);
|
|
||||||
constraints
|
|
||||||
.insert((ls.pos_var + ls.size_var + GAP_SIZE as f64) | EQ(REQUIRED) | rs.pos_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to maintain ratios and lock non-flexible sizes
|
|
||||||
for span in spans {
|
|
||||||
if span.fixed {
|
|
||||||
constraints.insert(span.size_var | EQ(REQUIRED) | span.size as f64);
|
|
||||||
} else {
|
|
||||||
let ratio = span.size as f64 / old_flex_space as f64;
|
|
||||||
constraints.insert((span.size_var / new_flex_space as f64) | EQ(STRONG) | ratio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last pane needs to end at the end of the space
|
|
||||||
let last = spans.last().unwrap();
|
|
||||||
constraints.insert((last.pos_var + last.size_var) | EQ(REQUIRED) | space as f64);
|
|
||||||
|
|
||||||
constraints
|
|
||||||
}
|
|
@ -6,7 +6,9 @@ use crate::{
|
|||||||
SessionState,
|
SessionState,
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use zellij_utils::{input::command::TerminalAction, pane_size::PositionAndSize};
|
use zellij_utils::input::command::TerminalAction;
|
||||||
|
use zellij_utils::input::layout::LayoutTemplate;
|
||||||
|
use zellij_utils::pane_size::Size;
|
||||||
|
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
|
||||||
@ -73,12 +75,12 @@ impl ServerOsApi for FakeInputOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_new_screen(position_and_size: PositionAndSize) -> Screen {
|
fn create_new_screen(size: Size) -> Screen {
|
||||||
let mut bus: Bus<ScreenInstruction> = Bus::empty();
|
let mut bus: Bus<ScreenInstruction> = Bus::empty();
|
||||||
let fake_os_input = FakeInputOutput {};
|
let fake_os_input = FakeInputOutput {};
|
||||||
bus.os_input = Some(Box::new(fake_os_input));
|
bus.os_input = Some(Box::new(fake_os_input));
|
||||||
let mut client_attributes = ClientAttributes::default();
|
let mut client_attributes = ClientAttributes::default();
|
||||||
client_attributes.position_and_size = position_and_size;
|
client_attributes.size = size;
|
||||||
let max_panes = None;
|
let max_panes = None;
|
||||||
let mode_info = ModeInfo::default();
|
let mode_info = ModeInfo::default();
|
||||||
let session_state = Arc::new(RwLock::new(SessionState::Attached));
|
let session_state = Arc::new(RwLock::new(SessionState::Attached));
|
||||||
@ -92,19 +94,20 @@ fn create_new_screen(position_and_size: PositionAndSize) -> Screen {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_tab(screen: &mut Screen, pid: i32) {
|
||||||
|
screen.apply_layout(LayoutTemplate::default().into(), vec![pid]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn open_new_tab() {
|
fn open_new_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
|
|
||||||
assert_eq!(screen.tabs.len(), 2, "Screen now has two tabs");
|
assert_eq!(screen.tabs.len(), 2, "Screen now has two tabs");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -116,17 +119,14 @@ fn open_new_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn switch_to_prev_tab() {
|
pub fn switch_to_prev_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.switch_tab_prev();
|
screen.switch_tab_prev();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -138,17 +138,14 @@ pub fn switch_to_prev_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn switch_to_next_tab() {
|
pub fn switch_to_next_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.switch_tab_prev();
|
screen.switch_tab_prev();
|
||||||
screen.switch_tab_next();
|
screen.switch_tab_next();
|
||||||
|
|
||||||
@ -161,17 +158,14 @@ pub fn switch_to_next_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn close_tab() {
|
pub fn close_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.close_tab();
|
screen.close_tab();
|
||||||
|
|
||||||
assert_eq!(screen.tabs.len(), 1, "Only one tab left");
|
assert_eq!(screen.tabs.len(), 1, "Only one tab left");
|
||||||
@ -184,20 +178,32 @@ pub fn close_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn close_the_middle_tab() {
|
pub fn close_the_middle_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.new_tab(3);
|
new_tab(&mut screen, 3);
|
||||||
|
dbg!(screen
|
||||||
|
.tabs
|
||||||
|
.values()
|
||||||
|
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
|
||||||
|
.collect::<Vec<_>>());
|
||||||
screen.switch_tab_prev();
|
screen.switch_tab_prev();
|
||||||
|
dbg!(screen
|
||||||
|
.tabs
|
||||||
|
.values()
|
||||||
|
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
|
||||||
|
.collect::<Vec<_>>());
|
||||||
screen.close_tab();
|
screen.close_tab();
|
||||||
|
dbg!(screen
|
||||||
|
.tabs
|
||||||
|
.values()
|
||||||
|
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
|
||||||
|
.collect::<Vec<_>>());
|
||||||
|
|
||||||
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
|
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -209,18 +215,15 @@ pub fn close_the_middle_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_focus_left_at_left_screen_edge_changes_tab() {
|
fn move_focus_left_at_left_screen_edge_changes_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.new_tab(3);
|
new_tab(&mut screen, 3);
|
||||||
screen.switch_tab_prev();
|
screen.switch_tab_prev();
|
||||||
screen.move_focus_left_or_previous_tab();
|
screen.move_focus_left_or_previous_tab();
|
||||||
|
|
||||||
@ -233,18 +236,15 @@ fn move_focus_left_at_left_screen_edge_changes_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn move_focus_right_at_right_screen_edge_changes_tab() {
|
fn move_focus_right_at_right_screen_edge_changes_tab() {
|
||||||
let position_and_size = PositionAndSize {
|
let size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.new_tab(3);
|
new_tab(&mut screen, 3);
|
||||||
screen.switch_tab_prev();
|
screen.switch_tab_prev();
|
||||||
screen.move_focus_right_or_next_tab();
|
screen.move_focus_right_or_next_tab();
|
||||||
|
|
||||||
@ -257,17 +257,14 @@ fn move_focus_right_at_right_screen_edge_changes_tab() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn toggle_to_previous_tab_simple() {
|
pub fn toggle_to_previous_tab_simple() {
|
||||||
let position_and_size = PositionAndSize {
|
let position_and_size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(position_and_size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.go_to_tab(1);
|
screen.go_to_tab(1);
|
||||||
screen.go_to_tab(2);
|
screen.go_to_tab(2);
|
||||||
|
|
||||||
@ -288,18 +285,15 @@ pub fn toggle_to_previous_tab_simple() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn toggle_to_previous_tab_create_tabs_only() {
|
pub fn toggle_to_previous_tab_create_tabs_only() {
|
||||||
let position_and_size = PositionAndSize {
|
let position_and_size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(position_and_size);
|
||||||
|
|
||||||
screen.new_tab(1);
|
new_tab(&mut screen, 1);
|
||||||
screen.new_tab(2);
|
new_tab(&mut screen, 2);
|
||||||
screen.new_tab(3);
|
new_tab(&mut screen, 3);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
screen.tab_history,
|
screen.tab_history,
|
||||||
@ -341,19 +335,16 @@ pub fn toggle_to_previous_tab_create_tabs_only() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn toggle_to_previous_tab_delete() {
|
pub fn toggle_to_previous_tab_delete() {
|
||||||
let position_and_size = PositionAndSize {
|
let position_and_size = Size {
|
||||||
cols: 121,
|
cols: 121,
|
||||||
rows: 20,
|
rows: 20,
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
let mut screen = create_new_screen(position_and_size);
|
let mut screen = create_new_screen(position_and_size);
|
||||||
|
|
||||||
screen.new_tab(1); // 0
|
new_tab(&mut screen, 1); // 0
|
||||||
screen.new_tab(2); // 1
|
new_tab(&mut screen, 2); // 1
|
||||||
screen.new_tab(3); // 2
|
new_tab(&mut screen, 3); // 2
|
||||||
screen.new_tab(4); // 3
|
new_tab(&mut screen, 4); // 3
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
screen.tab_history,
|
screen.tab_history,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -170,9 +170,6 @@ pub(crate) fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObj
|
|||||||
zellij_export! {
|
zellij_export! {
|
||||||
host_subscribe,
|
host_subscribe,
|
||||||
host_unsubscribe,
|
host_unsubscribe,
|
||||||
host_set_invisible_borders,
|
|
||||||
host_set_fixed_height,
|
|
||||||
host_set_fixed_width,
|
|
||||||
host_set_selectable,
|
host_set_selectable,
|
||||||
host_get_plugin_ids,
|
host_get_plugin_ids,
|
||||||
host_open_file,
|
host_open_file,
|
||||||
@ -204,42 +201,6 @@ fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn host_set_fixed_height(plugin_env: &PluginEnv, fixed_height: i32) {
|
|
||||||
let fixed_height = fixed_height as usize;
|
|
||||||
plugin_env
|
|
||||||
.senders
|
|
||||||
.send_to_screen(ScreenInstruction::SetFixedHeight(
|
|
||||||
PaneId::Plugin(plugin_env.plugin_id),
|
|
||||||
fixed_height,
|
|
||||||
plugin_env.tab_index,
|
|
||||||
))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn host_set_fixed_width(plugin_env: &PluginEnv, fixed_width: i32) {
|
|
||||||
let fixed_width = fixed_width as usize;
|
|
||||||
plugin_env
|
|
||||||
.senders
|
|
||||||
.send_to_screen(ScreenInstruction::SetFixedWidth(
|
|
||||||
PaneId::Plugin(plugin_env.plugin_id),
|
|
||||||
fixed_width,
|
|
||||||
plugin_env.tab_index,
|
|
||||||
))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn host_set_invisible_borders(plugin_env: &PluginEnv, invisible_borders: i32) {
|
|
||||||
let invisible_borders = invisible_borders != 0;
|
|
||||||
plugin_env
|
|
||||||
.senders
|
|
||||||
.send_to_screen(ScreenInstruction::SetInvisibleBorders(
|
|
||||||
PaneId::Plugin(plugin_env.plugin_id),
|
|
||||||
invisible_borders,
|
|
||||||
plugin_env.tab_index,
|
|
||||||
))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn host_get_plugin_ids(plugin_env: &PluginEnv) {
|
fn host_get_plugin_ids(plugin_env: &PluginEnv) {
|
||||||
let ids = PluginIds {
|
let ids = PluginIds {
|
||||||
plugin_id: plugin_env.plugin_id,
|
plugin_id: plugin_env.plugin_id,
|
||||||
@ -289,7 +250,6 @@ fn host_set_timeout(plugin_env: &PluginEnv, secs: f64) {
|
|||||||
|
|
||||||
// Helper Functions ---------------------------------------------------------------------------------------------------
|
// Helper Functions ---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// FIXME: Unwrap city
|
|
||||||
pub fn wasi_read_string(wasi_env: &WasiEnv) -> String {
|
pub fn wasi_read_string(wasi_env: &WasiEnv) -> String {
|
||||||
let mut state = wasi_env.state();
|
let mut state = wasi_env.state();
|
||||||
let wasi_file = state.fs.stdout_mut().unwrap().as_mut().unwrap();
|
let wasi_file = state.fs.stdout_mut().unwrap().as_mut().unwrap();
|
||||||
|
@ -17,22 +17,10 @@ pub fn unsubscribe(event_types: &[EventType]) {
|
|||||||
|
|
||||||
// Plugin Settings
|
// Plugin Settings
|
||||||
|
|
||||||
pub fn set_fixed_height(fixed_height: i32) {
|
|
||||||
unsafe { host_set_fixed_height(fixed_height) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_fixed_width(fixed_width: i32) {
|
|
||||||
unsafe { host_set_fixed_width(fixed_width) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_selectable(selectable: bool) {
|
pub fn set_selectable(selectable: bool) {
|
||||||
unsafe { host_set_selectable(if selectable { 1 } else { 0 }) };
|
unsafe { host_set_selectable(if selectable { 1 } else { 0 }) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_invisible_borders(invisible_borders: bool) {
|
|
||||||
unsafe { host_set_invisible_borders(if invisible_borders { 1 } else { 0 }) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query Functions
|
// Query Functions
|
||||||
pub fn get_plugin_ids() -> PluginIds {
|
pub fn get_plugin_ids() -> PluginIds {
|
||||||
unsafe { host_get_plugin_ids() };
|
unsafe { host_get_plugin_ids() };
|
||||||
@ -68,10 +56,7 @@ pub fn object_to_stdout(object: &impl Serialize) {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
fn host_subscribe();
|
fn host_subscribe();
|
||||||
fn host_unsubscribe();
|
fn host_unsubscribe();
|
||||||
fn host_set_fixed_height(fixed_height: i32);
|
|
||||||
fn host_set_fixed_width(fixed_width: i32);
|
|
||||||
fn host_set_selectable(selectable: i32);
|
fn host_set_selectable(selectable: i32);
|
||||||
fn host_set_invisible_borders(invisible_borders: i32);
|
|
||||||
fn host_get_plugin_ids();
|
fn host_get_plugin_ids();
|
||||||
fn host_open_file();
|
fn host_open_file();
|
||||||
fn host_set_timeout(secs: f64);
|
fn host_set_timeout(secs: f64);
|
||||||
|
@ -22,7 +22,7 @@ pub enum Direction {
|
|||||||
// They might need to be adjusted in the default config
|
// They might need to be adjusted in the default config
|
||||||
// as well `../../assets/config/default.yaml`
|
// as well `../../assets/config/default.yaml`
|
||||||
/// Actions that can be bound to keys.
|
/// Actions that can be bound to keys.
|
||||||
#[derive(Eq, Clone, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Quit Zellij.
|
/// Quit Zellij.
|
||||||
Quit,
|
Quit,
|
||||||
|
@ -10,28 +10,43 @@
|
|||||||
// then [`zellij-utils`] could be a proper place.
|
// then [`zellij-utils`] could be a proper place.
|
||||||
use crate::{
|
use crate::{
|
||||||
input::{command::RunCommand, config::ConfigError},
|
input::{command::RunCommand, config::ConfigError},
|
||||||
pane_size::PositionAndSize,
|
pane_size::{Dimension, PaneGeom},
|
||||||
setup,
|
setup,
|
||||||
};
|
};
|
||||||
use crate::{serde, serde_yaml};
|
use crate::{serde, serde_yaml};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use std::{
|
||||||
|
cmp::max,
|
||||||
|
ops::Not,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
use std::{fs::File, io::prelude::*};
|
use std::{fs::File, io::prelude::*};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
|
||||||
#[serde(crate = "self::serde")]
|
#[serde(crate = "self::serde")]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical,
|
Vertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
|
impl Not for Direction {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Direction::Horizontal => Direction::Vertical,
|
||||||
|
Direction::Vertical => Direction::Horizontal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
|
||||||
#[serde(crate = "self::serde")]
|
#[serde(crate = "self::serde")]
|
||||||
pub enum SplitSize {
|
pub enum SplitSize {
|
||||||
Percent(u8), // 1 to 100
|
Percent(f64), // 1 to 100
|
||||||
Fixed(u16), // An absolute number of columns or rows
|
Fixed(usize), // An absolute number of columns or rows
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
@ -44,7 +59,7 @@ pub enum Run {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The layout struct ultimately used to build the layouts.
|
// The layout struct ultimately used to build the layouts.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(crate = "self::serde")]
|
#[serde(crate = "self::serde")]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
@ -58,7 +73,7 @@ pub struct Layout {
|
|||||||
|
|
||||||
// The struct that is used to deserialize the layout from
|
// The struct that is used to deserialize the layout from
|
||||||
// a yaml configuration file
|
// a yaml configuration file
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(crate = "self::serde")]
|
#[serde(crate = "self::serde")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LayoutFromYaml {
|
pub struct LayoutFromYaml {
|
||||||
@ -148,7 +163,7 @@ impl LayoutFromYaml {
|
|||||||
|
|
||||||
// The struct that carries the information template that is used to
|
// The struct that carries the information template that is used to
|
||||||
// construct the layout
|
// construct the layout
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(crate = "self::serde")]
|
#[serde(crate = "self::serde")]
|
||||||
pub struct LayoutTemplate {
|
pub struct LayoutTemplate {
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
@ -191,7 +206,7 @@ impl LayoutTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The tab-layout struct used to specify each individual tab.
|
// The tab-layout struct used to specify each individual tab.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(crate = "self::serde")]
|
#[serde(crate = "self::serde")]
|
||||||
pub struct TabLayout {
|
pub struct TabLayout {
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
@ -238,10 +253,7 @@ impl Layout {
|
|||||||
run_instructions
|
run_instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position_panes_in_space(
|
pub fn position_panes_in_space(&self, space: &PaneGeom) -> Vec<(Layout, PaneGeom)> {
|
||||||
&self,
|
|
||||||
space: &PositionAndSize,
|
|
||||||
) -> Vec<(Layout, PositionAndSize)> {
|
|
||||||
split_space(space, self)
|
split_space(space, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,149 +280,90 @@ impl Layout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_space_to_parts_vertically(
|
fn layout_size(direction: Direction, layout: &Layout) -> usize {
|
||||||
space_to_split: &PositionAndSize,
|
fn child_layout_size(
|
||||||
sizes: Vec<Option<SplitSize>>,
|
direction: Direction,
|
||||||
) -> Vec<PositionAndSize> {
|
parent_direction: Direction,
|
||||||
let mut split_parts = Vec::new();
|
layout: &Layout,
|
||||||
let mut current_x_position = space_to_split.x;
|
) -> usize {
|
||||||
let mut current_width = 0;
|
let size = if parent_direction == direction { 1 } else { 0 };
|
||||||
let max_width = space_to_split.cols;
|
if layout.parts.is_empty() {
|
||||||
|
size
|
||||||
let mut parts_to_grow = Vec::new();
|
} else {
|
||||||
|
let children_size = layout
|
||||||
// First fit in the parameterized sizes
|
.parts
|
||||||
for size in sizes {
|
.iter()
|
||||||
let columns = match size {
|
.map(|p| child_layout_size(direction, layout.direction, p))
|
||||||
Some(SplitSize::Percent(percent)) => {
|
.sum::<usize>();
|
||||||
(max_width as f32 * (percent as f32 / 100.0)) as usize
|
max(size, children_size)
|
||||||
} // TODO: round properly
|
|
||||||
Some(SplitSize::Fixed(size)) => size as usize,
|
|
||||||
None => {
|
|
||||||
parts_to_grow.push(current_x_position);
|
|
||||||
1 // This is grown later on
|
|
||||||
}
|
|
||||||
};
|
|
||||||
split_parts.push(PositionAndSize {
|
|
||||||
x: current_x_position,
|
|
||||||
y: space_to_split.y,
|
|
||||||
cols: columns,
|
|
||||||
rows: space_to_split.rows,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
current_width += columns;
|
|
||||||
current_x_position += columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
part.cols = new_columns;
|
|
||||||
last_flexible_index = idx;
|
|
||||||
}
|
|
||||||
current_width += part.cols;
|
|
||||||
current_x_position += part.cols;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
child_layout_size(direction, direction, layout)
|
||||||
if current_width < max_width {
|
|
||||||
// we have some extra space left, let's add it to the last flexible part
|
|
||||||
let extra = max_width - current_width;
|
|
||||||
let mut last_part = split_parts.get_mut(last_flexible_index).unwrap();
|
|
||||||
last_part.cols += extra;
|
|
||||||
for part in (&mut split_parts[last_flexible_index + 1..]).iter_mut() {
|
|
||||||
part.x += extra;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
split_parts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_space_to_parts_horizontally(
|
fn split_space(space_to_split: &PaneGeom, layout: &Layout) -> Vec<(Layout, PaneGeom)> {
|
||||||
space_to_split: &PositionAndSize,
|
|
||||||
sizes: Vec<Option<SplitSize>>,
|
|
||||||
) -> Vec<PositionAndSize> {
|
|
||||||
let mut split_parts = Vec::new();
|
|
||||||
let mut current_y_position = space_to_split.y;
|
|
||||||
let mut current_height = 0;
|
|
||||||
let max_height = space_to_split.rows;
|
|
||||||
|
|
||||||
let mut parts_to_grow = Vec::new();
|
|
||||||
|
|
||||||
for size in sizes {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
split_parts.push(PositionAndSize {
|
|
||||||
x: space_to_split.x,
|
|
||||||
y: current_y_position,
|
|
||||||
cols: space_to_split.cols,
|
|
||||||
rows,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
current_height += rows;
|
|
||||||
current_y_position += rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
if current_height > max_height {
|
|
||||||
panic!("Layout contained too many rows to fit onto the screen!");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut last_flexible_index = split_parts.len() - 1;
|
|
||||||
if let Some(new_rows) = (max_height - current_height).checked_div(parts_to_grow.len()) {
|
|
||||||
current_height = 0;
|
|
||||||
current_y_position = 0;
|
|
||||||
|
|
||||||
for (idx, part) in split_parts.iter_mut().enumerate() {
|
|
||||||
part.y = current_y_position;
|
|
||||||
if parts_to_grow.contains(&part.y) {
|
|
||||||
part.rows = new_rows;
|
|
||||||
last_flexible_index = idx;
|
|
||||||
}
|
|
||||||
current_height += part.rows;
|
|
||||||
current_y_position += part.rows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if current_height < max_height {
|
|
||||||
// we have some extra space left, let's add it to the last flexible part
|
|
||||||
let extra = max_height - current_height;
|
|
||||||
let mut last_part = split_parts.get_mut(last_flexible_index).unwrap();
|
|
||||||
last_part.rows += extra;
|
|
||||||
for part in (&mut split_parts[last_flexible_index + 1..]).iter_mut() {
|
|
||||||
part.y += extra;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
split_parts
|
|
||||||
}
|
|
||||||
|
|
||||||
fn split_space(
|
|
||||||
space_to_split: &PositionAndSize,
|
|
||||||
layout: &Layout,
|
|
||||||
) -> Vec<(Layout, PositionAndSize)> {
|
|
||||||
let mut pane_positions = Vec::new();
|
let mut pane_positions = Vec::new();
|
||||||
let sizes: Vec<Option<SplitSize>> = layout.parts.iter().map(|part| part.split_size).collect();
|
let sizes: Vec<Option<SplitSize>> = layout.parts.iter().map(|part| part.split_size).collect();
|
||||||
|
|
||||||
let split_parts = match layout.direction {
|
let mut split_geom = Vec::new();
|
||||||
Direction::Vertical => split_space_to_parts_vertically(space_to_split, sizes),
|
let (mut current_position, split_dimension_space, mut inherited_dimension) =
|
||||||
Direction::Horizontal => split_space_to_parts_horizontally(space_to_split, sizes),
|
match layout.direction {
|
||||||
};
|
Direction::Vertical => (space_to_split.x, space_to_split.cols, space_to_split.rows),
|
||||||
|
Direction::Horizontal => (space_to_split.y, space_to_split.rows, space_to_split.cols),
|
||||||
|
};
|
||||||
|
|
||||||
|
let flex_parts = sizes.iter().filter(|s| s.is_none()).count();
|
||||||
|
|
||||||
|
for (&size, part) in sizes.iter().zip(&layout.parts) {
|
||||||
|
let split_dimension = match size {
|
||||||
|
Some(SplitSize::Percent(percent)) => Dimension::percent(percent),
|
||||||
|
Some(SplitSize::Fixed(size)) => Dimension::fixed(size),
|
||||||
|
None => {
|
||||||
|
let free_percent = if let Some(p) = split_dimension_space.as_percent() {
|
||||||
|
p - sizes
|
||||||
|
.iter()
|
||||||
|
.map(|&s| {
|
||||||
|
if let Some(SplitSize::Percent(ip)) = s {
|
||||||
|
ip
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum::<f64>()
|
||||||
|
} else {
|
||||||
|
panic!("Implicit sizing within fixed-size panes is not supported");
|
||||||
|
};
|
||||||
|
Dimension::percent(free_percent / flex_parts as f64)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
inherited_dimension.set_inner(
|
||||||
|
layout
|
||||||
|
.parts
|
||||||
|
.iter()
|
||||||
|
.map(|p| layout_size(!layout.direction, p))
|
||||||
|
.max()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let geom = match layout.direction {
|
||||||
|
Direction::Vertical => PaneGeom {
|
||||||
|
x: current_position,
|
||||||
|
y: space_to_split.y,
|
||||||
|
cols: split_dimension,
|
||||||
|
rows: inherited_dimension,
|
||||||
|
},
|
||||||
|
Direction::Horizontal => PaneGeom {
|
||||||
|
x: space_to_split.x,
|
||||||
|
y: current_position,
|
||||||
|
cols: inherited_dimension,
|
||||||
|
rows: split_dimension,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
split_geom.push(geom);
|
||||||
|
current_position += layout_size(layout.direction, part);
|
||||||
|
}
|
||||||
|
|
||||||
for (i, part) in layout.parts.iter().enumerate() {
|
for (i, part) in layout.parts.iter().enumerate() {
|
||||||
let part_position_and_size = split_parts.get(i).unwrap();
|
let part_position_and_size = split_geom.get(i).unwrap();
|
||||||
if !part.parts.is_empty() {
|
if !part.parts.is_empty() {
|
||||||
let mut part_positions = split_space(part_position_and_size, part);
|
let mut part_positions = split_space(part_position_and_size, part);
|
||||||
pane_positions.append(&mut part_positions);
|
pane_positions.append(&mut part_positions);
|
||||||
|
@ -163,7 +163,7 @@ fn three_panes_with_tab_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -174,14 +174,14 @@ fn three_panes_with_tab_merged_correctly() {
|
|||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -263,7 +263,7 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -274,14 +274,14 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() {
|
|||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -380,7 +380,7 @@ fn deeply_nested_tab_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(21)),
|
split_size: Some(SplitSize::Percent(21.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -391,7 +391,7 @@ fn deeply_nested_tab_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(22)),
|
split_size: Some(SplitSize::Percent(22.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -402,47 +402,47 @@ fn deeply_nested_tab_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(23)),
|
split_size: Some(SplitSize::Percent(23.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(24)),
|
split_size: Some(SplitSize::Percent(24.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
split_size: Some(SplitSize::Percent(78)),
|
split_size: Some(SplitSize::Percent(78.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
split_size: Some(SplitSize::Percent(79)),
|
split_size: Some(SplitSize::Percent(79.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
split_size: Some(SplitSize::Percent(90)),
|
split_size: Some(SplitSize::Percent(90.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(15)),
|
split_size: Some(SplitSize::Percent(15.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(15)),
|
split_size: Some(SplitSize::Percent(15.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(15)),
|
split_size: Some(SplitSize::Percent(15.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -484,7 +484,7 @@ fn three_tabs_tab_one_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -523,7 +523,7 @@ fn three_tabs_tab_two_merged_correctly() {
|
|||||||
direction: Direction::Horizontal,
|
direction: Direction::Horizontal,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -534,7 +534,7 @@ fn three_tabs_tab_two_merged_correctly() {
|
|||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -573,7 +573,7 @@ fn three_tabs_tab_three_merged_correctly() {
|
|||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
borderless: false,
|
borderless: false,
|
||||||
parts: vec![],
|
parts: vec![],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
@ -584,7 +584,7 @@ fn three_tabs_tab_three_merged_correctly() {
|
|||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
split_size: Some(SplitSize::Percent(50)),
|
split_size: Some(SplitSize::Percent(50.0)),
|
||||||
run: None,
|
run: None,
|
||||||
},
|
},
|
||||||
Layout {
|
Layout {
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
cli::CliArgs,
|
cli::CliArgs,
|
||||||
errors::{get_current_ctx, ErrorContext},
|
errors::{get_current_ctx, ErrorContext},
|
||||||
input::{actions::Action, layout::LayoutFromYaml, options::Options},
|
input::{actions::Action, layout::LayoutFromYaml, options::Options},
|
||||||
pane_size::PositionAndSize,
|
pane_size::Size,
|
||||||
};
|
};
|
||||||
use interprocess::local_socket::LocalSocketStream;
|
use interprocess::local_socket::LocalSocketStream;
|
||||||
use nix::unistd::dup;
|
use nix::unistd::dup;
|
||||||
@ -39,7 +39,7 @@ pub enum ClientType {
|
|||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
pub struct ClientAttributes {
|
pub struct ClientAttributes {
|
||||||
pub position_and_size: PositionAndSize,
|
pub size: Size,
|
||||||
pub palette: Palette,
|
pub palette: Palette,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,13 +57,8 @@ pub enum ClientToServerMsg {
|
|||||||
DetachSession(SessionId),
|
DetachSession(SessionId),
|
||||||
// Disconnect from the session we're connected to
|
// Disconnect from the session we're connected to
|
||||||
DisconnectFromSession,*/
|
DisconnectFromSession,*/
|
||||||
TerminalResize(PositionAndSize),
|
TerminalResize(Size),
|
||||||
NewClient(
|
NewClient(ClientAttributes, Box<CliArgs>, Box<Options>, LayoutFromYaml),
|
||||||
ClientAttributes,
|
|
||||||
Box<CliArgs>,
|
|
||||||
Box<Options>,
|
|
||||||
Option<LayoutFromYaml>,
|
|
||||||
),
|
|
||||||
AttachClient(ClientAttributes, bool, Options),
|
AttachClient(ClientAttributes, bool, Options),
|
||||||
Action(Action),
|
Action(Action),
|
||||||
ClientExited,
|
ClientExited,
|
||||||
|
@ -1,49 +1,144 @@
|
|||||||
use nix::pty::Winsize;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::position::Position;
|
use crate::position::Position;
|
||||||
|
|
||||||
/// Contains the position and size of a [`Pane`], or more generally of any terminal, measured
|
/// Contains the position and size of a [`Pane`], or more generally of any terminal, measured
|
||||||
/// in character rows and columns.
|
/// in character rows and columns.
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct PaneGeom {
|
||||||
|
pub x: usize,
|
||||||
|
pub y: usize,
|
||||||
|
pub rows: Dimension,
|
||||||
|
pub cols: Dimension,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct PositionAndSize {
|
pub struct Viewport {
|
||||||
pub x: usize,
|
pub x: usize,
|
||||||
pub y: usize,
|
pub y: usize,
|
||||||
pub rows: usize,
|
pub rows: usize,
|
||||||
pub cols: usize,
|
pub cols: usize,
|
||||||
// FIXME: Honestly, these shouldn't exist and rows / columns should be enums like:
|
|
||||||
// Dimension::Flex(usize) / Dimension::Fixed(usize), but 400+ compiler errors is more than
|
|
||||||
// I'm in the mood for right now...
|
|
||||||
pub rows_fixed: bool,
|
|
||||||
pub cols_fixed: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Winsize> for PositionAndSize {
|
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
fn from(winsize: Winsize) -> PositionAndSize {
|
pub struct Offset {
|
||||||
PositionAndSize {
|
pub top: usize,
|
||||||
cols: winsize.ws_col as usize,
|
pub bottom: usize,
|
||||||
rows: winsize.ws_row as usize,
|
pub right: usize,
|
||||||
|
pub left: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Size {
|
||||||
|
pub rows: usize,
|
||||||
|
pub cols: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Dimension {
|
||||||
|
pub constraint: Constraint,
|
||||||
|
inner: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Dimension {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::percent(100.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dimension {
|
||||||
|
pub fn fixed(size: usize) -> Dimension {
|
||||||
|
Self {
|
||||||
|
constraint: Constraint::Fixed(size),
|
||||||
|
inner: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn percent(percent: f64) -> Dimension {
|
||||||
|
Self {
|
||||||
|
constraint: Constraint::Percent(percent),
|
||||||
|
inner: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_usize(&self) -> usize {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_percent(&self) -> Option<f64> {
|
||||||
|
if let Constraint::Percent(p) = self.constraint {
|
||||||
|
Some(p)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_inner(&mut self, inner: usize) {
|
||||||
|
self.inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fixed(&self) -> bool {
|
||||||
|
matches!(self.constraint, Constraint::Fixed(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum Constraint {
|
||||||
|
/// Constrains the dimension to a fixed, integer number of rows / columns
|
||||||
|
Fixed(usize),
|
||||||
|
/// Constrains the dimension to a flexible percent size of the total screen
|
||||||
|
Percent(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneGeom {
|
||||||
|
pub fn contains(&self, point: &Position) -> bool {
|
||||||
|
let col = point.column.0 as usize;
|
||||||
|
let row = point.line.0 as usize;
|
||||||
|
self.x <= col
|
||||||
|
&& col < self.x + self.cols.as_usize()
|
||||||
|
&& self.y <= row
|
||||||
|
&& row < self.y + self.rows.as_usize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Offset {
|
||||||
|
pub fn frame(size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
top: size,
|
||||||
|
bottom: size,
|
||||||
|
right: size,
|
||||||
|
left: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This should be top and left, not bottom and right, but `boundaries.rs` would need
|
||||||
|
// some changing
|
||||||
|
pub fn shift(bottom: usize, right: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
bottom,
|
||||||
|
right,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PositionAndSize {
|
impl From<PaneGeom> for Viewport {
|
||||||
pub fn contains(&self, point: &Position) -> bool {
|
fn from(pane: PaneGeom) -> Self {
|
||||||
let col = point.column.0 as usize;
|
Self {
|
||||||
let row = point.line.0 as usize;
|
x: pane.x,
|
||||||
self.x <= col && col < self.x + self.cols && self.y <= row && row < self.y + self.rows
|
y: pane.y,
|
||||||
}
|
rows: pane.rows.as_usize(),
|
||||||
pub fn reduce_outer_frame(mut self, frame_width: usize) -> Self {
|
cols: pane.cols.as_usize(),
|
||||||
self.x += frame_width;
|
}
|
||||||
self.rows -= frame_width * 2;
|
}
|
||||||
self.y += frame_width;
|
}
|
||||||
self.cols -= frame_width * 2;
|
|
||||||
self
|
impl From<Size> for Viewport {
|
||||||
}
|
fn from(size: Size) -> Self {
|
||||||
pub fn reduce_top_line(mut self) -> Self {
|
Self {
|
||||||
self.y += 1;
|
rows: size.rows,
|
||||||
self.rows -= 1;
|
cols: size.cols,
|
||||||
self
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::pane_size::PositionAndSize;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Deserialize, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Deserialize, Serialize)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
pub line: Line,
|
pub line: Line,
|
||||||
@ -16,10 +14,10 @@ impl Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn relative_to(&self, position_and_size: &PositionAndSize) -> Self {
|
pub fn relative_to(&self, line: usize, column: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
line: Line(self.line.0 - position_and_size.y as isize),
|
line: Line(self.line.0 - line as isize),
|
||||||
column: Column(self.column.0.saturating_sub(position_and_size.x)),
|
column: Column(self.column.0.saturating_sub(column)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
use std::{iter, str::from_utf8};
|
use std::{iter, str::from_utf8};
|
||||||
|
|
||||||
use strip_ansi_escapes::strip;
|
|
||||||
|
|
||||||
use colors_transform::{Color, Rgb};
|
use colors_transform::{Color, Rgb};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
use strip_ansi_escapes::strip;
|
||||||
use zellij_tile::data::{Palette, PaletteColor, PaletteSource, ThemeHue};
|
use zellij_tile::data::{Palette, PaletteColor, PaletteSource, ThemeHue};
|
||||||
|
|
||||||
const UNIX_PERMISSIONS: u32 = 0o700;
|
const UNIX_PERMISSIONS: u32 = 0o700;
|
||||||
|
Loading…
Reference in New Issue
Block a user