diff options
author | Aram Drevekenin <aram@poor.dev> | 2024-07-05 15:13:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-05 15:13:51 +0200 |
commit | a6d6c0e4ff03c35cafb6f04363ecab856ee3682d (patch) | |
tree | eadf1c24c4c6a1e4b7499b3cd047351fd29e52de /default-plugins/status-bar/src | |
parent | 8e33b20559ecddcaba17e5cc8d6f05b638718cac (diff) |
feat(ui): status bar redesign (#3475)
* work
* work
* working
* get default mode from server and some ui responsiveness
* work
* finish design and get tests to pass
* get e2e tests to pass
* add classic layout
* add classic layout assets
* fix e2e tests
* style(fmt): rustfmt
* fix plugin system test
* style(fmt): some cleanups
Diffstat (limited to 'default-plugins/status-bar/src')
-rw-r--r-- | default-plugins/status-bar/src/first_line.rs | 47 | ||||
-rw-r--r-- | default-plugins/status-bar/src/main.rs | 30 | ||||
-rw-r--r-- | default-plugins/status-bar/src/one_line_ui.rs | 1440 |
3 files changed, 1505 insertions, 12 deletions
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index 1fffd7558..ee9b9478d 100644 --- a/default-plugins/status-bar/src/first_line.rs +++ b/default-plugins/status-bar/src/first_line.rs @@ -8,14 +8,16 @@ use crate::{ }; use crate::{ColoredElements, LinePart}; -struct KeyShortcut { - mode: KeyMode, - action: KeyAction, - key: Option<KeyWithModifier>, +#[derive(Debug)] +pub struct KeyShortcut { + pub mode: KeyMode, + pub action: KeyAction, + pub key: Option<KeyWithModifier>, } -#[derive(PartialEq)] -enum KeyAction { +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum KeyAction { + Unlock, Lock, Pane, Tab, @@ -27,7 +29,8 @@ enum KeyAction { Tmux, } -enum KeyMode { +#[derive(Debug, Copy, Clone)] +pub enum KeyMode { Unselected, UnselectedAlternate, Selected, @@ -42,6 +45,7 @@ impl KeyShortcut { pub fn full_text(&self) -> String { match self.action { KeyAction::Lock => String::from("LOCK"), + KeyAction::Unlock => String::from("UNLOCK"), KeyAction::Pane => String::from("PANE"), KeyAction::Tab => String::from("TAB"), KeyAction::Resize => String::from("RESIZE"), @@ -82,6 +86,35 @@ impl KeyShortcut { }; format!("{}", key) } + pub fn get_key(&self) -> Option<KeyWithModifier> { + self.key.clone() + } + pub fn get_mode(&self) -> KeyMode { + self.mode + } + pub fn get_action(&self) -> KeyAction { + self.action + } + pub fn is_selected(&self) -> bool { + match self.mode { + KeyMode::Selected => true, + _ => false, + } + } + pub fn short_text(&self) -> String { + match self.action { + KeyAction::Lock => String::from("Lo"), + KeyAction::Unlock => String::from("Un"), + KeyAction::Pane => String::from("Pa"), + KeyAction::Tab => String::from("Ta"), + KeyAction::Resize => String::from("Re"), + KeyAction::Search => String::from("Se"), + KeyAction::Quit => String::from("Qu"), + KeyAction::Session => String::from("Se"), + KeyAction::Move => String::from("Mo"), + KeyAction::Tmux => String::from("Tm"), + } + } } /// Generate long mode shortcut tile. diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 6fed529f4..7db9b09d9 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -1,4 +1,5 @@ mod first_line; +mod one_line_ui; mod second_line; mod tip; @@ -15,6 +16,7 @@ use zellij_tile::prelude::*; use zellij_tile_utils::{palette_match, style}; use first_line::first_line; +use one_line_ui::one_line_ui; use second_line::{ floating_panes_are_visible, fullscreen_panes_to_hide, keybinds, locked_floating_panes_are_visible, locked_fullscreen_panes_to_hide, system_clipboard_error, @@ -35,6 +37,8 @@ struct State { mode_info: ModeInfo, text_copy_destination: Option<CopyDestination>, display_system_clipboard_failure: bool, + classic_ui: bool, + base_mode_is_locked: bool, } register_plugin!(State); @@ -177,9 +181,13 @@ fn color_elements(palette: Palette, different_color_alternates: bool) -> Colored } impl ZellijPlugin for State { - fn load(&mut self, _configuration: BTreeMap<String, String>) { + fn load(&mut self, configuration: BTreeMap<String, String>) { // TODO: Should be able to choose whether to use the cache through config. self.tip_name = get_cached_tip_name(); + self.classic_ui = configuration + .get("classic") + .map(|c| c == "true") + .unwrap_or(false); set_selectable(false); subscribe(&[ EventType::ModeUpdate, @@ -198,6 +206,7 @@ impl ZellijPlugin for State { should_render = true; } self.mode_info = mode_info; + self.base_mode_is_locked = self.mode_info.base_mode == Some(InputMode::Locked); }, Event::TabUpdate(tabs) => { if self.tabs != tabs { @@ -244,6 +253,21 @@ impl ZellijPlugin for State { "" }; + if rows == 1 && !self.classic_ui { + let active_tab = self.tabs.iter().find(|t| t.active); + print!( + "{}", + one_line_ui( + &self.mode_info, + active_tab, + cols, + separator, + self.base_mode_is_locked + ) + ); + return; + } + let active_tab = self.tabs.iter().find(|t| t.active); let first_line = first_line(&self.mode_info, active_tab, cols, separator); let second_line = self.second_line(cols); @@ -418,10 +442,6 @@ pub fn style_key_with_modifier( let common_modifiers = get_common_modifiers(keyvec.iter().collect()); - // let modifier_str = match get_common_modifier(keyvec.iter().collect()) { - // Some(modifier) => modifier, - // None => "".to_string(), - // }; let no_common_modifier = common_modifiers.is_empty(); let modifier_str = common_modifiers .iter() diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs new file mode 100644 index 000000000..87b1e2fd2 --- /dev/null +++ b/default-plugins/status-bar/src/one_line_ui.rs @@ -0,0 +1,1440 @@ +use ansi_term::{ANSIString, ANSIStrings}; +use ansi_term::{ + Color::{Fixed, RGB}, + Style, +}; +use std::collections::HashMap; +use zellij_tile::prelude::actions::Action; +use zellij_tile::prelude::*; +use zellij_tile_utils::palette_match; + +use crate::first_line::{to_char, KeyAction, KeyMode, KeyShortcut}; +use crate::{action_key, action_key_group, color_elements, MORE_MSG, TO_NORMAL}; +use crate::{ColoredElements, LinePart}; +use unicode_width::UnicodeWidthStr; + +pub fn one_line_ui( + help: &ModeInfo, + tab_info: Option<&TabInfo>, + mut max_len: usize, + separator: &str, + base_mode_is_locked: bool, +) -> LinePart { + let mut line_part_to_render = LinePart::default(); + let mut append = |line_part: &LinePart, max_len: &mut usize| { + line_part_to_render.append(line_part); + *max_len = max_len.saturating_sub(line_part.len); + }; + + render_mode_key_indicators(help, max_len, separator, base_mode_is_locked) + .map(|mode_key_indicators| append(&mode_key_indicators, &mut max_len)) + .and_then(|_| match help.mode { + InputMode::Normal | InputMode::Locked => render_secondary_info(help, tab_info, max_len) + .map(|secondary_info| append(&secondary_info, &mut max_len)), + _ => add_keygroup_separator(help, max_len) + .map(|key_group_separator| append(&key_group_separator, &mut max_len)) + .and_then(|_| keybinds(help, max_len)) + .map(|keybinds| append(&keybinds, &mut max_len)), + }); + line_part_to_render +} + +fn to_base_mode(base_mode: InputMode) -> Action { + Action::SwitchToMode(base_mode) +} + +fn base_mode_locked_mode_indicators(help: &ModeInfo) -> HashMap<InputMode, Vec<KeyShortcut>> { + let locked_binds = &help.get_keybinds_for_mode(InputMode::Locked); + let normal_binds = &help.get_keybinds_for_mode(InputMode::Normal); + let pane_binds = &help.get_keybinds_for_mode(InputMode::Pane); + let tab_binds = &help.get_keybinds_for_mode(InputMode::Tab); + let resize_binds = &help.get_keybinds_for_mode(InputMode::Resize); + let move_binds = &help.get_keybinds_for_mode(InputMode::Move); + let scroll_binds = &help.get_keybinds_for_mode(InputMode::Scroll); + let session_binds = &help.get_keybinds_for_mode(InputMode::Session); + HashMap::from([ + ( + InputMode::Locked, + vec![KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Unlock, + to_char(action_key( + locked_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Normal, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Pane, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Pane)], + )), + ), + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Tab, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Tab)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Resize, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Resize)], + )), + ), + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Move, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Move)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Search, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Scroll)], + )), + ), + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Session, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Session)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Quit, + to_char(action_key(normal_binds, &[Action::Quit])), + ), + ], + ), + ( + InputMode::Pane, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + pane_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Pane, + to_char(action_key( + pane_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + ), + ], + ), + ( + InputMode::Tab, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + tab_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Tab, + to_char(action_key( + tab_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + ), + ], + ), + ( + InputMode::Resize, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + resize_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Resize, + to_char(action_key( + resize_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + ), + ], + ), + ( + InputMode::Move, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + move_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Move, + to_char(action_key( + move_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + ), + ], + ), + ( + InputMode::Scroll, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + scroll_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Search, + to_char(action_key( + scroll_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + ), + ], + ), + ( + InputMode::Session, + vec![ + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Unlock, + to_char(action_key( + session_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::Selected, + KeyAction::Session, + to_char(action_key( + session_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + ), + ], + ), + ]) +} + +fn base_mode_normal_mode_indicators(help: &ModeInfo) -> HashMap<InputMode, Vec<KeyShortcut>> { + let locked_binds = &help.get_keybinds_for_mode(InputMode::Locked); + let normal_binds = &help.get_keybinds_for_mode(InputMode::Normal); + let pane_binds = &help.get_keybinds_for_mode(InputMode::Pane); + let tab_binds = &help.get_keybinds_for_mode(InputMode::Tab); + let resize_binds = &help.get_keybinds_for_mode(InputMode::Resize); + let move_binds = &help.get_keybinds_for_mode(InputMode::Move); + let scroll_binds = &help.get_keybinds_for_mode(InputMode::Scroll); + let session_binds = &help.get_keybinds_for_mode(InputMode::Session); + HashMap::from([ + ( + InputMode::Locked, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Lock, + to_char(action_key( + locked_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Normal, + vec![ + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Lock, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Pane, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Pane)], + )), + ), + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Tab, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Tab)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Resize, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Resize)], + )), + ), + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Move, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Move)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Search, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Scroll)], + )), + ), + KeyShortcut::new( + KeyMode::Unselected, + KeyAction::Session, + to_char(action_key( + normal_binds, + &[Action::SwitchToMode(InputMode::Session)], + )), + ), + KeyShortcut::new( + KeyMode::UnselectedAlternate, + KeyAction::Quit, + to_char(action_key(normal_binds, &[Action::Quit])), + ), + ], + ), + ( + InputMode::Pane, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Pane, + to_char(action_key( + pane_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Tab, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Tab, + to_char(action_key( + tab_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Resize, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Resize, + to_char(action_key( + resize_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Move, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Move, + to_char(action_key( + move_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Scroll, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Search, + to_char(action_key( + scroll_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ( + InputMode::Session, + vec![KeyShortcut::new( + KeyMode::Selected, + KeyAction::Session, + to_char(action_key( + session_binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + )], + ), + ]) +} + +fn render_mode_key_indicators( + help: &ModeInfo, + max_len: usize, + separator: &str, + base_mode_is_locked: bool, +) -> Option<LinePart> { + let mut line_part_to_render = LinePart::default(); + let supports_arrow_fonts = !help.capabilities.arrow_fonts; + let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts); + let default_keys = if base_mode_is_locked { + base_mode_locked_mode_indicators(help) + } else { + base_mode_normal_mode_indicators(help) + }; + match common_modifiers_in_all_modes(&default_keys) { + Some(modifiers) => { + if let Some(default_keys) = default_keys.get(&help.mode) { + let keys_without_common_modifiers: Vec<KeyShortcut> = default_keys + .iter() + .map(|key_shortcut| { + let key = key_shortcut + .get_key() + .map(|k| k.strip_common_modifiers(&modifiers)); + let mode = key_shortcut.get_mode(); + let action = key_shortcut.get_action(); + KeyShortcut::new(mode, action, key) + }) + .collect(); + render_common_modifiers( + &colored_elements, + help, + &modifiers, + &mut line_part_to_render, + separator, + ); + + let full_shortcut_list = + full_inline_keys_modes_shortcut_list(&keys_without_common_modifiers, help); + + if line_part_to_render.len + full_shortcut_list.len <= max_len { + line_part_to_render.append(&full_shortcut_list); + } else { + let shortened_shortcut_list = shortened_inline_keys_modes_shortcut_list( + &keys_without_common_modifiers, + help, + ); + if line_part_to_render.len + shortened_shortcut_list.len <= max_len { + line_part_to_render.append(&shortened_shortcut_list); + } + } + } + }, + None => { + if let Some(default_keys) = default_keys.get(&help.mode) { + let full_shortcut_list = full_modes_shortcut_list(&default_keys, help); + if line_part_to_render.len + full_shortcut_list.len <= max_len { + line_part_to_render.append(&full_shortcut_list); + } else { + let shortened_shortcut_list = + shortened_modes_shortcut_list(&default_keys, help); + if line_part_to_render.len + shortened_shortcut_list.len <= max_len { + line_part_to_render.append(&shortened_shortcut_list); + } + } + } + }, + } + if line_part_to_render.len <= max_len { + Some(line_part_to_render) + } else { + None + } +} + +fn full_inline_keys_modes_shortcut_list( + keys_without_common_modifiers: &Vec<KeyShortcut>, + help: &ModeInfo, +) -> LinePart { + let mut full_shortcut_list = LinePart::default(); + for key in keys_without_common_modifiers { + let is_selected = key.is_selected(); + let shortcut = add_shortcut_with_inline_key( + help, + &key.full_text(), + key.key + .as_ref() + .map(|k| vec![k.clone()]) + .unwrap_or_else(|| vec![]), + is_selected, + ); + full_shortcut_list.append(&shortcut); + } + full_shortcut_list +} + +fn shortened_inline_keys_modes_shortcut_list( + keys_without_common_modifiers: &Vec<KeyShortcut>, + help: &ModeInfo, +) -> LinePart { + let mut shortened_shortcut_list = LinePart::default(); + for key in keys_without_common_modifiers { + let is_selected = key.is_selected(); + let shortcut = add_shortcut_with_key_only( + help, + key.key + .as_ref() + .map(|k| vec![k.clone()]) + .unwrap_or_else(|| vec![]), + is_selected, + ); + shortened_shortcut_list.append(&shortcut); + } + shortened_shortcut_list +} + +fn full_modes_shortcut_list(default_keys: &Vec<KeyShortcut>, help: &ModeInfo) -> LinePart { + let mut full_shortcut_list = LinePart::default(); + for key in default_keys { + let is_selected = key.is_selected(); + full_shortcut_list.append(&add_shortcut( + help, + &key.full_text(), + &key.key + .as_ref() + .map(|k| vec![k.clone()]) + .unwrap_or_else(|| vec![]), + is_selected, + Some(3), + )); + } + full_shortcut_list +} + |