diff --git a/Cargo.lock b/Cargo.lock index 55cfcf01..cf2d94fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,6 +310,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "colors-transform" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -1106,6 +1112,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "polling" version = "2.0.3" @@ -1471,6 +1483,7 @@ dependencies = [ "ansi_term 0.12.1", "colored", "zellij-tile", + "zellij-tile-extra", ] [[package]] @@ -1563,6 +1576,7 @@ dependencies = [ "ansi_term 0.12.1", "colored", "zellij-tile", + "zellij-tile-extra", ] [[package]] @@ -2173,6 +2187,27 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" +[[package]] +name = "x11" +version = "2.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "xrdb" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2dd91a21c92e87678e22f95956a03bfd314ce3232f39dbedd49dddb50f0c6d" +dependencies = [ + "libc", + "scopeguard", + "x11", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -2190,6 +2225,7 @@ dependencies = [ "async-std", "backtrace", "bincode", + "colors-transform", "directories-next", "futures", "insta", @@ -2213,7 +2249,9 @@ dependencies = [ "vte 0.8.0", "wasmer", "wasmer-wasi", + "xrdb", "zellij-tile", + "zellij-tile-extra", ] [[package]] @@ -2225,3 +2263,10 @@ dependencies = [ "strum", "strum_macros", ] + +[[package]] +name = "zellij-tile-extra" +version = "1.0.0" +dependencies = [ + "ansi_term 0.12.1", +] diff --git a/Cargo.toml b/Cargo.toml index 743379b2..a1f260cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,10 @@ lazy_static = "1.4.0" wasmer = "1.0.0" wasmer-wasi = "1.0.0" interprocess = "1.0.1" +xrdb = "0.1.1" +colors-transform = "0.2.5" zellij-tile = { path = "zellij-tile/", version = "1.1.0" } +zellij-tile-extra = { path = "zellij-tile-extra/", version="1.0.0" } [dependencies.async-std] version = "1.3.0" @@ -52,6 +55,7 @@ structopt = "0.3" [workspace] members = [ "zellij-tile", + "zellij-tile-extra", "default-plugins/status-bar", "default-plugins/strider", "default-plugins/tab-bar", diff --git a/Makefile.toml b/Makefile.toml index 628899f9..1d11f0d1 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -108,7 +108,7 @@ args = ["install", "cross"] [tasks.publish] clear = true workspace = false -dependencies = ["build-plugins-release", "wasm-opt-plugins", "build-release", "publish-zellij-tile"] +dependencies = ["build-plugins-release", "wasm-opt-plugins", "build-release", "publish-zellij-tile", "publish-zellij-tile-extra"] run_task = "publish-zellij" [tasks.publish-zellij-tile] @@ -117,6 +117,12 @@ cwd = "zellij-tile" command = "cargo" args = ["publish"] +[tasks.publish-zellij-tile-extra] +ignore_errors = true +cwd = "zellij-tile-extra" +command = "cargo" +args = ["publish"] + [tasks.publish-zellij] command = "cargo" args = ["publish"] \ No newline at end of file diff --git a/default-plugins/status-bar/Cargo.toml b/default-plugins/status-bar/Cargo.toml index c94d8feb..8c6cc85d 100644 --- a/default-plugins/status-bar/Cargo.toml +++ b/default-plugins/status-bar/Cargo.toml @@ -9,3 +9,4 @@ license = "MIT" colored = "2" ansi_term = "0.12" zellij-tile = { path = "../../zellij-tile" } +zellij-tile-extra = { path = "../../zellij-tile-extra" } \ No newline at end of file diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index ea48cb1b..77cb4dfc 100644 --- a/default-plugins/status-bar/src/first_line.rs +++ b/default-plugins/status-bar/src/first_line.rs @@ -1,8 +1,8 @@ -use ansi_term::{ANSIStrings, Style}; +use ansi_term::ANSIStrings; use zellij_tile::prelude::*; -use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN, RED, WHITE}; -use crate::{LinePart, ARROW_SEPARATOR}; +use crate::color_elements; +use crate::{ColoredElements, LinePart, ARROW_SEPARATOR}; struct CtrlKeyShortcut { mode: CtrlKeyMode, @@ -63,32 +63,13 @@ impl CtrlKeyShortcut { } } -fn unselected_mode_shortcut(letter: char, text: &str) -> LinePart { - let prefix_separator = Style::new().fg(GRAY).on(BRIGHT_GRAY).paint(ARROW_SEPARATOR); - let char_left_separator = Style::new() - .bold() - .fg(BLACK) - .on(BRIGHT_GRAY) - .bold() - .paint(" <"); - let char_shortcut = Style::new() - .bold() - .fg(RED) - .on(BRIGHT_GRAY) - .bold() - .paint(letter.to_string()); - let char_right_separator = Style::new() - .bold() - .fg(BLACK) - .on(BRIGHT_GRAY) - .bold() - .paint(">"); - let styled_text = Style::new() - .fg(BLACK) - .on(BRIGHT_GRAY) - .bold() - .paint(format!("{} ", text)); - let suffix_separator = Style::new().fg(BRIGHT_GRAY).on(GRAY).paint(ARROW_SEPARATOR); +fn unselected_mode_shortcut(letter: char, text: &str, palette: ColoredElements) -> LinePart { + let prefix_separator = palette.unselected_prefix_separator.paint(ARROW_SEPARATOR); + let char_left_separator = palette.unselected_char_left_separator.paint(" <"); + let char_shortcut = palette.unselected_char_shortcut.paint(letter.to_string()); + let char_right_separator = palette.unselected_char_right_separator.paint(">"); + let styled_text = palette.unselected_styled_text.paint(format!("{} ", text)); + let suffix_separator = palette.unselected_suffix_separator.paint(ARROW_SEPARATOR); LinePart { part: ANSIStrings(&[ prefix_separator, @@ -103,22 +84,13 @@ fn unselected_mode_shortcut(letter: char, text: &str) -> LinePart { } } -fn selected_mode_shortcut(letter: char, text: &str) -> LinePart { - let prefix_separator = Style::new().fg(GRAY).on(GREEN).paint(ARROW_SEPARATOR); - let char_left_separator = Style::new().bold().fg(BLACK).on(GREEN).bold().paint(" <"); - let char_shortcut = Style::new() - .bold() - .fg(RED) - .on(GREEN) - .bold() - .paint(letter.to_string()); - let char_right_separator = Style::new().bold().fg(BLACK).on(GREEN).bold().paint(">"); - let styled_text = Style::new() - .fg(BLACK) - .on(GREEN) - .bold() - .paint(format!("{} ", text)); - let suffix_separator = Style::new().fg(GREEN).on(GRAY).paint(ARROW_SEPARATOR); +fn selected_mode_shortcut(letter: char, text: &str, palette: ColoredElements) -> LinePart { + let prefix_separator = palette.selected_prefix_separator.paint(ARROW_SEPARATOR); + let char_left_separator = palette.selected_char_left_separator.paint(" <".to_string()); + let char_shortcut = palette.selected_char_shortcut.paint(format!("{}", letter)); + let char_right_separator = palette.selected_char_right_separator.paint(">".to_string()); + let styled_text = palette.selected_styled_text.paint(format!("{} ", text)); + let suffix_separator = palette.selected_suffix_separator.paint(ARROW_SEPARATOR); LinePart { part: ANSIStrings(&[ prefix_separator, @@ -133,71 +105,69 @@ fn selected_mode_shortcut(letter: char, text: &str) -> LinePart { } } -fn disabled_mode_shortcut(text: &str) -> LinePart { - let prefix_separator = Style::new().fg(GRAY).on(BRIGHT_GRAY).paint(ARROW_SEPARATOR); - let styled_text = Style::new() - .fg(GRAY) - .on(BRIGHT_GRAY) - .dimmed() - .paint(format!("{} ", text)); - let suffix_separator = Style::new().fg(BRIGHT_GRAY).on(GRAY).paint(ARROW_SEPARATOR); +fn disabled_mode_shortcut(text: &str, palette: ColoredElements) -> LinePart { + let prefix_separator = palette.disabled_prefix_separator.paint(ARROW_SEPARATOR); + let styled_text = palette.disabled_styled_text.paint(format!("{} ", text)); + let suffix_separator = palette.disabled_suffix_separator.paint(ARROW_SEPARATOR); LinePart { part: format!("{}{}{}", prefix_separator, styled_text, suffix_separator), len: text.chars().count() + 2 + 1, // 2 for the arrows, 1 for the padding in the end } } -fn selected_mode_shortcut_single_letter(letter: char) -> LinePart { +fn selected_mode_shortcut_single_letter(letter: char, palette: ColoredElements) -> LinePart { let char_shortcut_text = format!(" {} ", letter); let len = char_shortcut_text.chars().count() + 4; // 2 for the arrows, 2 for the padding - let prefix_separator = Style::new().fg(GRAY).on(GREEN).paint(ARROW_SEPARATOR); - let char_shortcut = Style::new() - .bold() - .fg(RED) - .on(GREEN) - .bold() + let prefix_separator = palette + .selected_single_letter_prefix_separator + .paint(ARROW_SEPARATOR); + let char_shortcut = palette + .selected_single_letter_char_shortcut .paint(char_shortcut_text); - let suffix_separator = Style::new().fg(GREEN).on(GRAY).paint(ARROW_SEPARATOR); + let suffix_separator = palette + .selected_single_letter_suffix_separator + .paint(ARROW_SEPARATOR); LinePart { part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(), len, } } -fn unselected_mode_shortcut_single_letter(letter: char) -> LinePart { +fn unselected_mode_shortcut_single_letter(letter: char, palette: ColoredElements) -> LinePart { let char_shortcut_text = format!(" {} ", letter); let len = char_shortcut_text.chars().count() + 4; // 2 for the arrows, 2 for the padding - let prefix_separator = Style::new().fg(GRAY).on(BRIGHT_GRAY).paint(ARROW_SEPARATOR); - let char_shortcut = Style::new() - .bold() - .fg(RED) - .on(BRIGHT_GRAY) - .bold() + let prefix_separator = palette + .unselected_single_letter_prefix_separator + .paint(ARROW_SEPARATOR); + let char_shortcut = palette + .unselected_single_letter_char_shortcut .paint(char_shortcut_text); - let suffix_separator = Style::new().fg(BRIGHT_GRAY).on(GRAY).paint(ARROW_SEPARATOR); + let suffix_separator = palette + .unselected_single_letter_suffix_separator + .paint(ARROW_SEPARATOR); LinePart { part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(), len, } } -fn full_ctrl_key(key: &CtrlKeyShortcut) -> LinePart { +fn full_ctrl_key(key: &CtrlKeyShortcut, palette: ColoredElements) -> LinePart { let full_text = key.full_text(); let letter_shortcut = key.letter_shortcut(); match key.mode { CtrlKeyMode::Unselected => { - unselected_mode_shortcut(letter_shortcut, &format!(" {}", full_text)) + unselected_mode_shortcut(letter_shortcut, &format!(" {}", full_text), palette) } CtrlKeyMode::Selected => { - selected_mode_shortcut(letter_shortcut, &format!(" {}", full_text)) + selected_mode_shortcut(letter_shortcut, &format!(" {}", full_text), palette) } CtrlKeyMode::Disabled => { - disabled_mode_shortcut(&format!(" <{}> {}", letter_shortcut, full_text)) + disabled_mode_shortcut(&format!(" <{}> {}", letter_shortcut, full_text), palette) } } } -fn shortened_ctrl_key(key: &CtrlKeyShortcut) -> LinePart { +fn shortened_ctrl_key(key: &CtrlKeyShortcut, palette: ColoredElements) -> LinePart { let shortened_text = key.shortened_text(); let letter_shortcut = key.letter_shortcut(); let shortened_text = match key.action { @@ -205,27 +175,34 @@ fn shortened_ctrl_key(key: &CtrlKeyShortcut) -> LinePart { _ => shortened_text, }; match key.mode { - CtrlKeyMode::Unselected => unselected_mode_shortcut(letter_shortcut, &shortened_text), - CtrlKeyMode::Selected => selected_mode_shortcut(letter_shortcut, &shortened_text), - CtrlKeyMode::Disabled => { - disabled_mode_shortcut(&format!(" <{}>{}", letter_shortcut, shortened_text)) + CtrlKeyMode::Unselected => { + unselected_mode_shortcut(letter_shortcut, &shortened_text, palette) } + CtrlKeyMode::Selected => selected_mode_shortcut(letter_shortcut, &shortened_text, palette), + CtrlKeyMode::Disabled => disabled_mode_shortcut( + &format!(" <{}>{}", letter_shortcut, shortened_text), + palette, + ), + CtrlKeyMode::Disabled => disabled_mode_shortcut( + &format!(" <{}>{}", letter_shortcut, shortened_text), + palette, + ), } } -fn single_letter_ctrl_key(key: &CtrlKeyShortcut) -> LinePart { +fn single_letter_ctrl_key(key: &CtrlKeyShortcut, palette: ColoredElements) -> LinePart { let letter_shortcut = key.letter_shortcut(); match key.mode { - CtrlKeyMode::Unselected => unselected_mode_shortcut_single_letter(letter_shortcut), - CtrlKeyMode::Selected => selected_mode_shortcut_single_letter(letter_shortcut), - CtrlKeyMode::Disabled => disabled_mode_shortcut(&format!(" {}", letter_shortcut)), + CtrlKeyMode::Unselected => unselected_mode_shortcut_single_letter(letter_shortcut, palette), + CtrlKeyMode::Selected => selected_mode_shortcut_single_letter(letter_shortcut, palette), + CtrlKeyMode::Disabled => disabled_mode_shortcut(&format!(" {}", letter_shortcut), palette), } } -fn key_indicators(max_len: usize, keys: &[CtrlKeyShortcut]) -> LinePart { +fn key_indicators(max_len: usize, keys: &[CtrlKeyShortcut], palette: ColoredElements) -> LinePart { let mut line_part = LinePart::default(); for ctrl_key in keys { - let key = full_ctrl_key(ctrl_key); + let key = full_ctrl_key(ctrl_key, palette); line_part.part = format!("{}{}", line_part.part, key.part); line_part.len += key.len; } @@ -234,7 +211,7 @@ fn key_indicators(max_len: usize, keys: &[CtrlKeyShortcut]) -> LinePart { } line_part = LinePart::default(); for ctrl_key in keys { - let key = shortened_ctrl_key(ctrl_key); + let key = shortened_ctrl_key(ctrl_key, palette); line_part.part = format!("{}{}", line_part.part, key.part); line_part.len += key.len; } @@ -243,7 +220,7 @@ fn key_indicators(max_len: usize, keys: &[CtrlKeyShortcut]) -> LinePart { } line_part = LinePart::default(); for ctrl_key in keys { - let key = single_letter_ctrl_key(ctrl_key); + let key = single_letter_ctrl_key(ctrl_key, palette); line_part.part = format!("{}{}", line_part.part, key.part); line_part.len += key.len; } @@ -254,16 +231,18 @@ fn key_indicators(max_len: usize, keys: &[CtrlKeyShortcut]) -> LinePart { line_part } -pub fn superkey() -> LinePart { +pub fn superkey(palette: ColoredElements) -> LinePart { let prefix_text = " Ctrl + "; - let prefix = Style::new().fg(WHITE).on(GRAY).bold().paint(prefix_text); + let prefix = palette.superkey_prefix.paint(prefix_text); + let suffix_separator = palette.superkey_suffix_separator.paint(ARROW_SEPARATOR); LinePart { - part: prefix.to_string(), + part: ANSIStrings(&[prefix, suffix_separator]).to_string(), len: prefix_text.chars().count(), } } pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { + let colored_elements = color_elements(help.palette); match &help.mode { InputMode::Locked => key_indicators( max_len, @@ -275,6 +254,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Scroll), CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Quit), ], + colored_elements, ), InputMode::Resize => key_indicators( max_len, @@ -286,6 +266,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit), ], + colored_elements, ), InputMode::Pane => key_indicators( max_len, @@ -297,6 +278,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit), ], + colored_elements, ), InputMode::Tab | InputMode::RenameTab => key_indicators( max_len, @@ -308,6 +290,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit), ], + colored_elements, ), InputMode::Scroll => key_indicators( max_len, @@ -319,6 +302,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Scroll), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit), ], + colored_elements, ), InputMode::Normal => key_indicators( max_len, @@ -330,6 +314,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize) -> LinePart { CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll), CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit), ], + colored_elements, ), } } diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 5529eb31..93898cea 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -1,23 +1,15 @@ mod first_line; mod second_line; +use ansi_term::Style; + use std::fmt::{Display, Error, Formatter}; use zellij_tile::prelude::*; +use zellij_tile_extra::*; use first_line::{ctrl_keys, superkey}; use second_line::keybinds; -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); -} - // for more of these, copy paste from: https://en.wikipedia.org/wiki/Box-drawing_character static ARROW_SEPARATOR: &str = ""; static MORE_MSG: &str = " ... "; @@ -41,6 +33,98 @@ impl Display for LinePart { } } +#[derive(Clone, Copy)] +pub struct ColoredElements { + // selected mode + pub selected_prefix_separator: Style, + pub selected_char_left_separator: Style, + pub selected_char_shortcut: Style, + pub selected_char_right_separator: Style, + pub selected_styled_text: Style, + pub selected_suffix_separator: Style, + // unselected mode + pub unselected_prefix_separator: Style, + pub unselected_char_left_separator: Style, + pub unselected_char_shortcut: Style, + pub unselected_char_right_separator: Style, + pub unselected_styled_text: Style, + pub unselected_suffix_separator: Style, + // disabled mode + pub disabled_prefix_separator: Style, + pub disabled_styled_text: Style, + pub disabled_suffix_separator: Style, + // selected single letter + pub selected_single_letter_prefix_separator: Style, + pub selected_single_letter_char_shortcut: Style, + pub selected_single_letter_suffix_separator: Style, + // unselected single letter + pub unselected_single_letter_prefix_separator: Style, + pub unselected_single_letter_char_shortcut: Style, + pub unselected_single_letter_suffix_separator: Style, + // superkey + pub superkey_prefix: Style, + pub superkey_suffix_separator: Style, +} + +// I really hate this, but I can't come up with a good solution for this, +// we need different colors from palette for the default theme +// plus here we can add new sources in the future, like Theme +// that can be defined in the config perhaps +fn color_elements(palette: Palette) -> ColoredElements { + match palette.source { + PaletteSource::Default => ColoredElements { + selected_prefix_separator: style!(palette.bg, palette.green), + selected_char_left_separator: style!(palette.black, palette.green).bold(), + selected_char_shortcut: style!(palette.red, palette.green).bold(), + selected_char_right_separator: style!(palette.black, palette.green).bold(), + selected_styled_text: style!(palette.black, palette.green).bold(), + selected_suffix_separator: style!(palette.green, palette.bg).bold(), + unselected_prefix_separator: style!(palette.bg, palette.fg), + unselected_char_left_separator: style!(palette.bg, palette.fg).bold(), + unselected_char_shortcut: style!(palette.red, palette.fg).bold(), + unselected_char_right_separator: style!(palette.bg, palette.fg).bold(), + unselected_styled_text: style!(palette.black, palette.fg).bold(), + unselected_suffix_separator: style!(palette.fg, palette.bg), + disabled_prefix_separator: style!(palette.bg, palette.fg), + disabled_styled_text: style!(palette.bg, palette.fg).dimmed(), + disabled_suffix_separator: style!(palette.fg, palette.bg), + selected_single_letter_prefix_separator: style!(palette.fg, palette.green), + selected_single_letter_char_shortcut: style!(palette.red, palette.green).bold(), + selected_single_letter_suffix_separator: style!(palette.green, palette.fg), + unselected_single_letter_prefix_separator: style!(palette.fg, palette.bg), + unselected_single_letter_char_shortcut: style!(palette.red, palette.fg).bold(), + unselected_single_letter_suffix_separator: style!(palette.fg, palette.bg), + superkey_prefix: style!(palette.white, palette.bg).bold(), + superkey_suffix_separator: style!(palette.bg, palette.bg), + }, + PaletteSource::Xresources => ColoredElements { + selected_prefix_separator: style!(palette.bg, palette.green), + selected_char_left_separator: style!(palette.fg, palette.green).bold(), + selected_char_shortcut: style!(palette.red, palette.green).bold(), + selected_char_right_separator: style!(palette.fg, palette.green).bold(), + selected_styled_text: style!(palette.bg, palette.green).bold(), + selected_suffix_separator: style!(palette.green, palette.bg).bold(), + unselected_prefix_separator: style!(palette.bg, palette.fg), + unselected_char_left_separator: style!(palette.bg, palette.fg).bold(), + unselected_char_shortcut: style!(palette.red, palette.fg).bold(), + unselected_char_right_separator: style!(palette.bg, palette.fg).bold(), + unselected_styled_text: style!(palette.bg, palette.fg).bold(), + unselected_suffix_separator: style!(palette.fg, palette.bg), + disabled_prefix_separator: style!(palette.bg, palette.fg), + disabled_styled_text: style!(palette.bg, palette.fg).dimmed(), + disabled_suffix_separator: style!(palette.fg, palette.bg), + selected_single_letter_prefix_separator: style!(palette.fg, palette.green), + selected_single_letter_char_shortcut: style!(palette.red, palette.green).bold(), + selected_single_letter_suffix_separator: style!(palette.green, palette.fg), + unselected_single_letter_prefix_separator: style!(palette.fg, palette.bg), + unselected_single_letter_char_shortcut: style!(palette.red, palette.fg).bold(), + unselected_single_letter_suffix_separator: style!(palette.fg, palette.bg), + superkey_prefix: style!(palette.bg, palette.fg).bold(), + superkey_suffix_separator: style!(palette.fg, palette.bg), + }, + } +} + impl ZellijPlugin for State { fn load(&mut self) { set_selectable(false); @@ -56,7 +140,8 @@ impl ZellijPlugin for State { } fn render(&mut self, _rows: usize, cols: usize) { - let superkey = superkey(); + let colored_elements = color_elements(self.mode_info.palette); + let superkey = superkey(colored_elements); let ctrl_keys = ctrl_keys(&self.mode_info, cols - superkey.len); let first_line = format!("{}{}", superkey, ctrl_keys); @@ -64,7 +149,13 @@ impl ZellijPlugin for State { // [48;5;238m is gray background, [0K is so that it fills the rest of the line // [m is background reset, [0K is so that it clears the rest of the line - println!("{}\u{1b}[48;5;238m\u{1b}[0K", first_line); + println!( + "{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", + first_line, + self.mode_info.palette.bg.0, + self.mode_info.palette.bg.1, + self.mode_info.palette.bg.2 + ); println!("\u{1b}[m{}\u{1b}[0K", second_line); } } diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs index 1046314e..6982c410 100644 --- a/default-plugins/status-bar/src/second_line.rs +++ b/default-plugins/status-bar/src/second_line.rs @@ -1,19 +1,35 @@ // use colored::*; -use ansi_term::{ANSIStrings, Style}; +use ansi_term::{ANSIStrings, Color::RGB, Style}; use zellij_tile::prelude::*; -use crate::colors::{GREEN, ORANGE, WHITE}; use crate::{LinePart, MORE_MSG}; -fn full_length_shortcut(is_first_shortcut: bool, letter: &str, description: &str) -> LinePart { +fn full_length_shortcut( + is_first_shortcut: bool, + letter: &str, + description: &str, + palette: Palette, +) -> LinePart { let separator = if is_first_shortcut { " " } else { " / " }; - let separator = Style::new().fg(WHITE).paint(separator); + let separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint(separator); let shortcut_len = letter.chars().count() + 3; // 2 for <>'s around shortcut, 1 for the space - let shortcut_left_separator = Style::new().fg(WHITE).paint("<"); - let shortcut = Style::new().fg(GREEN).bold().paint(letter); - let shortcut_right_separator = Style::new().fg(WHITE).paint("> "); + let shortcut_left_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint("<"); + let shortcut = Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(letter); + let shortcut_right_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint("> "); let description_len = description.chars().count(); - let description = Style::new().fg(WHITE).bold().paint(description); + let description = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .bold() + .paint(description); let len = shortcut_len + description_len + separator.chars().count(); LinePart { part: format!( @@ -30,16 +46,33 @@ fn full_length_shortcut(is_first_shortcut: bool, letter: &str, description: &str } } -fn first_word_shortcut(is_first_shortcut: bool, letter: &str, description: &str) -> LinePart { +fn first_word_shortcut( + is_first_shortcut: bool, + letter: &str, + description: &str, + palette: Palette, +) -> LinePart { let separator = if is_first_shortcut { " " } else { " / " }; - let separator = Style::new().fg(WHITE).paint(separator); + let separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint(separator); let shortcut_len = letter.chars().count() + 3; // 2 for <>'s around shortcut, 1 for the space - let shortcut_left_separator = Style::new().fg(WHITE).paint("<"); - let shortcut = Style::new().fg(GREEN).bold().paint(letter); - let shortcut_right_separator = Style::new().fg(WHITE).paint("> "); + let shortcut_left_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint("<"); + let shortcut = Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(letter); + let shortcut_right_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint("> "); let description_first_word = description.split(' ').next().unwrap_or(""); let description_first_word_length = description_first_word.chars().count(); - let description_first_word = Style::new().fg(WHITE).bold().paint(description_first_word); + let description_first_word = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .bold() + .paint(description_first_word); let len = shortcut_len + description_first_word_length + separator.chars().count(); LinePart { part: format!( @@ -55,7 +88,7 @@ fn first_word_shortcut(is_first_shortcut: bool, letter: &str, description: &str) len, } } -fn quicknav_full() -> LinePart { +fn quicknav_full(palette: Palette) -> LinePart { let text_first_part = " Tip: "; let alt = "Alt"; let text_second_part = " + "; @@ -82,22 +115,37 @@ fn quicknav_full() -> LinePart { part: format!( "{}{}{}{}{}{}{}{}{}{}{}", text_first_part, - Style::new().fg(ORANGE).bold().paint(alt), + Style::new() + .fg(RGB(palette.orange.0, palette.orange.1, palette.orange.2)) + .bold() + .paint(alt), text_second_part, - Style::new().fg(GREEN).bold().paint(new_pane_shortcut), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(new_pane_shortcut), text_third_part, - Style::new().fg(ORANGE).bold().paint(second_alt), + Style::new() + .fg(RGB(palette.orange.0, palette.orange.1, palette.orange.2)) + .bold() + .paint(second_alt), text_fourth_part, - Style::new().fg(GREEN).bold().paint(brackets_navigation), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(brackets_navigation), text_fifth_part, - Style::new().fg(GREEN).bold().paint(hjkl_navigation), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(hjkl_navigation), text_sixths_part, ), len, } } -fn quicknav_medium() -> LinePart { +fn quicknav_medium(palette: Palette) -> LinePart { let text_first_part = " Tip: "; let alt = "Alt"; let text_second_part = " + "; @@ -124,22 +172,37 @@ fn quicknav_medium() -> LinePart { part: format!( "{}{}{}{}{}{}{}{}{}{}{}", text_first_part, - Style::new().fg(ORANGE).bold().paint(alt), + Style::new() + .fg(RGB(palette.orange.0, palette.orange.1, palette.orange.2)) + .bold() + .paint(alt), text_second_part, - Style::new().fg(GREEN).bold().paint(new_pane_shortcut), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(new_pane_shortcut), text_third_part, - Style::new().fg(ORANGE).bold().paint(second_alt), + Style::new() + .fg(RGB(palette.orange.0, palette.orange.1, palette.orange.2)) + .bold() + .paint(second_alt), text_fourth_part, - Style::new().fg(GREEN).bold().paint(brackets_navigation), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(brackets_navigation), text_fifth_part, - Style::new().fg(GREEN).bold().paint(hjkl_navigation), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(hjkl_navigation), text_sixths_part, ), len, } } -fn quicknav_short() -> LinePart { +fn quicknav_short(palette: Palette) -> LinePart { let text_first_part = " QuickNav: "; let alt = "Alt"; let text_second_part = " + "; @@ -160,39 +223,66 @@ fn quicknav_short() -> LinePart { part: format!( "{}{}{}{}{}{}{}{}", text_first_part, - Style::new().fg(ORANGE).bold().paint(alt), + Style::new() + .fg(RGB(palette.orange.0, palette.orange.1, palette.orange.2)) + .bold() + .paint(alt), text_second_part, - Style::new().fg(GREEN).bold().paint(new_pane_shortcut), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(new_pane_shortcut), text_third_part, - Style::new().fg(GREEN).bold().paint(brackets_navigation), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(brackets_navigation), text_fifth_part, - Style::new().fg(GREEN).bold().paint(hjkl_navigation), + Style::new() + .fg(RGB(palette.green.0, palette.green.1, palette.green.2)) + .bold() + .paint(hjkl_navigation), ), len, } } -fn locked_interface_indication() -> LinePart { +fn locked_interface_indication(palette: Palette) -> LinePart { let locked_text = " -- INTERFACE LOCKED -- "; let locked_text_len = locked_text.chars().count(); - let locked_styled_text = Style::new().fg(WHITE).bold().paint(locked_text); + let locked_styled_text = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .bold() + .paint(locked_text); LinePart { part: format!("{}", locked_styled_text), len: locked_text_len, } } -fn select_pane_shortcut(is_first_shortcut: bool) -> LinePart { +fn select_pane_shortcut(is_first_shortcut: bool, palette: Palette) -> LinePart { let shortcut = "ENTER"; let description = "Select pane"; let separator = if is_first_shortcut { " " } else { " / " }; - let separator = Style::new().fg(WHITE).paint(separator); + let separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint(separator); let shortcut_len = shortcut.chars().count() + 3; // 2 for <>'s around shortcut, 1 for the space - let shortcut_left_separator = Style::new().fg(WHITE).paint("<"); - let shortcut = Style::new().fg(ORANGE).bold().paint(shortcut); - let shortcut_right_separator = Style::new().fg(WHITE).paint("> "); + let shortcut_left_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint("<"); + let shortcut = Style::new() + .fg(RGB(palette.orange.0, palette.orange.1, palette.orange.2)) + .bold() + .paint(shortcut); + let shortcut_right_separator = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .paint("> "); let description_len = description.chars().count(); - let description = Style::new().fg(WHITE).bold().paint(description); + let description = Style::new() + .fg(RGB(palette.fg.0, palette.fg.1, palette.fg.2)) + .bold() + .paint(description); let len = shortcut_len + description_len + separator.chars().count(); LinePart { part: format!( @@ -211,16 +301,16 @@ fn select_pane_shortcut(is_first_shortcut: bool) -> LinePart { fn full_shortcut_list(help: &ModeInfo) -> LinePart { match help.mode { - InputMode::Normal => quicknav_full(), - InputMode::Locked => locked_interface_indication(), + InputMode::Normal => quicknav_full(help.palette), + InputMode::Locked => locked_interface_indication(help.palette), _ => { let mut line_part = LinePart::default(); for (i, (letter, description)) in help.keybinds.iter().enumerate() { - let shortcut = full_length_shortcut(i == 0, &letter, &description); + let shortcut = full_length_shortcut(i == 0, &letter, &description, help.palette); line_part.len += shortcut.len; line_part.part = format!("{}{}", line_part.part, shortcut,); } - let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty()); + let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty(), help.palette); line_part.len += select_pane_shortcut.len; line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); line_part @@ -230,16 +320,16 @@ fn full_shortcut_list(help: &ModeInfo) -> LinePart { fn shortened_shortcut_list(help: &ModeInfo) -> LinePart { match help.mode { - InputMode::Normal => quicknav_medium(), - InputMode::Locked => locked_interface_indication(), + InputMode::Normal => quicknav_medium(help.palette), + InputMode::Locked => locked_interface_indication(help.palette), _ => { let mut line_part = LinePart::default(); for (i, (letter, description)) in help.keybinds.iter().enumerate() { - let shortcut = first_word_shortcut(i == 0, &letter, &description); + let shortcut = first_word_shortcut(i == 0, &letter, &description, help.palette); line_part.len += shortcut.len; line_part.part = format!("{}{}", line_part.part, shortcut,); } - let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty()); + let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty(), help.palette); line_part.len += select_pane_shortcut.len; line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); line_part @@ -250,7 +340,7 @@ fn shortened_shortcut_list(help: &ModeInfo) -> LinePart { fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { match help.mode { InputMode::Normal => { - let line_part = quicknav_short(); + let line_part = quicknav_short(help.palette); if line_part.len <= max_len { line_part } else { @@ -258,7 +348,7 @@ fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { } } InputMode::Locked => { - let line_part = locked_interface_indication(); + let line_part = locked_interface_indication(help.palette); if line_part.len <= max_len { line_part } else { @@ -268,7 +358,7 @@ fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { _ => { let mut line_part = LinePart::default(); for (i, (letter, description)) in help.keybinds.iter().enumerate() { - let shortcut = first_word_shortcut(i == 0, &letter, &description); + let shortcut = first_word_shortcut(i == 0, &letter, &description, help.palette); if line_part.len + shortcut.len + MORE_MSG.chars().count() > max_len { // TODO: better line_part.part = format!("{}{}", line_part.part, MORE_MSG); @@ -278,7 +368,7 @@ fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart { line_part.len += shortcut.len; line_part.part = format!("{}{}", line_part.part, shortcut); } - let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty()); + let select_pane_shortcut = select_pane_shortcut(help.keybinds.is_empty(), help.palette); if line_part.len + select_pane_shortcut.len <= max_len { line_part.len += select_pane_shortcut.len; line_part.part = format!("{}{}", line_part.part, select_pane_shortcut,); diff --git a/default-plugins/tab-bar/Cargo.toml b/default-plugins/tab-bar/Cargo.toml index 59b95b4b..d27144dd 100644 --- a/default-plugins/tab-bar/Cargo.toml +++ b/default-plugins/tab-bar/Cargo.toml @@ -9,3 +9,4 @@ license = "MIT" colored = "2" ansi_term = "0.12" zellij-tile = { path = "../../zellij-tile" } +zellij-tile-extra = { path = "../../zellij-tile-extra" } \ No newline at end of file diff --git a/default-plugins/tab-bar/src/line.rs b/default-plugins/tab-bar/src/line.rs index 1d1c33ec..98ad5166 100644 --- a/default-plugins/tab-bar/src/line.rs +++ b/default-plugins/tab-bar/src/line.rs @@ -1,7 +1,8 @@ -use crate::colors::{BLACK, GRAY, ORANGE, WHITE}; -use ansi_term::{ANSIStrings, Style}; +use ansi_term::ANSIStrings; use crate::{LinePart, ARROW_SEPARATOR}; +use zellij_tile::prelude::*; +use zellij_tile_extra::*; fn get_current_title_len(current_title: &[LinePart]) -> usize { current_title @@ -47,7 +48,7 @@ fn populate_tabs_in_tab_line( } } -fn left_more_message(tab_count_to_the_left: usize) -> LinePart { +fn left_more_message(tab_count_to_the_left: usize, palette: Palette) -> LinePart { if tab_count_to_the_left == 0 { return LinePart { part: String::new(), @@ -61,9 +62,9 @@ fn left_more_message(tab_count_to_the_left: usize) -> LinePart { }; // 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 left_separator = style!(palette.fg, palette.orange).paint(ARROW_SEPARATOR); + let more_styled_text = style!(palette.fg, palette.orange).bold().paint(more_text); + let right_separator = style!(palette.orange, palette.bg).paint(ARROW_SEPARATOR); let more_styled_text = format!( "{}", ANSIStrings(&[left_separator, more_styled_text, right_separator,]) @@ -74,7 +75,7 @@ fn left_more_message(tab_count_to_the_left: usize) -> LinePart { } } -fn right_more_message(tab_count_to_the_right: usize) -> LinePart { +fn right_more_message(tab_count_to_the_right: usize, palette: Palette) -> LinePart { if tab_count_to_the_right == 0 { return LinePart { part: String::new(), @@ -87,9 +88,9 @@ fn right_more_message(tab_count_to_the_right: usize) -> LinePart { " +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 left_separator = style!(palette.fg, palette.orange).paint(ARROW_SEPARATOR); + let more_styled_text = style!(palette.fg, palette.orange).bold().paint(more_text); + let right_separator = style!(palette.orange, palette.bg).paint(ARROW_SEPARATOR); let more_styled_text = format!( "{}", ANSIStrings(&[left_separator, more_styled_text, right_separator,]) @@ -105,13 +106,15 @@ fn add_previous_tabs_msg( tabs_to_render: &mut Vec, title_bar: &mut Vec, cols: usize, + palette: Palette, ) { - while get_current_title_len(&tabs_to_render) + left_more_message(tabs_before_active.len()).len + while get_current_title_len(&tabs_to_render) + + left_more_message(tabs_before_active.len(), palette).len >= cols { tabs_before_active.push(tabs_to_render.remove(0)); } - let left_more_message = left_more_message(tabs_before_active.len()); + let left_more_message = left_more_message(tabs_before_active.len(), palette); title_bar.push(left_more_message); } @@ -119,20 +122,22 @@ fn add_next_tabs_msg( tabs_after_active: &mut Vec, title_bar: &mut Vec, cols: usize, + palette: Palette, ) { - while get_current_title_len(&title_bar) + right_more_message(tabs_after_active.len()).len + while get_current_title_len(&title_bar) + + right_more_message(tabs_after_active.len(), palette).len >= cols { tabs_after_active.insert(0, title_bar.pop().unwrap()); } - let right_more_message = right_more_message(tabs_after_active.len()); + let right_more_message = right_more_message(tabs_after_active.len(), palette); title_bar.push(right_more_message); } -fn tab_line_prefix() -> LinePart { +fn tab_line_prefix(palette: Palette) -> 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); + let prefix_styled_text = style!(palette.fg, palette.bg).bold().paint(prefix_text); LinePart { part: format!("{}", prefix_styled_text), len: prefix_text_len, @@ -143,6 +148,7 @@ pub fn tab_line( mut all_tabs: Vec, active_tab_index: usize, cols: usize, + palette: Palette, ) -> Vec { let mut tabs_to_render: Vec = vec![]; let mut tabs_after_active = all_tabs.split_off(active_tab_index); @@ -154,7 +160,7 @@ pub fn tab_line( }; tabs_to_render.push(active_tab); - let prefix = tab_line_prefix(); + let prefix = tab_line_prefix(palette); populate_tabs_in_tab_line( &mut tabs_before_active, &mut tabs_after_active, @@ -169,11 +175,17 @@ pub fn tab_line( &mut tabs_to_render, &mut tab_line, cols - prefix.len, + palette, ); } 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); + add_next_tabs_msg( + &mut tabs_after_active, + &mut tab_line, + cols - prefix.len, + palette, + ); } 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 index f059d20e..1c6d584e 100644 --- a/default-plugins/tab-bar/src/main.rs +++ b/default-plugins/tab-bar/src/main.rs @@ -15,22 +15,11 @@ pub struct LinePart { #[derive(Default)] struct State { tabs: Vec, - mode: InputMode, + 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 { @@ -43,7 +32,7 @@ impl ZellijPlugin for State { fn update(&mut self, event: Event) { match event { - Event::ModeUpdate(mode_info) => self.mode = mode_info.mode, + Event::ModeUpdate(mode_info) => self.mode_info = mode_info, Event::TabUpdate(tabs) => self.tabs = tabs, _ => unimplemented!(), // FIXME: This should be unreachable, but this could be cleaner } @@ -57,7 +46,7 @@ impl ZellijPlugin for State { let mut active_tab_index = 0; for t in self.tabs.iter_mut() { let mut tabname = t.name.clone(); - if t.active && self.mode == InputMode::RenameTab { + if t.active && self.mode_info.mode == InputMode::RenameTab { if tabname.is_empty() { tabname = String::from("Enter name..."); } @@ -65,14 +54,26 @@ impl ZellijPlugin for State { } else if t.active { active_tab_index = t.position; } - let tab = tab_style(tabname, t.active, t.position, t.is_sync_panes_active); + let tab = tab_style( + tabname, + t.active, + t.position, + t.is_sync_panes_active, + self.mode_info.palette, + ); all_tabs.push(tab); } - let tab_line = tab_line(all_tabs, active_tab_index, cols); + let tab_line = tab_line(all_tabs, active_tab_index, cols, self.mode_info.palette); let mut s = String::new(); for bar_part in tab_line { s = format!("{}{}", s, bar_part.part); } - println!("{}\u{1b}[48;5;238m\u{1b}[0K", s); + println!( + "{}\u{1b}[48;2;{};{};{}m\u{1b}[0K", + s, + self.mode_info.palette.bg.0, + self.mode_info.palette.bg.1, + self.mode_info.palette.bg.2 + ); } } diff --git a/default-plugins/tab-bar/src/tab.rs b/default-plugins/tab-bar/src/tab.rs index 6c52e629..ab110634 100644 --- a/default-plugins/tab-bar/src/tab.rs +++ b/default-plugins/tab-bar/src/tab.rs @@ -1,16 +1,15 @@ -use crate::colors::{BLACK, BRIGHT_GRAY, GRAY, GREEN}; use crate::{LinePart, ARROW_SEPARATOR}; -use ansi_term::{ANSIStrings, Style}; +use ansi_term::ANSIStrings; +use zellij_tile::prelude::*; +use zellij_tile_extra::*; -pub fn active_tab(text: String) -> LinePart { - let left_separator = Style::new().fg(GRAY).on(GREEN).paint(ARROW_SEPARATOR); +pub fn active_tab(text: String, palette: Palette) -> LinePart { + let left_separator = style!(palette.bg, palette.green).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(BLACK) - .on(GREEN) + let tab_styled_text = style!(palette.bg, palette.green) .bold() .paint(format!(" {} ", text)); - let right_separator = Style::new().fg(GREEN).on(GRAY).paint(ARROW_SEPARATOR); + let right_separator = style!(palette.green, palette.bg).paint(ARROW_SEPARATOR); let tab_styled_text = format!( "{}", ANSIStrings(&[left_separator, tab_styled_text, right_separator,]) @@ -21,15 +20,13 @@ pub fn active_tab(text: String) -> LinePart { } } -pub fn non_active_tab(text: String) -> LinePart { - let left_separator = Style::new().fg(GRAY).on(BRIGHT_GRAY).paint(ARROW_SEPARATOR); +pub fn non_active_tab(text: String, palette: Palette) -> LinePart { + let left_separator = style!(palette.bg, palette.bg).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(BLACK) - .on(BRIGHT_GRAY) + let tab_styled_text = style!(palette.fg, palette.bg) .bold() .paint(format!(" {} ", text)); - let right_separator = Style::new().fg(BRIGHT_GRAY).on(GRAY).paint(ARROW_SEPARATOR); + let right_separator = style!(palette.bg, palette.bg).paint(ARROW_SEPARATOR); let tab_styled_text = format!( "{}", ANSIStrings(&[left_separator, tab_styled_text, right_separator,]) @@ -45,6 +42,7 @@ pub fn tab_style( is_active_tab: bool, position: usize, is_sync_panes_active: bool, + palette: Palette, ) -> LinePart { let mut tab_text = if text.is_empty() { format!("Tab #{}", position + 1) @@ -55,8 +53,8 @@ pub fn tab_style( tab_text.push_str(" (Sync)"); } if is_active_tab { - active_tab(tab_text) + active_tab(tab_text, palette) } else { - non_active_tab(tab_text) + non_active_tab(tab_text, palette) } } diff --git a/src/client/boundaries.rs b/src/client/boundaries.rs index 28e01a11..4708ed6a 100644 --- a/src/client/boundaries.rs +++ b/src/client/boundaries.rs @@ -1,10 +1,10 @@ use crate::tab::Pane; -use ansi_term::Colour; +use crate::utils::shared::colors; +use ansi_term::Colour::RGB; use std::collections::HashMap; -use zellij_tile::data::InputMode; +use zellij_tile::data::{InputMode, Palette}; use std::fmt::{Display, Error, Formatter}; - pub mod boundary_type { pub const TOP_RIGHT: &str = "┐"; pub const VERTICAL: &str = "│"; @@ -19,12 +19,12 @@ pub mod boundary_type { pub const CROSS: &str = "┼"; } -pub mod colors { - use ansi_term::Colour::{self, Fixed}; - pub const GREEN: Colour = Fixed(154); - pub const GRAY: Colour = Fixed(238); - pub const ORANGE: Colour = Fixed(166); -} +// pub mod colors { +// use ansi_term::Colour::{self, Fixed}; +// pub const GREEN: Colour = Fixed(154); +// pub const GRAY: Colour = Fixed(238); +// pub const ORANGE: Colour = Fixed(166); +// } pub type BoundaryType = &'static str; // easy way to refer to boundary_type above @@ -32,7 +32,7 @@ pub type BoundaryType = &'static str; // easy way to refer to boundary_type abov pub struct BoundarySymbol { boundary_type: BoundaryType, invisible: bool, - color: Option, + color: Option<(u8, u8, u8)>, } impl BoundarySymbol { @@ -47,7 +47,7 @@ impl BoundarySymbol { self.invisible = true; self } - pub fn color(&mut self, color: Option) -> Self { + pub fn color(&mut self, color: Option<(u8, u8, u8)>) -> Self { self.color = color; *self } @@ -58,7 +58,11 @@ impl Display for BoundarySymbol { match self.invisible { true => write!(f, " "), false => match self.color { - Some(color) => write!(f, "{}", color.paint(self.boundary_type)), + Some(color) => write!( + f, + "{}", + RGB(color.0, color.1, color.2).paint(self.boundary_type) + ), None => write!(f, "{}", self.boundary_type), }, } @@ -764,11 +768,11 @@ impl Boundaries { boundary_characters: HashMap::new(), } } - pub fn add_rect(&mut self, rect: &dyn Pane, input_mode: InputMode, color: Option) { - let color = match color.is_some() { + pub fn add_rect(&mut self, rect: &dyn Pane, input_mode: InputMode, palette: Option) { + let color = match palette.is_some() { true => match input_mode { - InputMode::Normal | InputMode::Locked => Some(colors::GREEN), - _ => Some(colors::ORANGE), + InputMode::Normal | InputMode::Locked => Some(palette.unwrap().green), + _ => Some(palette.unwrap().orange), }, false => None, }; diff --git a/src/client/tab.rs b/src/client/tab.rs index f9bf7b57..b01e04e5 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -1,7 +1,6 @@ //! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size, //! as well as how they should be resized -use crate::boundaries::colors; use crate::client::pane_resizer::PaneResizer; use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContext}; use crate::layout::Layout; @@ -19,7 +18,7 @@ use std::{ collections::{BTreeMap, HashSet}, }; use std::{io::Write, sync::mpsc::channel}; -use zellij_tile::data::{Event, ModeInfo}; +use zellij_tile::data::{Event, InputMode, ModeInfo, Palette}; const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this @@ -75,6 +74,8 @@ pub struct Tab { pub send_app_instructions: SenderWithContext, should_clear_display_before_rendering: bool, pub mode_info: ModeInfo, + pub input_mode: InputMode, + pub colors: Palette, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -84,6 +85,8 @@ pub struct TabData { pub name: String, pub active: bool, pub mode_info: ModeInfo, + pub input_mode: InputMode, + pub colors: Palette, } // FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication @@ -229,6 +232,8 @@ impl Tab { max_panes: Option, pane_id: Option, mode_info: ModeInfo, + input_mode: InputMode, + colors: Palette, ) -> Self { let panes = if let Some(PaneId::Terminal(pid)) = pane_id { let new_terminal = TerminalPane::new(pid, *full_screen_ws); @@ -260,6 +265,8 @@ impl Tab { send_plugin_instructions, should_clear_display_before_rendering: false, mode_info, + input_mode, + colors, } } @@ -743,7 +750,7 @@ impl Tab { match self.active_terminal.unwrap() == pane.pid() { true => { pane.set_active_at(Instant::now()); - boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(colors::GREEN)) + boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(self.colors)) } false => boundaries.add_rect(pane.as_ref(), self.mode_info.mode, None), } diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 62118722..7304225c 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -12,7 +12,7 @@ use crate::wasm_vm::PluginInstruction; use crate::CommandIsExecuting; use termion::input::{TermRead, TermReadEventsAndRaw}; -use zellij_tile::data::{Event, InputMode, Key, ModeInfo}; +use zellij_tile::data::{Event, InputMode, Key, ModeInfo, Palette}; /// Handles the dispatching of [`Action`]s according to the current /// [`InputMode`], and keep tracks of the current [`InputMode`]. @@ -131,11 +131,14 @@ impl InputHandler { self.send_plugin_instructions .send(PluginInstruction::Update( None, - Event::ModeUpdate(get_mode_info(mode)), + Event::ModeUpdate(get_mode_info(mode, self.os_input.load_palette())), )) .unwrap(); self.send_screen_instructions - .send(ScreenInstruction::ChangeMode(get_mode_info(mode))) + .send(ScreenInstruction::ChangeMode(get_mode_info( + mode, + self.os_input.load_palette(), + ))) .unwrap(); self.send_screen_instructions .send(ScreenInstruction::Render) @@ -284,7 +287,7 @@ impl InputHandler { /// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds /// (as pairs of [`String`]s). // TODO this should probably be automatically generated in some way -pub fn get_mode_info(mode: InputMode) -> ModeInfo { +pub fn get_mode_info(mode: InputMode, palette: Palette) -> ModeInfo { let mut keybinds: Vec<(String, String)> = vec![]; match mode { InputMode::Normal | InputMode::Locked => {} @@ -315,7 +318,11 @@ pub fn get_mode_info(mode: InputMode) -> ModeInfo { keybinds.push(("Enter".to_string(), "when done".to_string())); } } - ModeInfo { mode, keybinds } + ModeInfo { + mode, + keybinds, + palette, + } } /// Entry point to the module. Instantiates an [`InputHandler`] and starts diff --git a/src/common/mod.rs b/src/common/mod.rs index 0afc6ef2..28446d2e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -43,7 +43,7 @@ use utils::consts::ZELLIJ_IPC_PIPE; use wasm_vm::{wasi_read_string, wasi_write_object, zellij_exports, PluginEnv, PluginInstruction}; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; -use zellij_tile::data::{EventType, ModeInfo}; +use zellij_tile::data::{EventType, InputMode, ModeInfo}; #[derive(Serialize, Deserialize, Debug)] pub enum ApiCommand { @@ -258,7 +258,7 @@ pub fn start(mut os_input: Box, opts: CliArgs, config: Config) { let send_plugin_instructions = send_plugin_instructions.clone(); let send_app_instructions = send_app_instructions.clone(); let max_panes = opts.max_panes; - + let colors = os_input.load_palette(); move || { let mut screen = Screen::new( receive_screen_instructions, @@ -268,7 +268,12 @@ pub fn start(mut os_input: Box, opts: CliArgs, config: Config) { &full_screen_ws, os_input, max_panes, - ModeInfo::default(), + ModeInfo { + palette: colors, + ..ModeInfo::default() + }, + InputMode::Normal, + colors, ); loop { let (event, mut err_ctx) = screen diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 35d77fdd..41a4bfe4 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -1,4 +1,5 @@ use crate::panes::PositionAndSize; +use crate::utils::shared::{colors, detect_theme, hex_to_rgb}; use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::pty::{forkpty, Winsize}; use nix::sys::signal::{kill, Signal}; @@ -12,6 +13,8 @@ use std::os::unix::io::RawFd; use std::path::PathBuf; use std::process::{Child, Command}; use std::sync::{Arc, Mutex}; +use xrdb::Colors; +use zellij_tile::data::{Palette, PaletteSource, Theme}; use signal_hook::{consts::signal::*, iterator::Signals}; @@ -191,6 +194,7 @@ pub trait OsApi: Send + Sync { /// Returns a [`Box`] pointer to this [`OsApi`] struct. fn box_clone(&self) -> Box; fn receive_sigwinch(&self, cb: Box); + fn load_palette(&self) -> Palette; } impl OsApi for OsInputOutput { @@ -261,6 +265,48 @@ impl OsApi for OsInputOutput { } } } + fn load_palette(&self) -> Palette { + let palette = match Colors::new("xresources") { + Some(palette) => { + let fg = hex_to_rgb(&palette.fg); + let bg = hex_to_rgb(&palette.bg); + let colors: Vec<(u8, u8, u8)> = + palette.colors.iter().map(|c| hex_to_rgb(c)).collect(); + let theme = detect_theme(bg); + Palette { + source: PaletteSource::Xresources, + theme, + fg, + bg, + black: colors[0], + red: colors[1], + green: colors[2], + yellow: colors[3], + blue: colors[4], + magenta: colors[5], + cyan: colors[6], + white: colors[7], + orange: colors[9], + } + } + None => Palette { + source: PaletteSource::Default, + theme: Theme::Dark, + fg: colors::BRIGHT_GRAY, + bg: colors::GRAY, + black: colors::BLACK, + red: colors::RED, + green: colors::GREEN, + yellow: colors::GRAY, + blue: colors::GRAY, + magenta: colors::GRAY, + cyan: colors::GRAY, + white: colors::WHITE, + orange: colors::ORANGE, + }, + }; + palette + } } impl Clone for Box { diff --git a/src/common/screen.rs b/src/common/screen.rs index eee1166f..e6cf73f5 100644 --- a/src/common/screen.rs +++ b/src/common/screen.rs @@ -13,7 +13,7 @@ use crate::tab::Tab; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{layout::Layout, panes::PaneId}; -use zellij_tile::data::{Event, ModeInfo, TabInfo}; +use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, TabInfo}; /// Instructions that can be sent to the [`Screen`]. #[derive(Debug, Clone)] @@ -81,6 +81,8 @@ pub struct Screen { /// The [`OsApi`] this [`Screen`] uses. os_api: Box, mode_info: ModeInfo, + input_mode: InputMode, + colors: Palette, } impl Screen { @@ -96,6 +98,8 @@ impl Screen { os_api: Box, max_panes: Option, mode_info: ModeInfo, + input_mode: InputMode, + colors: Palette, ) -> Self { Screen { receiver: receive_screen_instructions, @@ -108,6 +112,8 @@ impl Screen { tabs: BTreeMap::new(), os_api, mode_info, + input_mode, + colors, } } @@ -128,6 +134,8 @@ impl Screen { self.max_panes, Some(PaneId::Terminal(pane_id)), self.mode_info.clone(), + self.input_mode, + self.colors, ); self.active_tab_index = Some(tab_index); self.tabs.insert(tab_index, tab); @@ -283,6 +291,8 @@ impl Screen { self.max_panes, None, self.mode_info.clone(), + self.input_mode, + self.colors, ); tab.apply_layout(layout, new_pids); self.active_tab_index = Some(tab_index); diff --git a/src/common/utils/shared.rs b/src/common/utils/shared.rs index d5ba943b..3f336f7b 100644 --- a/src/common/utils/shared.rs +++ b/src/common/utils/shared.rs @@ -4,6 +4,9 @@ use std::{iter, str::from_utf8}; use strip_ansi_escapes::strip; +use colors_transform::{Color, Rgb}; +use zellij_tile::data::Theme; + fn ansi_len(s: &str) -> usize { from_utf8(&strip(s.as_bytes()).unwrap()) .unwrap() @@ -28,3 +31,36 @@ pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String { .collect::>() .join("\n\r") } + +// Colors +pub mod colors { + pub const WHITE: (u8, u8, u8) = (238, 238, 238); + pub const GREEN: (u8, u8, u8) = (175, 255, 0); + pub const GRAY: (u8, u8, u8) = (68, 68, 68); + pub const BRIGHT_GRAY: (u8, u8, u8) = (138, 138, 138); + pub const RED: (u8, u8, u8) = (135, 0, 0); + pub const ORANGE: (u8, u8, u8) = (215, 95, 0); + pub const BLACK: (u8, u8, u8) = (0, 0, 0); +} + +pub fn hex_to_rgb(hex: &Option) -> (u8, u8, u8) { + let c = hex.clone(); + let imm_str = &c.unwrap(); + let hex_str: &str = &imm_str; + let rgb = Rgb::from_hex_str(hex_str).unwrap().as_tuple(); + (rgb.0 as u8, rgb.1 as u8, rgb.2 as u8) +} + +// Dark magic +pub fn detect_theme(bg: (u8, u8, u8)) -> Theme { + let (r, g, b) = bg; + // HSP, P stands for perceived brightness + let hsp: f64 = (0.299 * (r as f64 * r as f64) + + 0.587 * (g as f64 * g as f64) + + 0.114 * (b as f64 * b as f64)) + .sqrt(); + match hsp > 127.5 { + true => Theme::Light, + false => Theme::Dark, + } +} diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index c9504a98..e00de017 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -8,6 +8,8 @@ use std::time::{Duration, Instant}; use crate::os_input_output::OsApi; use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes}; +use crate::utils::shared::colors; +use zellij_tile::data::{Palette, PaletteSource, Theme}; use crate::tests::utils::commands::{QUIT, SLEEP}; @@ -73,6 +75,7 @@ pub struct FakeInputOutput { last_snapshot_time: Arc>, should_trigger_sigwinch: Arc<(Mutex, Condvar)>, sigwinch_event: Option, + palette: Arc>, } impl FakeInputOutput { @@ -81,6 +84,21 @@ impl FakeInputOutput { let last_snapshot_time = Arc::new(Mutex::new(Instant::now())); let stdout_writer = FakeStdoutWriter::new(last_snapshot_time.clone()); win_sizes.insert(0, winsize); // 0 is the current terminal + let palette: Palette = Palette { + source: PaletteSource::Default, + theme: Theme::Dark, + fg: colors::BRIGHT_GRAY, + bg: colors::GRAY, + black: colors::BLACK, + red: colors::RED, + green: colors::GREEN, + yellow: colors::GRAY, + blue: colors::GRAY, + magenta: colors::GRAY, + cyan: colors::GRAY, + white: colors::WHITE, + orange: colors::ORANGE, + }; FakeInputOutput { read_buffers: Arc::new(Mutex::new(HashMap::new())), stdin_writes: Arc::new(Mutex::new(HashMap::new())), @@ -93,6 +111,7 @@ impl FakeInputOutput { possible_tty_inputs: get_possible_tty_inputs(), should_trigger_sigwinch: Arc::new((Mutex::new(false), Condvar::new())), sigwinch_event: None, + palette: Arc::new(Mutex::new(palette)), } } pub fn with_tty_inputs(mut self, tty_inputs: HashMap) -> Self { @@ -235,4 +254,22 @@ impl OsApi for FakeInputOutput { cb(); } } + fn load_palette(&self) -> Palette { + let palette: Palette = Palette { + source: PaletteSource::Default, + theme: Theme::Dark, + fg: colors::BRIGHT_GRAY, + bg: colors::GRAY, + black: colors::BLACK, + red: colors::RED, + green: colors::GREEN, + yellow: colors::GRAY, + blue: colors::GRAY, + magenta: colors::GRAY, + cyan: colors::GRAY, + white: colors::WHITE, + orange: colors::ORANGE, + }; + palette + } } diff --git a/zellij-tile-extra/Cargo.toml b/zellij-tile-extra/Cargo.toml new file mode 100644 index 00000000..69a0298b --- /dev/null +++ b/zellij-tile-extra/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "zellij-tile-extra" +version = "1.0.0" +authors = ["denis "] +edition = "2018" +description = "A utility library for Zellij plugins" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ansi_term = "0.12.1" \ No newline at end of file diff --git a/zellij-tile-extra/LICENSE.md b/zellij-tile-extra/LICENSE.md new file mode 120000 index 00000000..7eabdb1c --- /dev/null +++ b/zellij-tile-extra/LICENSE.md @@ -0,0 +1 @@ +../LICENSE.md \ No newline at end of file diff --git a/zellij-tile-extra/src/lib.rs b/zellij-tile-extra/src/lib.rs new file mode 100644 index 00000000..bec19158 --- /dev/null +++ b/zellij-tile-extra/src/lib.rs @@ -0,0 +1,15 @@ +#[macro_export] +macro_rules! rgb { + ($a:expr) => { + ansi_term::Color::RGB($a.0, $a.1, $a.2) + }; +} + +#[macro_export] +macro_rules! style { + ($a:expr, $b:expr) => { + ansi_term::Style::new() + .fg(ansi_term::Color::RGB($a.0, $a.1, $a.2)) + .on(ansi_term::Color::RGB($b.0, $b.1, $b.2)) + }; +} diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index 713282cb..86100e7a 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -67,6 +67,44 @@ impl Default for InputMode { } } +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum Theme { + Light, + Dark, +} +impl Default for Theme { + fn default() -> Theme { + Theme::Dark + } +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum PaletteSource { + Default, + Xresources, +} +impl Default for PaletteSource { + fn default() -> PaletteSource { + PaletteSource::Default + } +} +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct Palette { + pub source: PaletteSource, + pub theme: Theme, + pub fg: (u8, u8, u8), + pub bg: (u8, u8, u8), + pub black: (u8, u8, u8), + pub red: (u8, u8, u8), + pub green: (u8, u8, u8), + pub yellow: (u8, u8, u8), + pub blue: (u8, u8, u8), + pub magenta: (u8, u8, u8), + pub cyan: (u8, u8, u8), + pub white: (u8, u8, u8), + pub orange: (u8, u8, u8), +} + /// Represents the contents of the help message that is printed in the status bar, /// which indicates the current [`InputMode`] and what the keybinds for that mode /// are. Related to the default `status-bar` plugin. @@ -75,6 +113,7 @@ pub struct ModeInfo { pub mode: InputMode, // FIXME: This should probably return Keys and Actions, then sort out strings plugin-side pub keybinds: Vec<(String, String)>, // => + pub palette: Palette, } #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]