Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add(plugin): compact-bar & compact layout #1450

Merged
merged 14 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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