diff options
author | denis <denismaximov98@gmail.com> | 2021-04-22 11:45:29 +0300 |
---|---|---|
committer | denis <denismaximov98@gmail.com> | 2021-04-22 11:45:29 +0300 |
commit | 8d98ca7da0bc278a832e4e7de23ff63a84deb529 (patch) | |
tree | e7fdfaa6c1ae25e7737dbb36bd1dbcb94d62ab47 /default-plugins/tab-bar | |
parent | b2139f4f3472b0cae18a106479e8a1ef92f7115e (diff) | |
parent | 80e1f2927007347d540107b2aa8de7ffac7751b5 (diff) |
wip: merge main in
Diffstat (limited to 'default-plugins/tab-bar')
-rw-r--r-- | default-plugins/tab-bar/.cargo/config.toml | 2 | ||||
-rw-r--r-- | default-plugins/tab-bar/Cargo.toml | 11 | ||||
l--------- | default-plugins/tab-bar/LICENSE.md | 1 | ||||
-rw-r--r-- | default-plugins/tab-bar/src/line.rs | 180 | ||||
-rw-r--r-- | default-plugins/tab-bar/src/main.rs | 84 | ||||
-rw-r--r-- | default-plugins/tab-bar/src/tab.rs | 67 |
6 files changed, 345 insertions, 0 deletions
diff --git a/default-plugins/tab-bar/.cargo/config.toml b/default-plugins/tab-bar/.cargo/config.toml new file mode 100644 index 000000000..6b77899cb --- /dev/null +++ b/default-plugins/tab-bar/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasi" diff --git a/default-plugins/tab-bar/Cargo.toml b/default-plugins/tab-bar/Cargo.toml new file mode 100644 index 000000000..59b95b4bf --- /dev/null +++ b/default-plugins/tab-bar/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "tab-bar" +version = "0.1.0" +authors = ["Jonah Caplan <jonahcaplan@gmail.com>"] +edition = "2018" +license = "MIT" + +[dependencies] +colored = "2" +ansi_term = "0.12" +zellij-tile = { path = "../../zellij-tile" } diff --git a/default-plugins/tab-bar/LICENSE.md b/default-plugins/tab-bar/LICENSE.md new file mode 120000 index 000000000..f0608a63a --- /dev/null +++ b/default-plugins/tab-bar/LICENSE.md @@ -0,0 +1 @@ +../../LICENSE.md
\ No newline at end of file diff --git a/default-plugins/tab-bar/src/line.rs b/default-plugins/tab-bar/src/line.rs new file mode 100644 index 000000000..1d1c33ecd --- /dev/null +++ b/default-plugins/tab-bar/src/line.rs @@ -0,0 +1,180 @@ +use crate::colors::{BLACK, GRAY, ORANGE, WHITE}; +use ansi_term::{ANSIStrings, Style}; + +use crate::{LinePart, ARROW_SEPARATOR}; + +fn get_current_title_len(current_title: &[LinePart]) -> usize { + current_title + .iter() + .fold(0, |acc, title_part| acc + title_part.len) +} + +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, +) { + let mut take_next_tab_from_tabs_after = true; + loop { + if tabs_before_active.is_empty() && tabs_after_active.is_empty() { + break; + } + let current_title_len = get_current_title_len(&tabs_to_render); + if current_title_len >= cols { + break; + } + let should_take_next_tab = take_next_tab_from_tabs_after; + let can_take_next_tab = !tabs_after_active.is_empty() + && tabs_after_active.get(0).unwrap().len + current_title_len <= cols; + let can_take_previous_tab = !tabs_before_active.is_empty() + && tabs_before_active.last().unwrap().len + current_title_len <= cols; + if should_take_next_tab && can_take_next_tab { + let next_tab = tabs_after_active.remove(0); + tabs_to_render.push(next_tab); + take_next_tab_from_tabs_after = false; + } else if can_take_previous_tab { + let previous_tab = tabs_before_active.pop().unwrap(); + tabs_to_render.insert(0, previous_tab); + take_next_tab_from_tabs_after = true; + } else if can_take_next_tab { + let next_tab = tabs_after_active.remove(0); + tabs_to_render.push(next_tab); + take_next_tab_from_tabs_after = false; + } else { + break; + } + } +} + +fn left_more_message(tab_count_to_the_left: usize) -> LinePart { + if tab_count_to_the_left == 0 { + return LinePart { + part: String::new(), + len: 0, + }; + } + let more_text = if tab_count_to_the_left < 10000 { + format!(" ← +{} ", tab_count_to_the_left) + } else { + " ← +many ".to_string() + }; + // 238 + let more_text_len = more_text.chars().count() + 2; // 2 for the arrows + let left_separator = Style::new().fg(GRAY).on(ORANGE).paint(ARROW_SEPARATOR); + let more_styled_text = Style::new().fg(BLACK).on(ORANGE).bold().paint(more_text); + let right_separator = Style::new().fg(ORANGE).on(GRAY).paint(ARROW_SEPARATOR); + let more_styled_text = format!( + "{}", + ANSIStrings(&[left_separator, more_styled_text, right_separator,]) + ); + LinePart { + part: more_styled_text, + len: more_text_len, + } +} + +fn right_more_message(tab_count_to_the_right: usize) -> LinePart { + if tab_count_to_the_right == 0 { + return LinePart { + part: String::new(), + len: 0, + }; + }; + let more_text = if tab_count_to_the_right < 10000 { + format!(" +{} → ", tab_count_to_the_right) + } else { + " +many → ".to_string() + }; + let more_text_len = more_text.chars().count() + 1; // 2 for the arrow + let left_separator = Style::new().fg(GRAY).on(ORANGE).paint(ARROW_SEPARATOR); + let more_styled_text = Style::new().fg(BLACK).on(ORANGE).bold().paint(more_text); + let right_separator = Style::new().fg(ORANGE).on(GRAY).paint(ARROW_SEPARATOR); + let more_styled_text = format!( + "{}", + ANSIStrings(&[left_separator, more_styled_text, right_separator,]) + ); + LinePart { + part: more_styled_text, + len: more_text_len, + } +} + +fn add_previous_tabs_msg( + tabs_before_active: &mut Vec<LinePart>, + tabs_to_render: &mut Vec<LinePart>, + title_bar: &mut Vec<LinePart>, + cols: usize, +) { + while get_current_title_len(&tabs_to_render) + left_more_message(tabs_before_active.len()).len + >= cols + { + tabs_before_active.push(tabs_to_render.remove(0)); + } + let left_more_message = left_more_message(tabs_before_active.len()); + title_bar.push(left_more_message); +} + +fn add_next_tabs_msg( + tabs_after_active: &mut Vec<LinePart>, + title_bar: &mut Vec<LinePart>, + cols: usize, +) { + while get_current_title_len(&title_bar) + right_more_message(tabs_after_active.len()).len + >= cols + { + tabs_after_active.insert(0, title_bar.pop().unwrap()); + } + let right_more_message = right_more_message(tabs_after_active.len()); + title_bar.push(right_more_message); +} + +fn tab_line_prefix() -> LinePart { + let prefix_text = " Zellij ".to_string(); + let prefix_text_len = prefix_text.chars().count(); + let prefix_styled_text = Style::new().fg(WHITE).on(GRAY).bold().paint(prefix_text); + LinePart { + part: format!("{}", prefix_styled_text), + len: prefix_text_len, + } +} + +pub fn tab_line( + mut all_tabs: Vec<LinePart>, + active_tab_index: usize, + cols: usize, +) -> Vec<LinePart> { + let mut tabs_to_render: Vec<LinePart> = vec![]; + 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() + }; + tabs_to_render.push(active_tab); + + let prefix = tab_line_prefix(); + populate_tabs_in_tab_line( + &mut tabs_before_active, + &mut tabs_after_active, + &mut tabs_to_render, + cols - prefix.len, + ); + + let mut tab_line: Vec<LinePart> = vec![]; + if !tabs_before_active.is_empty() { + add_previous_tabs_msg( + &mut tabs_before_active, + &mut tabs_to_render, + &mut tab_line, + cols - prefix.len, + ); + } + tab_line.append(&mut tabs_to_render); + if !tabs_after_active.is_empty() { + add_next_tabs_msg(&mut tabs_after_active, &mut tab_line, cols - prefix.len); + } + tab_line.insert(0, prefix); + tab_line +} diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs new file mode 100644 index 000000000..e4a84d987 --- /dev/null +++ b/default-plugins/tab-bar/src/main.rs @@ -0,0 +1,84 @@ +mod line; +mod tab; + +use zellij_tile::prelude::*; + +use crate::line::tab_line; +use crate::tab::tab_style; + +#[derive(Debug)] +pub struct LinePart { + part: String, + len: usize, +} + +#[derive(Default)] +struct State { + tabs: Vec<TabInfo>, + mode_info: ModeInfo, +} + +static ARROW_SEPARATOR: &str = ""; + +pub mod colors { + use ansi_term::Colour::{self, Fixed}; + pub const WHITE: Colour = Fixed(255); + pub const BLACK: Colour = Fixed(16); + pub const GREEN: Colour = Fixed(154); + pub const ORANGE: Colour = Fixed(166); + pub const GRAY: Colour = Fixed(238); + pub const BRIGHT_GRAY: Colour = Fixed(245); + pub const RED: Colour = Fixed(88); +} + +register_plugin!(State); + +impl ZellijPlugin for State { + fn load(&mut self) { + set_selectable(false); + set_invisible_borders(true); + set_max_height(1); + subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]); + } + + fn update(&mut self, event: Event) { + match event { + Event::ModeUpdate(mode_info) => self.mode_info.mode = mode_info.mode, + Event::TabUpdate(tabs) => self.tabs = tabs, + _ => unimplemented!(), // FIXME: This should be unreachable, but this could be cleaner + } + } + + 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 self.tabs.iter_mut() { + 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.position, self.mode_info.palette); + all_tabs.push(tab); + } + let tab_line = tab_line(all_tabs, active_tab_index, cols); + let mut s = String::new(); + for bar_part in tab_line { + s = format!("{}{}", s, bar_part.part); + } + println!( + "{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", + s, + self.mode_info.palette.fg.0, + self.mode_info.palette.fg.1, + self.mode_info.palette.fg.2 + ); + } +} diff --git a/default-plugins/tab-bar/src/tab.rs b/default-plugins/tab-bar/src/tab.rs new file mode 100644 index 000000000..375d8c462 --- /dev/null +++ b/default-plugins/tab-bar/src/tab.rs @@ -0,0 +1,67 @@ +use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN}; +use crate::{LinePart, ARROW_SEPARATOR}; +use ansi_term::{ANSIStrings, Color::RGB, Style}; +use zellij_tile::data::Palette; + +pub fn active_tab(text: String, palette: Palette) -> LinePart { + let left_separator = Style::new() + .fg(RGB(palette.black.0, palette.black.1, palette.black.2)) + .on(RGB(palette.green.0, palette.green.1, palette.green.2)) + .paint(ARROW_SEPARATOR); + let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the text padding + let tab_styled_text = Style::new() + .fg(RGB(palette.black.0, palette.black.1, palette.black.2)) + .on(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(format!(" {} ", text)); + let right_separator = Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) + .paint(ARROW_SEPARATOR); + let tab_styled_text = format!( + "{}", + ANSIStrings(&[left_separator, tab_styled_text, right_separator,]) + ); + LinePart { + part: tab_styled_text, + len: tab_text_len, + } +} + +pub fn non_active_tab(text: String, palette: Palette) -> LinePart { + let left_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) + .paint(ARROW_SEPARATOR); + let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the padding + let tab_styled_text = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) + .bold() + .paint(format!(" {} ", text)); + let right_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .on(RGB(palette.bg.0, palette.bg.1, palette.bg.2)) + .paint(ARROW_SEPARATOR); + let tab_styled_text = format!( + "{}", + ANSIStrings(&[left_separator, tab_styled_text, right_separator,]) + ); + LinePart { + part: tab_styled_text, + len: tab_text_len, + } +} + +pub fn tab_style(text: String, is_active_tab: bool, position: usize, palette: Palette) -> LinePart { + let tab_text = if text.is_empty() { + format!("Tab #{}", position + 1) + } else { + text + }; + if is_active_tab { + active_tab(tab_text, palette) + } else { + non_active_tab(tab_text, palette) + } +} |