diff options
author | Aram Drevekenin <aram@poor.dev> | 2023-02-17 12:05:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-17 12:05:50 +0100 |
commit | f1ff272b0b65f6d328fef24531ada67ea585ce85 (patch) | |
tree | 0e33aa4a1af21c0785fa30118ee42a21972497cf /default-plugins/status-bar/src | |
parent | 1517036c2489b2a7dc43230e2ba8a05841a69dbe (diff) |
feat(ui): swap layouts and stacked panes (#2167)
* relayout working with hard coded layout
* work
* refactor(layout): PaneLayout => TiledPaneLayout
* tests passing
* tests passing
* tests passing
* stacked panes and passing tests
* tests for stacked panes
* refactor(panes): stacked panes
* fix: focusing into stacked panes from the left/right
* fix(layouts): handle stacked layouts in the middle of the screen
* fix(pane-stack): focus correctly when coming to stack from above/below
* fix(stacked-panes): resize stack
* fix(stacked-panes): focus with mouse
* fix(stacked-panes): focus next pane
* fix(layout-applier): sane focus order
* fix(stacked-panes): better titles for one-liners
* fix(stacked-panes): handle moving pane location in stack
* fix(relayout): properly calculate display area
* fix(relayout): properly calculate rounding errors
* fix(stacked-panes): properly handle closing a pane near a stack
* fix(swap-layouts): adjust swap layout sort order
* feat(swap-layouts): ui + ux
* fix(swap-layouts): include base layout
* refactor(layout): remove unused method
* fix(swap-layouts): respect pane contents and focus
* work
* fix(swap-layouts): load swap layouts from external file
* fix(swap-layouts): properly truncate layout children
* fix(stacked-panes): allow stacked panes to become fullscreen
* fix(swap-layouts): work with multiple tabs
* fix(swap-layouts): embed/eject panes properly with auto-layout
* fix(stacked-panes): close last pane in stack
* fix(stacked-panes): move focus for all clients in stack
* fix(floating-panes): set layout damaged when moving panes
* fix(relayout): move out of unfitting layout when resizing whole tab
* fix(ui): background color for swap layout indicator
* fix(keybinds): add switch next layout in tmux
* fix(ui): swap layout indication in compact layout
* fix(compact): correct swap constraint
* fix(tests): tmux swap config shortcut
* fix(resizes): cache resizes so as not to confuse panes (eg. vim) with multiple resizes that it debounces weirdly
* feat(cli): dump swap layouts
* fix(ui): stacked panes without pane frames
* fix(ux): move pane forward/backwards also with floating panes
* refactor(lint): remove unused stuff
* refactor(tab): move swap layouts to separate file
* style(fmt): rustfmt
* style(fmt): rustfmt
* refactor(panes): various cleanups
* chore(deps): upgrade termwiz to get alt left-bracket
* fix(assets): merge conflicts of binary files
* style(fmt): rustfmt
* style(clippy): no thank you!
* chore(repo): remove garbage file
Diffstat (limited to 'default-plugins/status-bar/src')
9 files changed, 250 insertions, 45 deletions
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index ae8fc70e7..23a6d5869 100644 --- a/default-plugins/status-bar/src/first_line.rs +++ b/default-plugins/status-bar/src/first_line.rs @@ -1,9 +1,11 @@ -use ansi_term::ANSIStrings; +use ansi_term::{unstyled_len, ANSIStrings}; use zellij_tile::prelude::actions::Action; use zellij_tile::prelude::*; use crate::color_elements; -use crate::{action_key, get_common_modifier, TO_NORMAL}; +use crate::{ + action_key, action_key_group, get_common_modifier, style_key_with_modifier, TO_NORMAL, +}; use crate::{ColoredElements, LinePart}; struct KeyShortcut { @@ -232,6 +234,102 @@ fn key_indicators( line_part } +fn swap_layout_keycode(mode_info: &ModeInfo, palette: &Palette) -> LinePart { + let mode_keybinds = mode_info.get_mode_keybinds(); + let prev_next_keys = action_key_group( + &mode_keybinds, + &[&[Action::PreviousSwapLayout], &[Action::NextSwapLayout]], + ); + let prev_next_keys_indicator = + style_key_with_modifier(&prev_next_keys, palette, Some(palette.black)); + let keycode = ANSIStrings(&prev_next_keys_indicator); + let len = unstyled_len(&keycode); + let part = keycode.to_string(); + LinePart { part, len } +} + +fn swap_layout_status( + max_len: usize, + swap_layout_name: &Option<String>, + is_swap_layout_damaged: bool, + mode_info: &ModeInfo, + colored_elements: ColoredElements, + palette: &Palette, + separator: &str, +) -> Option<LinePart> { + match swap_layout_name { + Some(swap_layout_name) => { + let mut swap_layout_name = format!(" {} ", swap_layout_name); + swap_layout_name.make_ascii_uppercase(); + let keycode = swap_layout_keycode(mode_info, palette); + let swap_layout_name_len = swap_layout_name.len() + 3; // 2 for the arrow separators, one for the screen end buffer + // + macro_rules! style_swap_layout_indicator { + ($style_name:ident) => {{ + ( + colored_elements + .$style_name + .prefix_separator + .paint(separator), + colored_elements + .$style_name + .styled_text + .paint(&swap_layout_name), + colored_elements + .$style_name + .suffix_separator + .paint(separator), + ) + }}; + } + let (prefix_separator, swap_layout_name, suffix_separator) = + if mode_info.mode == InputMode::Locked { + style_swap_layout_indicator!(disabled) + } else if is_swap_layout_damaged { + style_swap_layout_indicator!(unselected) + } else { + style_swap_layout_indicator!(selected) + }; + let swap_layout_indicator = format!( + "{}{}{}", + prefix_separator, swap_layout_name, suffix_separator + ); + let (part, full_len) = if mode_info.mode == InputMode::Locked { + ( + format!("{}", swap_layout_indicator), + swap_layout_name_len, // 1 is the space between + ) + } else { + ( + format!( + "{}{}{}{}", + keycode, + colored_elements.superkey_prefix.paint(" "), + swap_layout_indicator, + colored_elements.superkey_prefix.paint(" ") + ), + keycode.len + swap_layout_name_len + 1, // 1 is the space between + ) + }; + let short_len = swap_layout_name_len + 1; // 1 is the space between + if full_len <= max_len { + Some(LinePart { + part, + len: full_len, + }) + } else if short_len <= max_len && mode_info.mode != InputMode::Locked { + Some(LinePart { + part: swap_layout_indicator, + len: short_len, + }) + } else { + None + } + }, + None => None, + } +} + /// Get the keybindings for switching `InputMode`s and `Quit` visible in status bar. /// /// Return a Vector of `Key`s where each `Key` is a shortcut to switch to some `InputMode` or Quit @@ -351,7 +449,12 @@ fn get_key_shortcut_for_mode<'a>( None } -pub fn first_line(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart { +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(); @@ -432,7 +535,32 @@ pub fn first_line(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart )); } - key_indicators(max_len, &default_keys, colored_elements, separator, help) + let mut key_indicators = + 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( + 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); + } + } + } + key_indicators } #[cfg(test)] @@ -735,7 +863,7 @@ mod tests { ..ModeInfo::default() }; - let ret = first_line(&mode_info, 500, ">"); + let ret = first_line(&mode_info, None, 500, ">"); let ret = unstyle(ret); assert_eq!( @@ -759,7 +887,7 @@ mod tests { ..ModeInfo::default() }; - let ret = first_line(&mode_info, 500, ">"); + let ret = first_line(&mode_info, None, 500, ">"); let ret = unstyle(ret); assert_eq!( @@ -785,7 +913,7 @@ mod tests { ..ModeInfo::default() }; - let ret = first_line(&mode_info, 500, ">"); + let ret = first_line(&mode_info, None, 500, ">"); let ret = unstyle(ret); assert_eq!( @@ -812,7 +940,7 @@ mod tests { ..ModeInfo::default() }; - let ret = first_line(&mode_info, 50, ">"); + let ret = first_line(&mode_info, None, 50, ">"); let ret = unstyle(ret); assert_eq!(ret, " Ctrl + >> a >> b >> c >> d >> e >".to_string()); @@ -833,7 +961,7 @@ mod tests { ..ModeInfo::default() }; - let ret = first_line(&mode_info, 30, ""); + let ret = first_line(&mode_info, None, 30, ""); let ret = unstyle(ret); assert_eq!(ret, " Ctrl + a b c ".to_string()); diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 603cdf351..3e6685596 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -44,6 +44,13 @@ pub struct LinePart { len: usize, } +impl LinePart { + pub fn append(&mut self, to_append: &LinePart) { + self.part.push_str(&to_append.part); + self.len += to_append.len; + } +} + impl Display for LinePart { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { write!(f, "{}", self.part) @@ -236,7 +243,8 @@ impl ZellijPlugin for State { "" }; - let first_line = first_line(&self.mode_info, cols, separator); + 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); let background = match self.mode_info.style.colors.theme_hue { @@ -396,7 +404,11 @@ pub fn action_key_group(keymap: &[(Key, Vec<Action>)], actions: &[&[Action]]) -> /// /// The returned Vector of [`ANSIString`] is suitable for transformation into an [`ANSIStrings`] /// type. -pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIString<'static>> { +pub fn style_key_with_modifier( + keyvec: &[Key], + palette: &Palette, + background: Option<PaletteColor>, +) -> Vec<ANSIString<'static>> { // Nothing to do, quit... if keyvec.is_empty() { return vec![]; @@ -419,13 +431,32 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIStr let painted_modifier = if modifier_str.is_empty() { Style::new().paint("") } else { - Style::new().fg(orange_color).bold().paint(modifier_str) + if let Some(background) = background { + let background = palette_match!(background); + Style::new() + .fg(orange_color) + .on(background) + .bold() + .paint(modifier_str) + } else { + Style::new().fg(orange_color).bold().paint(modifier_str) + } }; ret.push(painted_modifier); // Prints key group start let group_start_str = if no_modifier { "<" } else { " + <" }; - ret.push(Style::new().fg(text_color).paint(group_start_str)); + if let Some(background) = background { + let background = palette_match!(background); + ret.push( + Style::new() + .fg(text_color) + .on(background) + .paint(group_start_str), + ); + } else { + ret.push(Style::new().fg(text_color).paint(group_start_str)); + } // Prints the keys let key = keyvec @@ -451,18 +482,50 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIStr "←↓↑→" => "", "←→" => "", "↓↑" => "", + "[]" => "", _ => "|", }; for (idx, key) in key.iter().enumerate() { if idx > 0 && !key_separator.is_empty() { - ret.push(Style::new().fg(text_color).paint(key_separator)); + if let Some(background) = background { + let background = palette_match!(background); + ret.push( + Style::new() + .fg(text_color) + .on(background) + .paint(key_separator), + ); + } else { + ret.push(Style::new().fg(text_color).paint(key_separator)); + } + } + if let Some(background) = background { + let background = palette_match!(background); + ret.push( + Style::new() + .fg(green_color) + .on(background) + .bold() + .paint(key.clone()), + ); + } else { + ret.push(Style::new().fg(green_color).bold().paint(key.clone())); } - ret.push(Style::new().fg(green_color).bold().paint(key.clone())); } let group_end_str = ">"; - ret.push(Style::new().fg(text_color).paint(group_end_str)); + if let Some(background) = background { + let background = palette_match!(background); + ret.push( + Style::new() + .fg(text_color) + .on(background) + .paint(group_end_str), + ); + } else { + ret.push(Style::new().fg(text_color).paint(group_end_str)); + } ret } @@ -623,7 +686,7 @@ pub mod tests { let keyvec = vec![Key::Char('a'), Key::Char('b'), Key::Char('c')]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<a|b|c>".to_string()) @@ -639,7 +702,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<hjkl>".to_string()) @@ -656,7 +719,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<h|k|j|l>".to_string()) @@ -672,7 +735,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<←↓↑→>".to_string()) @@ -683,7 +746,7 @@ pub mod tests { let keyvec = vec![Key::Char('←'), Key::Char('→')]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<←→>".to_string()) @@ -694,7 +757,7 @@ pub mod tests { let keyvec = vec![Key::Char('↓'), Key::Char('↑')]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<↓↑>".to_string()) @@ -710,7 +773,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "Ctrl + <a|b|c|d>".to_string()) @@ -726,7 +789,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "Alt + <a|b|c|d>".to_string()) @@ -742,7 +805,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "Alt + <←↓↑→>".to_string()) @@ -757,7 +820,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "<Alt+a|Ctrl+b|c>".to_string()) @@ -780,7 +843,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!( @@ -794,7 +857,7 @@ pub mod tests { let keyvec = vec![Key::Ctrl('\n'), Key::Ctrl(' '), Key::Ctrl('\t')]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "Ctrl + <ENTER|SPACE|TAB>".to_string()) @@ -809,7 +872,7 @@ pub mod tests { ]; let palette = get_palette(); - let ret = style_key_with_modifier(&keyvec, &palette); + let ret = style_key_with_modifier(&keyvec, &palette, None); let ret = unstyle(&ANSIStrings(&ret)); assert_eq!(ret, "Alt + <ENTER|SPACE|TAB>".to_string()) diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs index 9b2c23fb6..b7c8a9cec 100644 --- a/default-plugins/status-bar/src/second_line.rs +++ b/default-plugins/status-bar/src/second_line.rs @@ -30,7 +30,7 @@ fn full_length_shortcut( let separator = if is_first_shortcut { " " } else { " / " }; let mut bits: Vec<ANSIString> = vec![Style::new().fg(text_color).paint(separator)]; - bits.extend(style_key_with_modifier(&key, &palette)); + bits.extend(style_key_with_modifier(&key, &palette, None)); bits.push( Style::new() .fg(text_color) @@ -179,7 +179,7 @@ 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), TO_NORMAL])), + (s("New"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])), (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])])), @@ -210,6 +210,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> { &[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("Scroll"), s("Scroll"), action_key_group(&km, &[&[Action::ScrollDown], &[Action::ScrollUp]])), @@ -253,7 +254,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> { (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("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None), TO_NORMAL])), + (s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])), (s("Rename tab"), s("Rename"), action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])), (s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])), 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 dd67ab8f8..77dec8a60 100644 --- a/default-plugins/status-bar/src/tip/data/compact_layout.rs +++ b/default-plugins/status-bar/src/tip/data/compact_layout.rs @@ -93,8 +93,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> { } let mut bits = vec![]; - bits.extend(style_key_with_modifier(&to_pane, &help.style.colors)); + bits.extend(style_key_with_modifier(&to_pane, &help.style.colors, None)); bits.push(Style::new().paint(", ")); - bits.extend(style_key_with_modifier(&pane_frames, &help.style.colors)); + bits.extend(style_key_with_modifier( + &pane_frames, + &help.style.colors, + None, + )); bits } 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 bef8acba2..ebd944b77 100644 --- a/default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs +++ b/default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs @@ -83,8 +83,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> { } let mut bits = vec![]; - bits.extend(style_key_with_modifier(&to_pane, &help.style.colors)); + bits.extend(style_key_with_modifier(&to_pane, &help.style.colors, None)); bits.push(Style::new().paint(", ")); - bits.extend(style_key_with_modifier(&edit_buffer, &help.style.colors)); + bits.extend(style_key_with_modifier( + &edit_buffer, + &help.style.colors, + None, + )); bits } 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 d91c3ed8b..4bf4a143d 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 @@ -62,11 +62,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> { } let mut bits = vec![]; - bits.extend(style_key_with_modifier(&to_pane, &help.style.colors)); + bits.extend(style_key_with_modifier(&to_pane, &help.style.colors, None)); bits.push(Style::new().paint(", ")); bits.extend(style_key_with_modifier( &floating_toggle, &help.style.colors, + None, )); bits } diff --git a/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs b/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs index 67940e491..c39ea302e 100644 --- a/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs +++ b/default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs @@ -70,8 +70,8 @@ fn add_keybinds(help: &ModeInfo) -> V |