feat(ui): new status bar mode (#2619)

* supermode prototype

* fix integration tests

* fix tests

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2023-07-12 11:31:00 +02:00 committed by GitHub
parent 61f3789c88
commit 27763d26ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 439 additions and 173 deletions

View File

@ -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<Key>,
}
#[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<Key>) -> 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<Key>) -> Option<Key> {
let key = kv
.iter()
@ -419,6 +582,19 @@ pub fn to_char(kv: Vec<Key>) -> Option<Key> {
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<KeyShortcut> =
default_keys.iter().cloned().take(position).collect();
let second_keybinds: Vec<KeyShortcut> =
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<KeyShortcut> {
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

View File

@ -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<CopyDestination>,
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),
}

View File

@ -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<Key>) -> 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<Key>)> {
}
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<Key>)> {
};
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<Key>)> {
(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<Key>)> {
&[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<Key>)> {
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<Key>)> {
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 + <a|b|c> 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 / <n> New / <x> Close / <f> Fullscreen"
" <n> New / <←↓↑→> Change Focus / <x> Close / <f> Toggle Fullscreen",
);
}
@ -728,7 +635,7 @@ mod tests {
let ret = keybinds(&mode_info, "quicknav", 35);
let ret = unstyle(ret);
assert_eq!(ret, " <←↓↑→> Move / <n> New ... ");
assert_eq!(ret, " <n> New / <←↓↑→> Move ... ");
}
#[test]
@ -753,6 +660,9 @@ mod tests {
let ret = keybinds(&mode_info, "quicknav", 500);
let ret = unstyle(ret);
assert_eq!(ret, " Ctrl + <a|ENTER|1|SPACE> Move focus / <BACKSPACE> New / <ESC> Close / <END> Fullscreen");
assert_eq!(
ret,
" <BACKSPACE> New / Ctrl + <a|ENTER|1|SPACE> Change Focus / <ESC> Close / <END> Toggle Fullscreen"
);
}
}

View File

@ -77,7 +77,7 @@ pub fn compact_layout_short(help: &ModeInfo) -> LinePart {
fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
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(

View File

@ -67,7 +67,7 @@ pub fn edit_scrollbuffer_short(help: &ModeInfo) -> LinePart {
fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
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(

View File

@ -46,7 +46,7 @@ pub fn floating_panes_mouse_short(help: &ModeInfo) -> LinePart {
fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
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(

View File

@ -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")]

View File

@ -45,7 +45,7 @@ pub fn sync_tab_short(help: &ModeInfo) -> LinePart {
fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
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(

View File

@ -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());

View File

@ -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 + <g> LOCK  <> PANE  <> TAB  <> RESIZE  <> MOVE  <> SEARCH  <> SESSION  <> QUIT 
-- INTERFACE LOCKED --
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.

View File

@ -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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
<←→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <TAB> Toggle / <ENTER> Select pane
<n> New / <←→> Change focus / <x> Close / <r> Rename / <s> Sync / <TAB> Toggle / <ENTER> Select pane

View File

@ -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 + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
<↓↑> Scroll / <PgDn|PgUp> Scroll / <d|u> Scroll / <e> Edit / <s> Search / <ENTER> Select
<s> Search / <↓↑> Scroll / <PgDn|PgUp> Scroll / <d|u> Scroll / <e> Edit / <ENTER> Select