summaryrefslogtreecommitdiffstats
path: root/default-plugins/status-bar/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-07-05 15:13:51 +0200
committerGitHub <noreply@github.com>2024-07-05 15:13:51 +0200
commita6d6c0e4ff03c35cafb6f04363ecab856ee3682d (patch)
treeeadf1c24c4c6a1e4b7499b3cd047351fd29e52de /default-plugins/status-bar/src
parent8e33b20559ecddcaba17e5cc8d6f05b638718cac (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.rs47
-rw-r--r--default-plugins/status-bar/src/main.rs30
-rw-r--r--default-plugins/status-bar/src/one_line_ui.rs1440
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
+}
+