add(plugin): compact-bar & compact layout (#1450)
* add(plugin): `compact-bar` & `compact` layout * add(nix): `compact-bar` plugin * add(config): `compact-bar` to the config * add(workspace): `compact-bar` to workspace members * add(assets): `compact-bar` * chore(fmt): rustfmt * add(nix): add `compact-bar` * add: compact layout to dump command * nix(build): fix destination of copy command * add(makefile): add `compact-bar` to `plugin-build` * add(layout): `compact-bar` to layout * add: install `compact-bar` plugin * fix(test): update input plugin test * fix(plugin): default colors for compact-bar
This commit is contained in:
parent
ad9ba8ab24
commit
d62e6fb57e
18 changed files with 779 additions and 199 deletions
460
Cargo.lock
generated
460
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -34,6 +34,7 @@ members = [
|
||||||
"zellij-utils",
|
"zellij-utils",
|
||||||
"zellij-tile",
|
"zellij-tile",
|
||||||
"zellij-tile-utils",
|
"zellij-tile-utils",
|
||||||
|
"default-plugins/compact-bar",
|
||||||
"default-plugins/status-bar",
|
"default-plugins/status-bar",
|
||||||
"default-plugins/strider",
|
"default-plugins/strider",
|
||||||
"default-plugins/tab-bar",
|
"default-plugins/tab-bar",
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ end
|
||||||
|
|
||||||
[tasks.build-plugins-release]
|
[tasks.build-plugins-release]
|
||||||
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = [
|
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = [
|
||||||
|
"default-plugins/compact-bar",
|
||||||
"default-plugins/status-bar",
|
"default-plugins/status-bar",
|
||||||
"default-plugins/strider",
|
"default-plugins/strider",
|
||||||
"default-plugins/tab-bar",
|
"default-plugins/tab-bar",
|
||||||
|
|
@ -117,6 +118,7 @@ run_task = { name = "build-release", fork = true }
|
||||||
|
|
||||||
[tasks.build-plugins]
|
[tasks.build-plugins]
|
||||||
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = [
|
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = [
|
||||||
|
"default-plugins/compact-bar",
|
||||||
"default-plugins/status-bar",
|
"default-plugins/status-bar",
|
||||||
"default-plugins/strider",
|
"default-plugins/strider",
|
||||||
"default-plugins/tab-bar",
|
"default-plugins/tab-bar",
|
||||||
|
|
|
||||||
BIN
assets/plugins/compact-bar.wasm
Executable file
BIN
assets/plugins/compact-bar.wasm
Executable file
Binary file not shown.
2
default-plugins/compact-bar/.cargo/config.toml
Normal file
2
default-plugins/compact-bar/.cargo/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[build]
|
||||||
|
target = "wasm32-wasi"
|
||||||
13
default-plugins/compact-bar/Cargo.toml
Normal file
13
default-plugins/compact-bar/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "compact-bar"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Alexander Kenji Berthold <aks.kenji@protonmail.com>" ]
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
colored = "2"
|
||||||
|
ansi_term = "0.12"
|
||||||
|
unicode-width = "0.1.8"
|
||||||
|
zellij-tile = { path = "../../zellij-tile" }
|
||||||
|
zellij-tile-utils = { path = "../../zellij-tile-utils" }
|
||||||
1
default-plugins/compact-bar/LICENSE.md
Symbolic link
1
default-plugins/compact-bar/LICENSE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE.md
|
||||||
243
default-plugins/compact-bar/src/line.rs
Normal file
243
default-plugins/compact-bar/src/line.rs
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
use ansi_term::ANSIStrings;
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
use crate::{LinePart, ARROW_SEPARATOR};
|
||||||
|
use zellij_tile::prelude::*;
|
||||||
|
use zellij_tile_utils::style;
|
||||||
|
|
||||||
|
fn get_current_title_len(current_title: &[LinePart]) -> usize {
|
||||||
|
current_title.iter().map(|p| p.len).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
// move elements from before_active and after_active into tabs_to_render while they fit in cols
|
||||||
|
// adds collapsed_tabs to the left and right if there's left over tabs that don't fit
|
||||||
|
fn populate_tabs_in_tab_line(
|
||||||
|
tabs_before_active: &mut Vec<LinePart>,
|
||||||
|
tabs_after_active: &mut Vec<LinePart>,
|
||||||
|
tabs_to_render: &mut Vec<LinePart>,
|
||||||
|
cols: usize,
|
||||||
|
palette: Palette,
|
||||||
|
capabilities: PluginCapabilities,
|
||||||
|
) {
|
||||||
|
let mut middle_size = get_current_title_len(tabs_to_render);
|
||||||
|
|
||||||
|
let mut total_left = 0;
|
||||||
|
let mut total_right = 0;
|
||||||
|
loop {
|
||||||
|
let left_count = tabs_before_active.len();
|
||||||
|
let right_count = tabs_after_active.len();
|
||||||
|
let collapsed_left = left_more_message(left_count, palette, tab_separator(capabilities));
|
||||||
|
let collapsed_right = right_more_message(right_count, palette, tab_separator(capabilities));
|
||||||
|
|
||||||
|
let total_size = collapsed_left.len + middle_size + collapsed_right.len;
|
||||||
|
|
||||||
|
if total_size > cols {
|
||||||
|
// break and dont add collapsed tabs to tabs_to_render, they will not fit
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let left = if let Some(tab) = tabs_before_active.last() {
|
||||||
|
tab.len
|
||||||
|
} else {
|
||||||
|
usize::MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
let right = if let Some(tab) = tabs_after_active.first() {
|
||||||
|
tab.len
|
||||||
|
} else {
|
||||||
|
usize::MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
// total size is shortened if the next tab to be added is the last one, as that will remove the collapsed tab
|
||||||
|
let size_by_adding_left =
|
||||||
|
left.saturating_add(total_size)
|
||||||
|
.saturating_sub(if left_count == 1 {
|
||||||
|
collapsed_left.len
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
});
|
||||||
|
let size_by_adding_right =
|
||||||
|
right
|
||||||
|
.saturating_add(total_size)
|
||||||
|
.saturating_sub(if right_count == 1 {
|
||||||
|
collapsed_right.len
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
});
|
||||||
|
|
||||||
|
let left_fits = size_by_adding_left <= cols;
|
||||||
|
let right_fits = size_by_adding_right <= cols;
|
||||||
|
// active tab is kept in the middle by adding to the side that
|
||||||
|
// has less width, or if the tab on the other side doesn' fit
|
||||||
|
if (total_left <= total_right || !right_fits) && left_fits {
|
||||||
|
// add left tab
|
||||||
|
let tab = tabs_before_active.pop().unwrap();
|
||||||
|
middle_size += tab.len;
|
||||||
|
total_left += tab.len;
|
||||||
|
tabs_to_render.insert(0, tab);
|
||||||
|
} else if right_fits {
|
||||||
|
// add right tab
|
||||||
|
let tab = tabs_after_active.remove(0);
|
||||||
|
middle_size += tab.len;
|
||||||
|
total_right += tab.len;
|
||||||
|
tabs_to_render.push(tab);
|
||||||
|
} else {
|
||||||
|
// there's either no space to add more tabs or no more tabs to add, so we're done
|
||||||
|
tabs_to_render.insert(0, collapsed_left);
|
||||||
|
tabs_to_render.push(collapsed_right);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn left_more_message(tab_count_to_the_left: usize, palette: Palette, separator: &str) -> LinePart {
|
||||||
|
if tab_count_to_the_left == 0 {
|
||||||
|
return LinePart::default();
|
||||||
|
}
|
||||||
|
let more_text = if tab_count_to_the_left < 10000 {
|
||||||
|
format!(" ← +{} ", tab_count_to_the_left)
|
||||||
|
} else {
|
||||||
|
" ← +many ".to_string()
|
||||||
|
};
|
||||||
|
// 238
|
||||||
|
// chars length plus separator length on both sides
|
||||||
|
let more_text_len = more_text.width() + 2 * separator.width();
|
||||||
|
let text_color = match palette.theme_hue {
|
||||||
|
ThemeHue::Dark => palette.white,
|
||||||
|
ThemeHue::Light => palette.black,
|
||||||
|
};
|
||||||
|
let left_separator = style!(text_color, palette.orange).paint(separator);
|
||||||
|
let more_styled_text = style!(text_color, palette.orange).bold().paint(more_text);
|
||||||
|
let right_separator = style!(palette.orange, text_color).paint(separator);
|
||||||
|
let more_styled_text =
|
||||||
|
ANSIStrings(&[left_separator, more_styled_text, right_separator]).to_string();
|
||||||
|
LinePart {
|
||||||
|
part: more_styled_text,
|
||||||
|
len: more_text_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn right_more_message(
|
||||||
|
tab_count_to_the_right: usize,
|
||||||
|
palette: Palette,
|
||||||
|
separator: &str,
|
||||||
|
) -> LinePart {
|
||||||
|
if tab_count_to_the_right == 0 {
|
||||||
|
return LinePart::default();
|
||||||
|
};
|
||||||
|
let more_text = if tab_count_to_the_right < 10000 {
|
||||||
|
format!(" +{} → ", tab_count_to_the_right)
|
||||||
|
} else {
|
||||||
|
" +many → ".to_string()
|
||||||
|
};
|
||||||
|
// chars length plus separator length on both sides
|
||||||
|
let more_text_len = more_text.width() + 2 * separator.width();
|
||||||
|
let text_color = match palette.theme_hue {
|
||||||
|
ThemeHue::Dark => palette.white,
|
||||||
|
ThemeHue::Light => palette.black,
|
||||||
|
};
|
||||||
|
let left_separator = style!(text_color, palette.orange).paint(separator);
|
||||||
|
let more_styled_text = style!(text_color, palette.orange).bold().paint(more_text);
|
||||||
|
let right_separator = style!(palette.orange, text_color).paint(separator);
|
||||||
|
let more_styled_text =
|
||||||
|
ANSIStrings(&[left_separator, more_styled_text, right_separator]).to_string();
|
||||||
|
LinePart {
|
||||||
|
part: more_styled_text,
|
||||||
|
len: more_text_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_line_prefix(
|
||||||
|
session_name: Option<&str>,
|
||||||
|
mode: InputMode,
|
||||||
|
palette: Palette,
|
||||||
|
cols: usize,
|
||||||
|
) -> Vec<LinePart> {
|
||||||
|
let prefix_text = " Zellij ".to_string();
|
||||||
|
|
||||||
|
let prefix_text_len = prefix_text.chars().count();
|
||||||
|
let text_color = match palette.theme_hue {
|
||||||
|
ThemeHue::Dark => palette.white,
|
||||||
|
ThemeHue::Light => palette.black,
|
||||||
|
};
|
||||||
|
let bg_color = match palette.theme_hue {
|
||||||
|
ThemeHue::Dark => palette.black,
|
||||||
|
ThemeHue::Light => palette.white,
|
||||||
|
};
|
||||||
|
let prefix_styled_text = style!(text_color, bg_color).bold().paint(prefix_text);
|
||||||
|
let mut parts = vec![LinePart {
|
||||||
|
part: prefix_styled_text.to_string(),
|
||||||
|
len: prefix_text_len,
|
||||||
|
}];
|
||||||
|
if let Some(name) = session_name {
|
||||||
|
let name_part = format!("({}) ", name);
|
||||||
|
let name_part_len = name_part.width();
|
||||||
|
let text_color = match palette.theme_hue {
|
||||||
|
ThemeHue::Dark => palette.white,
|
||||||
|
ThemeHue::Light => palette.black,
|
||||||
|
};
|
||||||
|
let name_part_styled_text = style!(text_color, bg_color).bold().paint(name_part);
|
||||||
|
if cols.saturating_sub(prefix_text_len) >= name_part_len {
|
||||||
|
parts.push(LinePart {
|
||||||
|
part: name_part_styled_text.to_string(),
|
||||||
|
len: name_part_len,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mode_part = format!("({:?})", mode);
|
||||||
|
let mode_part_len = mode_part.width();
|
||||||
|
let mode_part_styled_text = style!(text_color, bg_color).bold().paint(mode_part);
|
||||||
|
if cols.saturating_sub(prefix_text_len) >= mode_part_len {
|
||||||
|
parts.push(LinePart {
|
||||||
|
part: format!("({:^6})", mode_part_styled_text),
|
||||||
|
len: mode_part_len,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
parts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tab_separator(capabilities: PluginCapabilities) -> &'static str {
|
||||||
|
if !capabilities.arrow_fonts {
|
||||||
|
ARROW_SEPARATOR
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tab_line(
|
||||||
|
session_name: Option<&str>,
|
||||||
|
mut all_tabs: Vec<LinePart>,
|
||||||
|
active_tab_index: usize,
|
||||||
|
cols: usize,
|
||||||
|
palette: Palette,
|
||||||
|
capabilities: PluginCapabilities,
|
||||||
|
mode: InputMode,
|
||||||
|
) -> Vec<LinePart> {
|
||||||
|
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
|
||||||
|
let mut tabs_before_active = all_tabs;
|
||||||
|
let active_tab = if !tabs_after_active.is_empty() {
|
||||||
|
tabs_after_active.remove(0)
|
||||||
|
} else {
|
||||||
|
tabs_before_active.pop().unwrap()
|
||||||
|
};
|
||||||
|
let mut prefix = tab_line_prefix(session_name, mode, palette, cols);
|
||||||
|
let prefix_len = get_current_title_len(&prefix);
|
||||||
|
|
||||||
|
// if active tab alone won't fit in cols, don't draw any tabs
|
||||||
|
if prefix_len + active_tab.len > cols {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tabs_to_render = vec![active_tab];
|
||||||
|
|
||||||
|
populate_tabs_in_tab_line(
|
||||||
|
&mut tabs_before_active,
|
||||||
|
&mut tabs_after_active,
|
||||||
|
&mut tabs_to_render,
|
||||||
|
cols.saturating_sub(prefix_len),
|
||||||
|
palette,
|
||||||
|
capabilities,
|
||||||
|
);
|
||||||
|
prefix.append(&mut tabs_to_render);
|
||||||
|
prefix
|
||||||
|
}
|
||||||
137
default-plugins/compact-bar/src/main.rs
Normal file
137
default-plugins/compact-bar/src/main.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
mod line;
|
||||||
|
mod tab;
|
||||||
|
|
||||||
|
use std::cmp::{max, min};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use zellij_tile::prelude::*;
|
||||||
|
|
||||||
|
use crate::line::tab_line;
|
||||||
|
use crate::tab::tab_style;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct LinePart {
|
||||||
|
part: String,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct State {
|
||||||
|
tabs: Vec<TabInfo>,
|
||||||
|
active_tab_idx: usize,
|
||||||
|
mode_info: ModeInfo,
|
||||||
|
mouse_click_pos: usize,
|
||||||
|
should_render: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
static ARROW_SEPARATOR: &str = "";
|
||||||
|
|
||||||
|
register_plugin!(State);
|
||||||
|
|
||||||
|
impl ZellijPlugin for State {
|
||||||
|
fn load(&mut self) {
|
||||||
|
set_selectable(false);
|
||||||
|
subscribe(&[
|
||||||
|
EventType::TabUpdate,
|
||||||
|
EventType::ModeUpdate,
|
||||||
|
EventType::Mouse,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, event: Event) {
|
||||||
|
match event {
|
||||||
|
Event::ModeUpdate(mode_info) => self.mode_info = mode_info,
|
||||||
|
Event::TabUpdate(tabs) => {
|
||||||
|
if let Some(active_tab_index) = tabs.iter().position(|t| t.active) {
|
||||||
|
// tabs are indexed starting from 1 so we need to add 1
|
||||||
|
self.active_tab_idx = active_tab_index + 1;
|
||||||
|
self.tabs = tabs;
|
||||||
|
} else {
|
||||||
|
eprintln!("Could not find active tab.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Mouse(me) => match me {
|
||||||
|
Mouse::LeftClick(_, col) => {
|
||||||
|
self.mouse_click_pos = col;
|
||||||
|
self.should_render = true;
|
||||||
|
}
|
||||||
|
Mouse::ScrollUp(_) => {
|
||||||
|
switch_tab_to(min(self.active_tab_idx + 1, self.tabs.len()) as u32);
|
||||||
|
}
|
||||||
|
Mouse::ScrollDown(_) => {
|
||||||
|
switch_tab_to(max(self.active_tab_idx.saturating_sub(1), 1) as u32);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
eprintln!("Got unrecognized event: {:?}", event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _rows: usize, cols: usize) {
|
||||||
|
if self.tabs.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut all_tabs: Vec<LinePart> = vec![];
|
||||||
|
let mut active_tab_index = 0;
|
||||||
|
for t in &mut self.tabs {
|
||||||
|
let mut tabname = t.name.clone();
|
||||||
|
if t.active && self.mode_info.mode == InputMode::RenameTab {
|
||||||
|
if tabname.is_empty() {
|
||||||
|
tabname = String::from("Enter name...");
|
||||||
|
}
|
||||||
|
active_tab_index = t.position;
|
||||||
|
} else if t.active {
|
||||||
|
active_tab_index = t.position;
|
||||||
|
}
|
||||||
|
let tab = tab_style(
|
||||||
|
tabname,
|
||||||
|
t.active,
|
||||||
|
t.is_sync_panes_active,
|
||||||
|
self.mode_info.style.colors,
|
||||||
|
self.mode_info.capabilities,
|
||||||
|
t.other_focused_clients.as_slice(),
|
||||||
|
);
|
||||||
|
all_tabs.push(tab);
|
||||||
|
}
|
||||||
|
let tab_line = tab_line(
|
||||||
|
self.mode_info.session_name.as_deref(),
|
||||||
|
all_tabs,
|
||||||
|
active_tab_index,
|
||||||
|
cols.saturating_sub(1),
|
||||||
|
self.mode_info.style.colors,
|
||||||
|
self.mode_info.capabilities,
|
||||||
|
self.mode_info.mode,
|
||||||
|
);
|
||||||
|
let mut s = String::new();
|
||||||
|
let mut len_cnt = 0;
|
||||||
|
for (idx, bar_part) in tab_line.iter().enumerate() {
|
||||||
|
s = format!("{}{}", s, &bar_part.part);
|
||||||
|
|
||||||
|
if self.should_render
|
||||||
|
&& self.mouse_click_pos > len_cnt
|
||||||
|
&& self.mouse_click_pos <= len_cnt + bar_part.len
|
||||||
|
&& idx > 2
|
||||||
|
{
|
||||||
|
// First three elements of tab_line are "Zellij", session name and empty thing, hence the idx > 2 condition.
|
||||||
|
// Tabs are indexed starting from 1, therefore we need subtract 2 below.
|
||||||
|
switch_tab_to(TryInto::<u32>::try_into(idx).unwrap() - 2);
|
||||||
|
}
|
||||||
|
len_cnt += bar_part.len;
|
||||||
|
}
|
||||||
|
let background = match self.mode_info.style.colors.theme_hue {
|
||||||
|
ThemeHue::Dark => self.mode_info.style.colors.black,
|
||||||
|
ThemeHue::Light => self.mode_info.style.colors.white,
|
||||||
|
};
|
||||||
|
match background {
|
||||||
|
PaletteColor::Rgb((r, g, b)) => {
|
||||||
|
println!("{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", s, r, g, b);
|
||||||
|
}
|
||||||
|
PaletteColor::EightBit(color) => {
|
||||||
|
println!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.should_render = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
84
default-plugins/compact-bar/src/tab.rs
Normal file
84
default-plugins/compact-bar/src/tab.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use crate::{line::tab_separator, LinePart};
|
||||||
|
use ansi_term::{ANSIString, ANSIStrings};
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
use zellij_tile::prelude::*;
|
||||||
|
use zellij_tile_utils::style;
|
||||||
|
|
||||||
|
fn cursors(focused_clients: &[ClientId], palette: Palette) -> (Vec<ANSIString>, usize) {
|
||||||
|
// cursor section, text length
|
||||||
|
let mut len = 0;
|
||||||
|
let mut cursors = vec![];
|
||||||
|
for client_id in focused_clients.iter() {
|
||||||
|
if let Some(color) = client_id_to_colors(*client_id, palette) {
|
||||||
|
cursors.push(style!(color.1, color.0).paint(" "));
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(cursors, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_tab(
|
||||||
|
text: String,
|
||||||
|
palette: Palette,
|
||||||
|
separator: &str,
|
||||||
|
focused_clients: &[ClientId],
|
||||||
|
active: bool,
|
||||||
|
) -> LinePart {
|
||||||
|
let background_color = if active { palette.green } else { palette.fg };
|
||||||
|
let foreground_color = match palette.theme_hue {
|
||||||
|
ThemeHue::Dark => palette.black,
|
||||||
|
ThemeHue::Light => palette.white,
|
||||||
|
};
|
||||||
|
let left_separator = style!(foreground_color, background_color).paint(separator);
|
||||||
|
let mut tab_text_len = text.width() + 2 + separator.width() * 2; // 2 for left and right separators, 2 for the text padding
|
||||||
|
|
||||||
|
let tab_styled_text = style!(foreground_color, background_color)
|
||||||
|
.bold()
|
||||||
|
.paint(format!(" {} ", text));
|
||||||
|
|
||||||
|
let right_separator = style!(background_color, foreground_color).paint(separator);
|
||||||
|
let tab_styled_text = if !focused_clients.is_empty() {
|
||||||
|
let (cursor_section, extra_length) = cursors(focused_clients, palette);
|
||||||
|
tab_text_len += extra_length;
|
||||||
|
let mut s = String::new();
|
||||||
|
let cursor_beginning = style!(foreground_color, background_color)
|
||||||
|
.bold()
|
||||||
|
.paint("[")
|
||||||
|
.to_string();
|
||||||
|
let cursor_section = ANSIStrings(&cursor_section).to_string();
|
||||||
|
let cursor_end = style!(foreground_color, background_color)
|
||||||
|
.bold()
|
||||||
|
.paint("]")
|
||||||
|
.to_string();
|
||||||
|
s.push_str(&left_separator.to_string());
|
||||||
|
s.push_str(&tab_styled_text.to_string());
|
||||||
|
s.push_str(&cursor_beginning);
|
||||||
|
s.push_str(&cursor_section);
|
||||||
|
s.push_str(&cursor_end);
|
||||||
|
s.push_str(&right_separator.to_string());
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
ANSIStrings(&[left_separator, tab_styled_text, right_separator]).to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
LinePart {
|
||||||
|
part: tab_styled_text,
|
||||||
|
len: tab_text_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tab_style(
|
||||||
|
text: String,
|
||||||
|
is_active_tab: bool,
|
||||||
|
is_sync_panes_active: bool,
|
||||||
|
palette: Palette,
|
||||||
|
capabilities: PluginCapabilities,
|
||||||
|
focused_clients: &[ClientId],
|
||||||
|
) -> LinePart {
|
||||||
|
let separator = tab_separator(capabilities);
|
||||||
|
let mut tab_text = text;
|
||||||
|
if is_sync_panes_active {
|
||||||
|
tab_text.push_str(" (Sync)");
|
||||||
|
}
|
||||||
|
render_tab(tab_text, palette, separator, focused_clients, is_active_tab)
|
||||||
|
}
|
||||||
|
|
@ -69,6 +69,7 @@ flake-utils.lib.eachSystem [
|
||||||
];
|
];
|
||||||
|
|
||||||
defaultPlugins = [
|
defaultPlugins = [
|
||||||
|
plugins.compact-bar
|
||||||
plugins.status-bar
|
plugins.status-bar
|
||||||
plugins.tab-bar
|
plugins.tab-bar
|
||||||
plugins.strider
|
plugins.strider
|
||||||
|
|
@ -123,6 +124,7 @@ flake-utils.lib.eachSystem [
|
||||||
cp ${plugins.tab-bar}/bin/tab-bar.wasm assets/plugins/tab-bar.wasm
|
cp ${plugins.tab-bar}/bin/tab-bar.wasm assets/plugins/tab-bar.wasm
|
||||||
cp ${plugins.status-bar}/bin/status-bar.wasm assets/plugins/status-bar.wasm
|
cp ${plugins.status-bar}/bin/status-bar.wasm assets/plugins/status-bar.wasm
|
||||||
cp ${plugins.strider}/bin/strider.wasm assets/plugins/strider.wasm
|
cp ${plugins.strider}/bin/strider.wasm assets/plugins/strider.wasm
|
||||||
|
cp ${plugins.compact-bar}/bin/compact-bar.wasm assets/plugins/compact-bar.wasm
|
||||||
'';
|
'';
|
||||||
|
|
||||||
desktopItems = [
|
desktopItems = [
|
||||||
|
|
|
||||||
|
|
@ -50,4 +50,5 @@ in {
|
||||||
status-bar = makeDefaultPlugin "status-bar";
|
status-bar = makeDefaultPlugin "status-bar";
|
||||||
tab-bar = makeDefaultPlugin "tab-bar";
|
tab-bar = makeDefaultPlugin "tab-bar";
|
||||||
strider = makeDefaultPlugin "strider";
|
strider = makeDefaultPlugin "strider";
|
||||||
|
compact-bar = makeDefaultPlugin "compact-bar";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ macro_rules! asset_map {
|
||||||
pub(crate) fn populate_data_dir(data_dir: &Path) {
|
pub(crate) fn populate_data_dir(data_dir: &Path) {
|
||||||
// First run installation of default plugins & layouts
|
// First run installation of default plugins & layouts
|
||||||
let mut assets = asset_map! {
|
let mut assets = asset_map! {
|
||||||
|
"assets/plugins/compact-bar.wasm" => "plugins/compact-bar.wasm",
|
||||||
"assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm",
|
"assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm",
|
||||||
"assets/plugins/tab-bar.wasm" => "plugins/tab-bar.wasm",
|
"assets/plugins/tab-bar.wasm" => "plugins/tab-bar.wasm",
|
||||||
"assets/plugins/strider.wasm" => "plugins/strider.wasm",
|
"assets/plugins/strider.wasm" => "plugins/strider.wasm",
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,8 @@ plugins:
|
||||||
tag: status-bar
|
tag: status-bar
|
||||||
- path: strider
|
- path: strider
|
||||||
tag: strider
|
tag: strider
|
||||||
|
- path: compact-bar
|
||||||
|
tag: compact-bar
|
||||||
|
|
||||||
# Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP
|
# Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP
|
||||||
# eg. when terminal window with an active zellij session is closed
|
# eg. when terminal window with an active zellij session is closed
|
||||||
|
|
|
||||||
13
zellij-utils/assets/layouts/compact.yaml
Normal file
13
zellij-utils/assets/layouts/compact.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
template:
|
||||||
|
direction: Horizontal
|
||||||
|
parts:
|
||||||
|
- direction: Vertical
|
||||||
|
body: true
|
||||||
|
- direction: Vertical
|
||||||
|
borderless: true
|
||||||
|
split_size:
|
||||||
|
Fixed: 1
|
||||||
|
run:
|
||||||
|
plugin:
|
||||||
|
location: "zellij:compact-bar"
|
||||||
|
|
@ -288,6 +288,7 @@ impl LayoutFromYamlIntermediate {
|
||||||
Some("default") => Self::default_from_assets(),
|
Some("default") => Self::default_from_assets(),
|
||||||
Some("strider") => Self::strider_from_assets(),
|
Some("strider") => Self::strider_from_assets(),
|
||||||
Some("disable-status-bar") => Self::disable_status_from_assets(),
|
Some("disable-status-bar") => Self::disable_status_from_assets(),
|
||||||
|
Some("compact") => Self::compact_from_assets(),
|
||||||
None | Some(_) => Err(ConfigError::IoPath(
|
None | Some(_) => Err(ConfigError::IoPath(
|
||||||
std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"),
|
std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"),
|
||||||
path.into(),
|
path.into(),
|
||||||
|
|
@ -314,6 +315,12 @@ impl LayoutFromYamlIntermediate {
|
||||||
serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?;
|
serde_yaml::from_str(&String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?)?;
|
||||||
Ok(layout)
|
Ok(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compact_from_assets() -> LayoutFromYamlIntermediateResult {
|
||||||
|
let layout: LayoutFromYamlIntermediate =
|
||||||
|
serde_yaml::from_str(&String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)?;
|
||||||
|
Ok(layout)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LayoutFromYamlResult = Result<LayoutFromYaml, ConfigError>;
|
type LayoutFromYamlResult = Result<LayoutFromYaml, ConfigError>;
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?);
|
let plugins = PluginsConfig::get_plugins_with_default(plugins.try_into()?);
|
||||||
|
|
||||||
assert_eq!(plugins.iter().count(), 4);
|
assert_eq!(plugins.iter().count(), 5);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,12 @@ pub const NO_STATUS_LAYOUT: &[u8] = include_bytes!(concat!(
|
||||||
"assets/layouts/disable-status-bar.yaml"
|
"assets/layouts/disable-status-bar.yaml"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
pub const COMPACT_BAR_LAYOUT: &[u8] = include_bytes!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/",
|
||||||
|
"assets/layouts/compact.yaml"
|
||||||
|
));
|
||||||
|
|
||||||
pub const FISH_EXTRA_COMPLETION: &[u8] = include_bytes!(concat!(
|
pub const FISH_EXTRA_COMPLETION: &[u8] = include_bytes!(concat!(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
"/",
|
"/",
|
||||||
|
|
@ -141,6 +147,7 @@ pub fn dump_specified_layout(layout: &str) -> std::io::Result<()> {
|
||||||
match layout {
|
match layout {
|
||||||
"strider" => dump_asset(STRIDER_LAYOUT),
|
"strider" => dump_asset(STRIDER_LAYOUT),
|
||||||
"default" => dump_asset(DEFAULT_LAYOUT),
|
"default" => dump_asset(DEFAULT_LAYOUT),
|
||||||
|
"compact" => dump_asset(COMPACT_BAR_LAYOUT),
|
||||||
"disable-status" => dump_asset(NO_STATUS_LAYOUT),
|
"disable-status" => dump_asset(NO_STATUS_LAYOUT),
|
||||||
not_found => Err(std::io::Error::new(
|
not_found => Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::Other,
|
std::io::ErrorKind::Other,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue