Skip to content

Commit

Permalink
add(plugin): compact-bar & compact layout (#1450)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
a-kenji authored Jun 3, 2022
1 parent ad9ba8a commit d62e6fb
Show file tree
Hide file tree
Showing 18 changed files with 779 additions and 199 deletions.
460 changes: 262 additions & 198 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ members = [
"zellij-utils",
"zellij-tile",
"zellij-tile-utils",
"default-plugins/compact-bar",
"default-plugins/status-bar",
"default-plugins/strider",
"default-plugins/tab-bar",
Expand Down
2 changes: 2 additions & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ end

[tasks.build-plugins-release]
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = [
"default-plugins/compact-bar",
"default-plugins/status-bar",
"default-plugins/strider",
"default-plugins/tab-bar",
Expand All @@ -117,6 +118,7 @@ run_task = { name = "build-release", fork = true }

[tasks.build-plugins]
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = [
"default-plugins/compact-bar",
"default-plugins/status-bar",
"default-plugins/strider",
"default-plugins/tab-bar",
Expand Down
Binary file added assets/plugins/compact-bar.wasm
Binary file not shown.
2 changes: 2 additions & 0 deletions default-plugins/compact-bar/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = "wasm32-wasi"
13 changes: 13 additions & 0 deletions default-plugins/compact-bar/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions default-plugins/compact-bar/LICENSE.md
243 changes: 243 additions & 0 deletions default-plugins/compact-bar/src/line.rs
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit d62e6fb

Please sign in to comment.