diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index 23a6d5869..183aa64f9 100644 --- a/default-plugins/status-bar/src/first_line.rs +++ b/default-plugins/status-bar/src/first_line.rs @@ -8,13 +8,14 @@ use crate::{ }; use crate::{ColoredElements, LinePart}; +#[derive(Debug, Clone, Copy)] struct KeyShortcut { mode: KeyMode, - action: KeyAction, + pub action: KeyAction, key: Option, } -#[derive(PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] enum KeyAction { Lock, Pane, @@ -27,6 +28,7 @@ enum KeyAction { Tmux, } +#[derive(Debug, Clone, Copy)] enum KeyMode { Unselected, UnselectedAlternate, @@ -34,6 +36,20 @@ enum KeyMode { Disabled, } +fn letter_shortcut(key: &Key, with_prefix: bool) -> String { + if with_prefix { + format!("{}", key) + } else { + match key { + Key::F(c) => format!("{}", c), + Key::Ctrl(c) => format!("{}", c), + Key::Char(_) => format!("{}", key), + Key::Alt(c) => format!("{}", c), + _ => String::from("??"), + } + } +} + impl KeyShortcut { pub fn new(mode: KeyMode, action: KeyAction, key: Option) -> Self { KeyShortcut { mode, action, key } @@ -57,17 +73,7 @@ impl KeyShortcut { Some(k) => k, None => return String::from("?"), }; - if with_prefix { - format!("{}", key) - } else { - match key { - Key::F(c) => format!("{}", c), - Key::Ctrl(c) => format!("{}", c), - Key::Char(_) => format!("{}", key), - Key::Alt(c) => format!("{}", c), - _ => String::from("??"), - } - } + letter_shortcut(&key, with_prefix) } } @@ -196,6 +202,58 @@ fn short_mode_shortcut( } } +fn short_key_indicators( + max_len: usize, + keys: &[KeyShortcut], + palette: ColoredElements, + separator: &str, + mode_info: &ModeInfo, + no_super: bool, +) -> LinePart { + let mut line_part = if no_super { + LinePart::default() + } else { + superkey(palette, separator, mode_info) + }; + let shared_super = if no_super { true } else { line_part.len > 0 }; + for ctrl_key in keys { + let line_empty = line_part.len == 0; + let key = short_mode_shortcut(ctrl_key, palette, separator, shared_super, line_empty); + line_part.part = format!("{}{}", line_part.part, key.part); + line_part.len += key.len; + } + if line_part.len < max_len { + return line_part; + } + + // Shortened doesn't fit, print nothing + line_part = LinePart::default(); + line_part +} + +fn full_key_indicators( + keys: &[KeyShortcut], + palette: ColoredElements, + separator: &str, + mode_info: &ModeInfo, + no_super: bool, +) -> LinePart { + // Print full-width hints + let mut line_part = if no_super { + LinePart::default() + } else { + superkey(palette, separator, mode_info) + }; + let shared_super = if no_super { true } else { line_part.len > 0 }; + for ctrl_key in keys { + let line_empty = line_part.len == 0; + let key = long_mode_shortcut(ctrl_key, palette, separator, shared_super, line_empty); + line_part.part = format!("{}{}", line_part.part, key.part); + line_part.len += key.len; + } + line_part +} + fn key_indicators( max_len: usize, keys: &[KeyShortcut], @@ -402,6 +460,111 @@ pub fn superkey(palette: ColoredElements, separator: &str, mode_info: &ModeInfo) } } +fn standby_mode_shortcut_key(help: &ModeInfo) -> Key { + let binds = &help.get_mode_keybinds(); + match help.mode { + InputMode::Locked => to_char(action_key( + binds, + &[Action::SwitchToMode(InputMode::Normal)], + )), + _ => to_char(action_key( + binds, + &[Action::SwitchToMode(InputMode::Locked)], + )), + } + .unwrap_or(Key::Char('?')) +} + +fn standby_mode_ui_indication( + has_shared_super: bool, + standby_mode: &InputMode, + standby_mode_shortcut_key: Key, + colored_elements: ColoredElements, + separator: &str, +) -> LinePart { + let mut line_part = LinePart::default(); + let standby_mode_shortcut = standby_mode_single_letter_selected( + has_shared_super, + standby_mode_shortcut_key, + colored_elements, + separator, + ); + // standby mode text hint + let key_shortcut = KeyShortcut::new( + KeyMode::Unselected, + input_mode_to_key_action(&standby_mode), + None, + ); + let styled_text = colored_elements + .unselected + .styled_text + .paint(format!(" {} ", key_shortcut.full_text())); + let suffix_separator = colored_elements + .unselected + .suffix_separator + .paint(separator); + let standby_mode_text = LinePart { + part: ANSIStrings(&[styled_text, suffix_separator]).to_string(), + len: key_shortcut.full_text().chars().count() + separator.chars().count() + 2, // 2 padding + }; + line_part.part = format!("{}{}", line_part.part, standby_mode_shortcut.part); + line_part.len += standby_mode_shortcut.len; + line_part.part = format!("{}{}", line_part.part, standby_mode_text.part); + line_part.len += standby_mode_text.len; + line_part +} + +fn standby_mode_single_letter_unselected( + has_shared_super: bool, + shortcut_key: Key, + palette: ColoredElements, + separator: &str, +) -> LinePart { + let prefix_separator = palette.unselected.prefix_separator.paint(separator); + let char_shortcut = palette.unselected.char_shortcut.paint(format!( + " {} ", + letter_shortcut(&shortcut_key, has_shared_super) + )); + let suffix_separator = palette.unselected.suffix_separator.paint(separator); + let len = prefix_separator.chars().count() + + char_shortcut.chars().count() + + suffix_separator.chars().count(); + LinePart { + part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(), + len, + } +} + +fn standby_mode_single_letter_selected( + has_shared_super: bool, + shortcut_key: Key, + palette: ColoredElements, + separator: &str, +) -> LinePart { + let prefix_separator = palette + .selected_standby_shortcut + .prefix_separator + .paint(separator); + let char_shortcut = palette + .selected_standby_shortcut + .char_shortcut + .paint(format!( + " {} ", + letter_shortcut(&shortcut_key, has_shared_super) + )); + let suffix_separator = palette + .selected_standby_shortcut + .suffix_separator + .paint(separator); + let len = prefix_separator.chars().count() + + char_shortcut.chars().count() + + suffix_separator.chars().count(); + LinePart { + part: ANSIStrings(&[prefix_separator, char_shortcut, suffix_separator]).to_string(), + len, + } +} + pub fn to_char(kv: Vec) -> Option { let key = kv .iter() @@ -419,6 +582,19 @@ pub fn to_char(kv: Vec) -> Option { key.cloned() } +fn input_mode_to_key_action(input_mode: &InputMode) -> KeyAction { + match input_mode { + InputMode::Normal | InputMode::Prompt | InputMode::Tmux => KeyAction::Lock, + InputMode::Locked => KeyAction::Lock, + InputMode::Pane | InputMode::RenamePane => KeyAction::Pane, + InputMode::Tab | InputMode::RenameTab => KeyAction::Tab, + InputMode::Resize => KeyAction::Resize, + InputMode::Move => KeyAction::Move, + InputMode::Scroll | InputMode::Search | InputMode::EnterSearch => KeyAction::Search, + InputMode::Session => KeyAction::Session, + } +} + /// Get the [`KeyShortcut`] for a specific [`InputMode`]. /// /// Iterates over the contents of `shortcuts` to find the [`KeyShortcut`] with the [`KeyAction`] @@ -449,7 +625,8 @@ fn get_key_shortcut_for_mode<'a>( None } -pub fn first_line( +pub fn first_line_supermode( + standby_mode: &InputMode, help: &ModeInfo, tab_info: Option<&TabInfo>, max_len: usize, @@ -457,9 +634,129 @@ pub fn first_line( ) -> LinePart { let supports_arrow_fonts = !help.capabilities.arrow_fonts; let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts); + + let standby_mode_shortcut_key = standby_mode_shortcut_key(&help); + + let mut line = superkey(colored_elements, separator, help); + let has_shared_super = line.len == 0; + let max_len_without_superkey = max_len.saturating_sub(line.len); + let mut append_to_line = |line_part_to_append: LinePart| { + line.part = format!("{}{}", line.part, line_part_to_append.part); + line.len += line_part_to_append.len; + }; + match help.mode { + InputMode::Locked => { + let standby_mode_shortcut = standby_mode_single_letter_unselected( + has_shared_super, + standby_mode_shortcut_key, + colored_elements, + separator, + ); + append_to_line(standby_mode_shortcut); + line + }, + _ => { + let mut default_keys = generate_default_keys(help); + default_keys.remove(0); // remove locked mode which is not relevant to the supermode ui + + if let Some(position) = default_keys + .iter() + .position(|d| d.action == input_mode_to_key_action(standby_mode)) + { + let standby_mode_ui_ribbon = standby_mode_ui_indication( + has_shared_super, + &standby_mode, + standby_mode_shortcut_key, + colored_elements, + separator, + ); + let first_keybinds: Vec = + default_keys.iter().cloned().take(position).collect(); + let second_keybinds: Vec = + default_keys.iter().cloned().skip(position + 1).collect(); + let first_key_indicators = + full_key_indicators(&first_keybinds, colored_elements, separator, help, true); + let second_key_indicators = + full_key_indicators(&second_keybinds, colored_elements, separator, help, true); + + if first_key_indicators.len + standby_mode_ui_ribbon.len + second_key_indicators.len + < max_len_without_superkey + { + append_to_line(first_key_indicators); + append_to_line(standby_mode_ui_ribbon); + append_to_line(second_key_indicators); + } else { + let length_of_each_half = + max_len_without_superkey.saturating_sub(standby_mode_ui_ribbon.len) / 2; + let first_key_indicators = short_key_indicators( + length_of_each_half, + &first_keybinds, + colored_elements, + separator, + help, + true, + ); + let second_key_indicators = short_key_indicators( + length_of_each_half, + &second_keybinds, + colored_elements, + separator, + help, + true, + ); + append_to_line(first_key_indicators); + append_to_line(standby_mode_ui_ribbon); + append_to_line(second_key_indicators); + } + if line.len < max_len { + if let Some(tab_info) = tab_info { + let remaining_space = max_len.saturating_sub(line.len); + line.append(&swap_layout_status_and_padding( + &tab_info, + remaining_space, + separator, + colored_elements, + help, + )); + } + } + } + line + }, + } +} + +fn swap_layout_status_and_padding( + tab_info: &TabInfo, + mut remaining_space: usize, + separator: &str, + colored_elements: ColoredElements, + help: &ModeInfo, +) -> LinePart { + let mut line = LinePart::default(); + if let Some(swap_layout_status) = swap_layout_status( + remaining_space, + &tab_info.active_swap_layout_name, + tab_info.is_swap_layout_dirty, + help, + colored_elements, + &help.style.colors, + separator, + ) { + remaining_space -= swap_layout_status.len; + for _ in 0..remaining_space { + line.part + .push_str(&ANSIStrings(&[colored_elements.superkey_prefix.paint(" ")]).to_string()); + line.len += 1; + } + line.append(&swap_layout_status); + } + line +} + +fn generate_default_keys(help: &ModeInfo) -> Vec { let binds = &help.get_mode_keybinds(); - // Unselect all by default - let mut default_keys = vec![ + vec![ KeyShortcut::new( KeyMode::Unselected, KeyAction::Lock, @@ -512,7 +809,20 @@ pub fn first_line( KeyAction::Quit, to_char(action_key(binds, &[Action::Quit])), ), - ]; + ] +} + +pub fn first_line( + help: &ModeInfo, + tab_info: Option<&TabInfo>, + max_len: usize, + separator: &str, +) -> LinePart { + let supports_arrow_fonts = !help.capabilities.arrow_fonts; + let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts); + let binds = &help.get_mode_keybinds(); + // Unselect all by default + let mut default_keys = generate_default_keys(help); // TODO: check that this still works if let Some(key_shortcut) = get_key_shortcut_for_mode(&mut default_keys, &help.mode) { key_shortcut.mode = KeyMode::Selected; @@ -539,25 +849,14 @@ pub fn first_line( key_indicators(max_len, &default_keys, colored_elements, separator, help); if key_indicators.len < max_len { if let Some(tab_info) = tab_info { - let mut remaining_space = max_len - key_indicators.len; - if let Some(swap_layout_status) = swap_layout_status( + let remaining_space = max_len - key_indicators.len; + key_indicators.append(&swap_layout_status_and_padding( + &tab_info, remaining_space, - &tab_info.active_swap_layout_name, - tab_info.is_swap_layout_dirty, - help, - colored_elements, - &help.style.colors, separator, - ) { - remaining_space -= swap_layout_status.len; - for _ in 0..remaining_space { - key_indicators.part.push_str( - &ANSIStrings(&[colored_elements.superkey_prefix.paint(" ")]).to_string(), - ); - key_indicators.len += 1; - } - key_indicators.append(&swap_layout_status); - } + colored_elements, + help, + )); } } key_indicators diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 3e6685596..ee14e45d5 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -13,10 +13,9 @@ use zellij_tile::prelude::actions::Action; use zellij_tile::prelude::*; use zellij_tile_utils::{palette_match, style}; -use first_line::first_line; +use first_line::{first_line, first_line_supermode}; 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, + floating_panes_are_visible, fullscreen_panes_to_hide, keybinds, system_clipboard_error, text_copied_hint, }; use tip::utils::get_cached_tip_name; @@ -34,6 +33,10 @@ struct State { mode_info: ModeInfo, text_copy_destination: Option, display_system_clipboard_failure: bool, + + supermode: bool, + standby_mode: InputMode, + current_mode: InputMode, } register_plugin!(State); @@ -60,6 +63,7 @@ impl Display for LinePart { #[derive(Clone, Copy)] pub struct ColoredElements { pub selected: SegmentStyle, + pub selected_standby_shortcut: SegmentStyle, pub unselected: SegmentStyle, pub unselected_alternate: SegmentStyle, pub disabled: SegmentStyle, @@ -109,6 +113,14 @@ fn color_elements(palette: Palette, different_color_alternates: bool) -> Colored styled_text: style!(background, palette.green).bold(), suffix_separator: style!(palette.green, background).bold(), }, + selected_standby_shortcut: SegmentStyle { + prefix_separator: style!(background, palette.green), + char_left_separator: style!(background, palette.green).bold(), + char_shortcut: style!(palette.red, palette.green).bold(), + char_right_separator: style!(background, palette.green).bold(), + styled_text: style!(background, palette.green).bold(), + suffix_separator: style!(palette.green, palette.fg).bold(), + }, unselected: SegmentStyle { prefix_separator: style!(background, palette.fg), char_left_separator: style!(background, palette.fg).bold(), @@ -145,6 +157,14 @@ fn color_elements(palette: Palette, different_color_alternates: bool) -> Colored styled_text: style!(background, palette.green).bold(), suffix_separator: style!(palette.green, background).bold(), }, + selected_standby_shortcut: SegmentStyle { + prefix_separator: style!(background, palette.green), + char_left_separator: style!(background, palette.green).bold(), + char_shortcut: style!(palette.red, palette.green).bold(), + char_right_separator: style!(background, palette.green).bold(), + styled_text: style!(background, palette.green).bold(), + suffix_separator: style!(palette.green, palette.fg).bold(), + }, unselected: SegmentStyle { prefix_separator: style!(background, palette.fg), char_left_separator: style!(background, palette.fg).bold(), @@ -187,12 +207,44 @@ impl ZellijPlugin for State { EventType::InputReceived, EventType::SystemClipboardFailure, ]); + self.supermode = false; // TODO: from config + self.standby_mode = InputMode::Pane; + if self.supermode { + switch_to_input_mode(&InputMode::Locked); // supermode should start locked (TODO: only + // once per app load, let's not do this on + // each tab loading) + } } fn update(&mut self, event: Event) -> bool { let mut should_render = false; match event { Event::ModeUpdate(mode_info) => { + if self.supermode { + // supermode is a "sticky" mode that is not Normal or Locked + // using this configuration, we default to Locked mode in order to avoid key + // collisions with terminal applications + // whenever the user switches away from locked mode, we make sure to place them + // in the standby mode + // whenever the user switches to a mode that is not locked or normal, we set + // that mode as the standby mode + // whenever the user switches away from the standby mode, we palce them in + // normal mode + if mode_info.mode == InputMode::Normal && self.current_mode == InputMode::Locked + { + switch_to_input_mode(&self.standby_mode); + } else if mode_info.mode == InputMode::Normal + && self.current_mode == self.standby_mode + { + switch_to_input_mode(&InputMode::Locked); + } else if mode_info.mode != InputMode::Locked + && mode_info.mode != InputMode::Normal + { + self.standby_mode = mode_info.mode; + } + self.current_mode = mode_info.mode; + } + if self.mode_info != mode_info { should_render = true; } @@ -244,7 +296,17 @@ impl ZellijPlugin for State { }; let active_tab = self.tabs.iter().find(|t| t.active); - let first_line = first_line(&self.mode_info, active_tab, cols, separator); + let first_line = if self.supermode { + first_line_supermode( + &self.standby_mode, + &self.mode_info, + active_tab, + cols, + separator, + ) + } else { + first_line(&self.mode_info, active_tab, cols, separator) + }; let second_line = self.second_line(cols); let background = match self.mode_info.style.colors.theme_hue { @@ -296,11 +358,7 @@ impl State { } else if let Some(active_tab) = active_tab { if active_tab.is_fullscreen_active { match self.mode_info.mode { - InputMode::Normal => fullscreen_panes_to_hide( - &self.mode_info.style.colors, - active_tab.panes_to_hide, - ), - InputMode::Locked => locked_fullscreen_panes_to_hide( + InputMode::Normal | InputMode::Locked => fullscreen_panes_to_hide( &self.mode_info.style.colors, active_tab.panes_to_hide, ), @@ -308,9 +366,8 @@ impl State { } } else if active_tab.are_floating_panes_visible { match self.mode_info.mode { - InputMode::Normal => floating_panes_are_visible(&self.mode_info), - InputMode::Locked => { - locked_floating_panes_are_visible(&self.mode_info.style.colors) + InputMode::Normal | InputMode::Locked => { + floating_panes_are_visible(&self.mode_info) }, _ => keybinds(&self.mode_info, &self.tip_name, cols), } diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs index b7c8a9cec..015618f06 100644 --- a/default-plugins/status-bar/src/second_line.rs +++ b/default-plugins/status-bar/src/second_line.rs @@ -45,20 +45,6 @@ fn full_length_shortcut( } } -fn locked_interface_indication(palette: Palette) -> LinePart { - let locked_text = " -- INTERFACE LOCKED -- "; - let locked_text_len = locked_text.chars().count(); - let text_color = palette_match!(match palette.theme_hue { - ThemeHue::Dark => palette.white, - ThemeHue::Light => palette.black, - }); - let locked_styled_text = Style::new().fg(text_color).bold().paint(locked_text); - LinePart { - part: locked_styled_text.to_string(), - len: locked_text_len, - } -} - fn add_shortcut(help: &ModeInfo, linepart: &LinePart, text: &str, keys: Vec) -> LinePart { let shortcut = if linepart.len == 0 { full_length_shortcut(true, keys, text, help.style.colors) @@ -144,21 +130,17 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { } if mi.mode == IM::Pane { vec![ - (s("Move focus"), s("Move"), + (s("New"), s("New"), action_key(&km, &[A::NewPane(None, None), TO_NORMAL])), + (s("Change Focus"), s("Move"), action_key_group(&km, &[&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)], &[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])), - (s("New"), s("New"), action_key(&km, &[A::NewPane(None, None), TO_NORMAL])), (s("Close"), s("Close"), action_key(&km, &[A::CloseFocus, TO_NORMAL])), (s("Rename"), s("Rename"), action_key(&km, &[A::SwitchToMode(IM::RenamePane), A::PaneNameInput(vec![0])])), - (s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])), - (s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])), - (s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])), - (s("Frames"), s("Frames"), action_key(&km, &[A::TogglePaneFrames, TO_NORMAL])), - (s("Floating toggle"), s("Floating"), + (s("Toggle Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])), + (s("Toggle Floating"), s("Floating"), action_key(&km, &[A::ToggleFloatingPanes, TO_NORMAL])), - (s("Embed pane"), s("Embed"), action_key(&km, &[A::TogglePaneEmbedOrFloating, TO_NORMAL])), - (s("Next"), s("Next"), action_key(&km, &[A::SwitchFocus])), + (s("Toggle Embed"), s("Embed"), action_key(&km, &[A::TogglePaneEmbedOrFloating, TO_NORMAL])), (s("Select pane"), s("Select"), to_normal_key), ]} else if mi.mode == IM::Tab { // With the default bindings, "Move focus" for tabs is tricky: It binds all the arrow keys @@ -178,8 +160,8 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { }; vec![ - (s("Move focus"), s("Move"), focus_keys), (s("New"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])), + (s("Change focus"), s("Move"), focus_keys), (s("Close"), s("Close"), action_key(&km, &[A::CloseTab, TO_NORMAL])), (s("Rename"), s("Rename"), action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])), @@ -187,6 +169,11 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { (s("Toggle"), s("Toggle"), action_key(&km, &[A::ToggleTab])), (s("Select pane"), s("Select"), to_normal_key), ]} else if mi.mode == IM::Resize { vec![ + (s("Increase/Decrease size"), s("Increase/Decrease"), + action_key_group(&km, &[ + &[A::Resize(Resize::Increase, None)], + &[A::Resize(Resize::Decrease, None)] + ])), (s("Increase to"), s("Increase"), action_key_group(&km, &[ &[A::Resize(Resize::Increase, Some(Dir::Left))], &[A::Resize(Resize::Increase, Some(Dir::Down))], @@ -199,19 +186,14 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { &[A::Resize(Resize::Decrease, Some(Dir::Up))], &[A::Resize(Resize::Decrease, Some(Dir::Right))] ])), - (s("Increase/Decrease size"), s("Increase/Decrease"), - action_key_group(&km, &[ - &[A::Resize(Resize::Increase, None)], - &[A::Resize(Resize::Decrease, None)] - ])), (s("Select pane"), s("Select"), to_normal_key), ]} else if mi.mode == IM::Move { vec![ - (s("Move"), s("Move"), action_key_group(&km, &[ + (s("Switch Location"), s("Move"), action_key_group(&km, &[ &[Action::MovePane(Some(Dir::Left))], &[Action::MovePane(Some(Dir::Down))], &[Action::MovePane(Some(Dir::Up))], &[Action::MovePane(Some(Dir::Right))]])), - (s("Next pane"), s("Next"), action_key(&km, &[Action::MovePane(None)])), - (s("Previous pane"), s("Previous"), action_key(&km, &[Action::MovePaneBackwards])), ]} else if mi.mode == IM::Scroll { vec![ + (s("Enter search term"), s("Search"), + action_key(&km, &[A::SwitchToMode(IM::EnterSearch), A::SearchInput(vec![0])])), (s("Scroll"), s("Scroll"), action_key_group(&km, &[&[Action::ScrollDown], &[Action::ScrollUp]])), (s("Scroll page"), s("Scroll"), @@ -220,22 +202,20 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { action_key_group(&km, &[&[Action::HalfPageScrollDown], &[Action::HalfPageScrollUp]])), (s("Edit scrollback in default editor"), s("Edit"), action_key(&km, &[Action::EditScrollback, TO_NORMAL])), - (s("Enter search term"), s("Search"), - action_key(&km, &[A::SwitchToMode(IM::EnterSearch), A::SearchInput(vec![0])])), (s("Select pane"), s("Select"), to_normal_key), ]} else if mi.mode == IM::EnterSearch { vec![ (s("When done"), s("Done"), action_key(&km, &[A::SwitchToMode(IM::Search)])), (s("Cancel"), s("Cancel"), action_key(&km, &[A::SearchInput(vec![27]), A::SwitchToMode(IM::Scroll)])), ]} else if mi.mode == IM::Search { vec![ + (s("Enter Search term"), s("Search"), + action_key(&km, &[A::SwitchToMode(IM::EnterSearch), A::SearchInput(vec![0])])), (s("Scroll"), s("Scroll"), action_key_group(&km, &[&[Action::ScrollDown], &[Action::ScrollUp]])), (s("Scroll page"), s("Scroll"), action_key_group(&km, &[&[Action::PageScrollDown], &[Action::PageScrollUp]])), (s("Scroll half page"), s("Scroll"), action_key_group(&km, &[&[Action::HalfPageScrollDown], &[Action::HalfPageScrollUp]])), - (s("Enter term"), s("Search"), - action_key(&km, &[A::SwitchToMode(IM::EnterSearch), A::SearchInput(vec![0])])), (s("Search down"), s("Down"), action_key(&km, &[A::Search(SDir::Down)])), (s("Search up"), s("Up"), action_key(&km, &[A::Search(SDir::Up)])), (s("Case sensitive"), s("Case"), @@ -270,8 +250,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)> { fn full_shortcut_list(help: &ModeInfo, tip: TipFn) -> LinePart { match help.mode { - InputMode::Normal => tip(help), - InputMode::Locked => locked_interface_indication(help.style.colors), + InputMode::Normal | InputMode::Locked => tip(help), _ => full_shortcut_list_nonstandard_mode(help), } } @@ -288,8 +267,7 @@ fn shortened_shortcut_list_nonstandard_mode(help: &ModeInfo) -> LinePart { fn shortened_shortcut_list(help: &ModeInfo, tip: TipFn) -> LinePart { match help.mode { - InputMode::Normal => tip(help), - InputMode::Locked => locked_interface_indication(help.style.colors), + InputMode::Normal | InputMode::Locked => tip(help), _ => shortened_shortcut_list_nonstandard_mode(help), } } @@ -312,7 +290,7 @@ fn best_effort_shortcut_list_nonstandard_mode(help: &ModeInfo, max_len: usize) - fn best_effort_shortcut_list(help: &ModeInfo, tip: TipFn, max_len: usize) -> LinePart { match help.mode { - InputMode::Normal => { + InputMode::Normal | InputMode::Locked => { let line_part = tip(help); if line_part.len <= max_len { line_part @@ -320,14 +298,6 @@ fn best_effort_shortcut_list(help: &ModeInfo, tip: TipFn, max_len: usize) -> Lin LinePart::default() } }, - InputMode::Locked => { - let line_part = locked_interface_indication(help.style.colors); - if line_part.len <= max_len { - line_part - } else { - LinePart::default() - } - }, _ => best_effort_shortcut_list_nonstandard_mode(help, max_len), } } @@ -472,68 +442,6 @@ pub fn floating_panes_are_visible(mode_info: &ModeInfo) -> LinePart { } } -pub fn locked_fullscreen_panes_to_hide(palette: &Palette, panes_to_hide: usize) -> LinePart { - let text_color = palette_match!(match palette.theme_hue { - ThemeHue::Dark => palette.white, - ThemeHue::Light => palette.black, - }); - let green_color = palette_match!(palette.green); - let orange_color = palette_match!(palette.orange); - let locked_text = " -- INTERFACE LOCKED -- "; - let shortcut_left_separator = Style::new().fg(text_color).bold().paint(" ("); - let shortcut_right_separator = Style::new().fg(text_color).bold().paint("): "); - let fullscreen = "FULLSCREEN"; - let puls = "+ "; - let panes = panes_to_hide.to_string(); - let hide = " hidden panes"; - let len = locked_text.chars().count() - + fullscreen.chars().count() - + puls.chars().count() - + panes.chars().count() - + hide.chars().count() - + 5; // 3 for ():'s around shortcut, 2 for the space - LinePart { - part: format!( - "{}{}{}{}{}{}{}", - Style::new().fg(text_color).bold().paint(locked_text), - shortcut_left_separator, - Style::new().fg(orange_color).bold().paint(fullscreen), - shortcut_right_separator, - Style::new().fg(text_color).bold().paint(puls), - Style::new().fg(green_color).bold().paint(panes), - Style::new().fg(text_color).bold().paint(hide) - ), - len, - } -} - -pub fn locked_floating_panes_are_visible(palette: &Palette) -> LinePart { - let white_color = match palette.white { - PaletteColor::Rgb((r, g, b)) => RGB(r, g, b), - PaletteColor::EightBit(color) => Fixed(color), - }; - let orange_color = match palette.orange { - PaletteColor::Rgb((r, g, b)) => RGB(r, g, b), - PaletteColor::EightBit(color) => Fixed(color), - }; - let shortcut_left_separator = Style::new().fg(white_color).bold().paint(" ("); - let shortcut_right_separator = Style::new().fg(white_color).bold().paint(")"); - let locked_text = " -- INTERFACE LOCKED -- "; - let floating_panes = "FLOATING PANES VISIBLE"; - - let len = locked_text.chars().count() + floating_panes.chars().count(); - LinePart { - part: format!( - "{}{}{}{}", - Style::new().fg(white_color).bold().paint(locked_text), - shortcut_left_separator, - Style::new().fg(orange_color).bold().paint(floating_panes), - shortcut_right_separator, - ), - len, - } -} - #[cfg(test)] /// Unit tests. /// @@ -668,7 +576,6 @@ mod tests { assert_eq!(ret, " / Ctrl + Foobar"); } - //pub fn keybinds(help: &ModeInfo, tip_name: &str, max_width: usize) -> LinePart { #[test] // Note how it leaves out elements that don't exist! @@ -698,7 +605,7 @@ mod tests { assert_eq!( ret, - " <←↓↑→> Move focus / New / Close / Fullscreen" + " New / <←↓↑→> Change Focus / Close / Toggle Fullscreen", ); } @@ -728,7 +635,7 @@ mod tests { let ret = keybinds(&mode_info, "quicknav", 35); let ret = unstyle(ret); - assert_eq!(ret, " <←↓↑→> Move / New ... "); + assert_eq!(ret, " New / <←↓↑→> Move ... "); } #[test] @@ -753,6 +660,9 @@ mod tests { let ret = keybinds(&mode_info, "quicknav", 500); let ret = unstyle(ret); - assert_eq!(ret, " Ctrl + Move focus / New / Close / Fullscreen"); + assert_eq!( + ret, + " New / Ctrl + Change Focus / Close / Toggle Fullscreen" + ); } } diff --git a/default-plugins/status-bar/src/tip/data/compact_layout.rs b/default-plugins/status-bar/src/tip/data/compact_layout.rs index 77dec8a60..13ee18bd3 100644 --- a/default-plugins/status-bar/src/tip/data/compact_layout.rs +++ b/default-plugins/status-bar/src/tip/data/compact_layout.rs @@ -77,7 +77,7 @@ pub fn compact_layout_short(help: &ModeInfo) -> LinePart { fn add_keybinds(help: &ModeInfo) -> Vec { let to_pane = action_key( - &help.get_mode_keybinds(), + &help.get_keybinds_for_mode(InputMode::Normal), &[Action::SwitchToMode(InputMode::Pane)], ); let pane_frames = action_key( diff --git a/default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs b/default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs index ebd944b77..9ff210ede 100644 --- a/default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs +++ b/default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs @@ -67,7 +67,7 @@ pub fn edit_scrollbuffer_short(help: &ModeInfo) -> LinePart { fn add_keybinds(help: &ModeInfo) -> Vec { let to_pane = action_key( - &help.get_mode_keybinds(), + &help.get_keybinds_for_mode(InputMode::Normal), &[Action::SwitchToMode(InputMode::Scroll)], ); let edit_buffer = action_key( diff --git a/default-plugins/status-bar/src/tip/data/floating_panes_mouse.rs b/default-plugins/status-bar/src/tip/data/floating_panes_mouse.rs index 4bf4a143d..17439c4bf 100644 --- a/default-plugins/status-bar/src/tip/data/floating_panes_mouse.rs +++ b/default-plugins/status-bar/src/tip/data/floating_panes_mouse.rs @@ -46,7 +46,7 @@ pub fn floating_panes_mouse_short(help: &ModeInfo) -> LinePart { fn add_keybinds(help: &ModeInfo) -> Vec { let to_pane = action_key( - &help.get_mode_keybinds(), + &help.get_keybinds_for_mode(InputMode::Normal), &[Action::SwitchToMode(InputMode::Pane)], ); let floating_toggle = action_key( diff --git a/default-plugins/status-bar/src/tip/data/quicknav.rs b/default-plugins/status-bar/src/tip/data/quicknav.rs index 318fe7025..fe118f2c4 100644 --- a/default-plugins/status-bar/src/tip/data/quicknav.rs +++ b/default-plugins/status-bar/src/tip/data/quicknav.rs @@ -61,7 +61,7 @@ struct Keygroups<'a> { } fn add_keybinds(help: &ModeInfo) -> Keygroups { - let normal_keymap = help.get_mode_keybinds(); + let normal_keymap = help.get_keybinds_for_mode(InputMode::Normal); let new_pane_keys = action_key(&normal_keymap, &[Action::NewPane(None, None)]); let new_pane = if new_pane_keys.is_empty() { vec![Style::new().bold().paint("UNBOUND")] diff --git a/default-plugins/status-bar/src/tip/data/sync_tab.rs b/default-plugins/status-bar/src/tip/data/sync_tab.rs index 486e2aa24..71a4ed50a 100644 --- a/default-plugins/status-bar/src/tip/data/sync_tab.rs +++ b/default-plugins/status-bar/src/tip/data/sync_tab.rs @@ -45,7 +45,7 @@ pub fn sync_tab_short(help: &ModeInfo) -> LinePart { fn add_keybinds(help: &ModeInfo) -> Vec { let to_tab = action_key( - &help.get_mode_keybinds(), + &help.get_keybinds_for_mode(InputMode::Normal), &[Action::SwitchToMode(InputMode::Tab)], ); let sync_tabs = action_key( diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index b34138b95..b411b4ea9 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -808,7 +808,7 @@ pub fn lock_mode() { name: "Send keys that should not be intercepted by the app", instruction: |mut remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.snapshot_contains("INTERFACE LOCKED") { + if remote_terminal.snapshot_contains("<> PANE") { remote_terminal.send_key(&TAB_MODE); remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); remote_terminal.send_key("abc".as_bytes()); diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap index e6d38062e..209b67791 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 836 +assertion_line: 840 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK  <> PANE  <> TAB  <> RESIZE  <> MOVE  <> SEARCH  <> SESSION  <> QUIT  - -- INTERFACE LOCKED -- + Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap index 34256a877..205ff40c7 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions-2.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1341 +assertion_line: 1354 expression: second_runner_snapshot --- Zellij (mirrored_sessions)  Tab #1  Tab #2  @@ -26,4 +26,4 @@ expression: second_runner_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - <←→> Move focus / New / Close / Rename / Sync / Toggle / Select pane + New / <←→> Change focus / Close / Rename / Sync / Toggle / Select pane diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap index 524969c40..e1d36e7cc 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 290 +assertion_line: 305 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ ││li█e21 │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - <↓↑> Scroll / Scroll / Scroll / Edit / Search / Select + Search / <↓↑> Scroll / Scroll / Scroll / Edit / Select