diff --git a/default-plugins/compact-bar/src/line.rs b/default-plugins/compact-bar/src/line.rs index 16d64d07..6bcbab18 100644 --- a/default-plugins/compact-bar/src/line.rs +++ b/default-plugins/compact-bar/src/line.rs @@ -256,6 +256,8 @@ pub fn tab_line( palette: Palette, capabilities: PluginCapabilities, mode: InputMode, + active_swap_layout_name: &Option, + is_swap_layout_dirty: bool, ) -> Vec { let mut tabs_after_active = all_tabs.split_off(active_tab_index); let mut tabs_before_active = all_tabs; @@ -283,5 +285,97 @@ pub fn tab_line( capabilities, ); prefix.append(&mut tabs_to_render); + + let current_title_len = get_current_title_len(&prefix); + if current_title_len < cols { + let mut remaining_space = cols - current_title_len; + if let Some(swap_layout_status) = swap_layout_status( + remaining_space, + active_swap_layout_name, + is_swap_layout_dirty, + mode, + &palette, + tab_separator(capabilities), + ) { + remaining_space -= swap_layout_status.len; + let mut buffer = String::new(); + for _ in 0..remaining_space { + buffer.push_str(&style!(palette.black, palette.black).paint(" ").to_string()); + } + prefix.push(LinePart { + part: buffer, + len: remaining_space, + tab_index: None, + }); + prefix.push(swap_layout_status); + } + } + prefix } + +fn swap_layout_status( + max_len: usize, + swap_layout_name: &Option, + is_swap_layout_damaged: bool, + input_mode: InputMode, + palette: &Palette, + separator: &str, +) -> Option { + match swap_layout_name { + Some(swap_layout_name) => { + let mut swap_layout_name = format!(" {} ", swap_layout_name); + swap_layout_name.make_ascii_uppercase(); + let swap_layout_name_len = swap_layout_name.len() + 3; + + let (prefix_separator, swap_layout_name, suffix_separator) = + if input_mode == InputMode::Locked { + ( + style!(palette.black, palette.fg).paint(separator), + style!(palette.black, palette.fg) + .italic() + .paint(&swap_layout_name), + style!(palette.fg, palette.black).paint(separator), + ) + } else if is_swap_layout_damaged { + ( + style!(palette.black, palette.fg).paint(separator), + style!(palette.black, palette.fg) + .bold() + .paint(&swap_layout_name), + style!(palette.fg, palette.black).paint(separator), + ) + } else { + ( + style!(palette.black, palette.green).paint(separator), + style!(palette.black, palette.green) + .bold() + .paint(&swap_layout_name), + style!(palette.green, palette.black).paint(separator), + ) + }; + let swap_layout_indicator = format!( + "{}{}{}", + prefix_separator, swap_layout_name, suffix_separator + ); + let (part, full_len) = (format!("{}", swap_layout_indicator), swap_layout_name_len); + let short_len = swap_layout_name_len + 1; // 1 is the space between + if full_len <= max_len { + Some(LinePart { + part, + len: full_len, + tab_index: None, + }) + } else if short_len <= max_len && input_mode != InputMode::Locked { + Some(LinePart { + part: swap_layout_indicator, + len: short_len, + tab_index: None, + }) + } else { + None + } + }, + None => None, + } +} diff --git a/default-plugins/compact-bar/src/main.rs b/default-plugins/compact-bar/src/main.rs index c6ce9bbc..71652f0f 100644 --- a/default-plugins/compact-bar/src/main.rs +++ b/default-plugins/compact-bar/src/main.rs @@ -92,6 +92,8 @@ impl ZellijPlugin for State { } let mut all_tabs: Vec = vec![]; let mut active_tab_index = 0; + let mut active_swap_layout_name = None; + let mut is_swap_layout_dirty = false; let mut is_alternate_tab = false; for t in &mut self.tabs { let mut tabname = t.name.clone(); @@ -102,6 +104,8 @@ impl ZellijPlugin for State { active_tab_index = t.position; } else if t.active { active_tab_index = t.position; + is_swap_layout_dirty = t.is_swap_layout_dirty; + active_swap_layout_name = t.active_swap_layout_name.clone(); } let tab = tab_style( tabname, @@ -121,6 +125,8 @@ impl ZellijPlugin for State { self.mode_info.style.colors, self.mode_info.capabilities, self.mode_info.mode, + &active_swap_layout_name, + is_swap_layout_dirty, ); let mut s = String::new(); let mut len_cnt = 0; diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index ae8fc70e..23a6d586 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, + is_swap_layout_damaged: bool, + mode_info: &ModeInfo, + colored_elements: ColoredElements, + palette: &Palette, + separator: &str, +) -> Option { + 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 603cdf35..3e668559 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)], 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> { +pub fn style_key_with_modifier( + keyvec: &[Key], + palette: &Palette, + background: Option, +) -> Vec> { // 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 Vec "", "←→" => "", "↓↑" => "", + "[]" => "", _ => "|", }; 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, "".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, "".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, "".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 + ".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 + ".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, "".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 + ".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 + ".to_string()) diff --git a/default-plugins/status-bar/src/second_line.rs b/default-plugins/status-bar/src/second_line.rs index 9b2c23fb..b7c8a9ce 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 = 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)> { 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)> { &[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)> { (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 dd67ab8f..77dec8a6 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 { } 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 bef8acba..ebd944b7 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 { } 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 d91c3ed8..4bf4a143 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 { } 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 67940e49..c39ea302 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) -> Vec { letters.push(key); } } - let arrows = style_key_with_modifier(&arrows, &help.style.colors); - let letters = style_key_with_modifier(&letters, &help.style.colors); + let arrows = style_key_with_modifier(&arrows, &help.style.colors, None); + let letters = style_key_with_modifier(&letters, &help.style.colors, None); if arrows.is_empty() && letters.is_empty() { vec![Style::new().bold().paint("UNBOUND")] } else if arrows.is_empty() || letters.is_empty() { diff --git a/default-plugins/status-bar/src/tip/data/quicknav.rs b/default-plugins/status-bar/src/tip/data/quicknav.rs index b8077e45..318fe702 100644 --- a/default-plugins/status-bar/src/tip/data/quicknav.rs +++ b/default-plugins/status-bar/src/tip/data/quicknav.rs @@ -66,7 +66,7 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups { let new_pane = if new_pane_keys.is_empty() { vec![Style::new().bold().paint("UNBOUND")] } else { - style_key_with_modifier(&new_pane_keys, &help.style.colors) + style_key_with_modifier(&new_pane_keys, &help.style.colors, None) }; let mut resize_keys = action_key_group( @@ -84,7 +84,7 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups { let resize = if resize_keys.is_empty() { vec![Style::new().bold().paint("UNBOUND")] } else { - style_key_with_modifier(&resize_keys, &help.style.colors) + style_key_with_modifier(&resize_keys, &help.style.colors, None) }; let move_focus_keys = action_key_group( @@ -113,8 +113,8 @@ fn add_keybinds(help: &ModeInfo) -> Keygroups { letters.push(key); } } - let arrows = style_key_with_modifier(&arrows, &help.style.colors); - let letters = style_key_with_modifier(&letters, &help.style.colors); + let arrows = style_key_with_modifier(&arrows, &help.style.colors, None); + let letters = style_key_with_modifier(&letters, &help.style.colors, None); let move_focus = if arrows.is_empty() && letters.is_empty() { vec![Style::new().bold().paint("UNBOUND")] } else if arrows.is_empty() || letters.is_empty() { 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 55331eb3..486e2aa2 100644 --- a/default-plugins/status-bar/src/tip/data/sync_tab.rs +++ b/default-plugins/status-bar/src/tip/data/sync_tab.rs @@ -61,8 +61,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec { } let mut bits = vec![]; - bits.extend(style_key_with_modifier(&to_tab, &help.style.colors)); + bits.extend(style_key_with_modifier(&to_tab, &help.style.colors, None)); bits.push(Style::new().paint(", ")); - bits.extend(style_key_with_modifier(&sync_tabs, &help.style.colors)); + bits.extend(style_key_with_modifier( + &sync_tabs, + &help.style.colors, + None, + )); bits } diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 9b007989..bafd9df7 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -201,6 +201,7 @@ fn read_from_channel( Rc::new(RefCell::new(Palette::default())), Rc::new(RefCell::new(HashMap::new())), None, + None, ); // 0 is the pane index loop { if !should_keep_running.load(Ordering::SeqCst) { @@ -397,6 +398,7 @@ impl RemoteRunner { y: 0, rows, cols, + is_stacked: false, }; setup_remote_environment(&mut channel, win_size); start_zellij(&mut channel); @@ -432,6 +434,7 @@ impl RemoteRunner { y: 0, rows, cols, + is_stacked: false, }; setup_remote_environment(&mut channel, win_size); start_zellij_mirrored_session(&mut channel); @@ -474,6 +477,7 @@ impl RemoteRunner { y: 0, rows, cols, + is_stacked: false, }; setup_remote_environment(&mut channel, win_size); start_zellij_in_session(&mut channel, session_name, mirrored); @@ -509,6 +513,7 @@ impl RemoteRunner { y: 0, rows, cols, + is_stacked: false, }; setup_remote_environment(&mut channel, win_size); attach_to_existing_session(&mut channel, session_name); @@ -544,6 +549,7 @@ impl RemoteRunner { y: 0, rows, cols, + is_stacked: false, }; setup_remote_environment(&mut channel, win_size); start_zellij_without_frames(&mut channel); @@ -580,6 +586,7 @@ impl RemoteRunner { y: 0, rows, cols, + is_stacked: false, }; setup_remote_environment(&mut channel, win_size); start_zellij_with_config(&mut channel, &remote_path.to_string_lossy()); diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap index 2673f0fb..c42ad188 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap @@ -25,5 +25,5 @@ expression: last_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap index d373cbbb..8d06a664 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1077 +assertion_line: 1046 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  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 54908a5c..4e5545bd 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 @@ -25,5 +25,5 @@ expression: second_runner_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  <←→> Move focus / New / Close / Rename / Sync / Toggle / Select pane diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap index d891bab4..db168c4c 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap @@ -25,5 +25,5 @@ expression: first_runner_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap index 5bf97567..1d108f8c 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1521 +assertion_line: 1490 expression: second_runner_snapshot --- Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ] @@ -25,5 +25,5 @@ expression: second_runner_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap index 2316cf55..70542a4a 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1520 +assertion_line: 1489 expression: first_runner_snapshot --- Zellij (multiple_users_in_same_pane_and_tab)  Tab #1 [ ] @@ -25,5 +25,5 @@ expression: first_runner_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap index c4dd1fff..0df5280a 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 744 +assertion_line: 745 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││ │ │ ││ │ └────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap index d7bf8cd5..4a184969 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 863 +assertion_line: 867 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││ │ │ ││ │ └────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ - Ctrl + g  p  t  n  h  s  o  q  + Ctrl + g  p  t  n  h  s  o  q  Alt + <[]>  BASE  QuickNav: Alt + / Alt + <←↓↑→> or Alt + / Alt + <+|-> 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 ff4ce544..0b789c5d 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: 274 +assertion_line: 275 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││line20 │ │ ││li█e21 │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

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

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  + <↓↑> Scroll / Scroll / Scroll / Edit / Search / Select diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap index e5132b9f..bc1ae996 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1152 +assertion_line: 1121 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││line18 │ │ ││li█e19 │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap index 0603ac26..d797b10b 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1998 +assertion_line: 1990 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap index 8dc5897b..2c4f2c76 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 154 +assertion_line: 155 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap index 0fe416cf..7a30c617 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__start_without_pane_frames.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1198 +assertion_line: 1167 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ $ │$ █ │ │ │ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap index e812fda8..0a3b4249 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__tmux_mode.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1802 +assertion_line: 1734 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ ││ │ │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  Tip: Alt + => new pane. Alt + <←↓↑→> or Alt + => navigate. Alt + <+|-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap index 81c4b4fe..01f65fb1 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_floating_panes.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 1683 +assertion_line: 1687 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -26,4 +26,4 @@ expression: last_snapshot │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  - (FLOATING PANES VISIBLE): Press Ctrl+p, to hide. + (FLOATING PANES VISIBLE): Press Ctrl+p, to hide. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap index ad24c740..76ffb7e6 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap @@ -1,6 +1,6 @@ --- source: src/tests/e2e/cases.rs -assertion_line: 334 +assertion_line: 335 expression: last_snapshot --- Zellij (e2e-test)  Tab #1  @@ -25,5 +25,5 @@ expression: last_snapshot │ │ │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ - Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  + Ctrl + LOCK 

PANE  TAB  RESIZE  MOVE  SEARCH  SESSION  QUIT  BASE  (FULLSCREEN): + 1 hidden panes diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 91e11f10..f095e0c6 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -108,6 +108,7 @@ pub(crate) struct SessionMetaData { pub capabilities: PluginCapabilities, pub client_attributes: ClientAttributes, pub default_shell: Option, + pub layout: Box, screen_thread: Option>, pty_thread: Option>, plugin_thread: Option>, @@ -346,7 +347,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { }) }); - let spawn_tabs = |tab_layout, floating_panes_layout, tab_name| { + let spawn_tabs = |tab_layout, floating_panes_layout, tab_name, swap_layouts| { session_data .read() .unwrap() @@ -358,6 +359,7 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { tab_layout, floating_panes_layout, tab_name, + swap_layouts, client_id, )) .unwrap() @@ -369,6 +371,10 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { Some(tab_layout.clone()), floating_panes_layout.clone(), tab_name, + ( + layout.swap_tiled_layouts.clone(), + layout.swap_floating_layouts.clone(), + ), ); } @@ -386,7 +392,15 @@ pub fn start_server(mut os_input: Box, socket_path: PathBuf) { .unwrap(); } } else { - spawn_tabs(None, layout.floating_panes_template.clone(), None); + spawn_tabs( + None, + layout.template.map(|t| t.1).clone().unwrap_or_default(), + None, + ( + layout.swap_tiled_layouts.clone(), + layout.swap_floating_layouts.clone(), + ), + ); } session_data .read() @@ -751,6 +765,7 @@ fn init_session( ); let store = get_store(); + let layout = layout.clone(); move || { plugin_thread_main( plugin_bus, @@ -811,6 +826,7 @@ fn init_session( capabilities, default_shell, client_attributes, + layout, screen_thread: Some(screen_thread), pty_thread: Some(pty_thread), plugin_thread: Some(plugin_thread), diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs index 8e863af4..abcf24c8 100644 --- a/zellij-server/src/os_input_output.rs +++ b/zellij-server/src/os_input_output.rs @@ -396,10 +396,11 @@ pub struct ServerOsInputOutput { orig_termios: Arc>, client_senders: Arc>>, terminal_id_to_raw_fd: Arc>>>, // A value of None means the - // terminal_id exists but is - // not connected to an fd (eg. - // a command pane with a - // non-existing command) + // terminal_id exists but is + // not connected to an fd (eg. + // a command pane with a + // non-existing command) + cached_resizes: Arc>>>, // } // async fn in traits is not supported by rust, so dtolnay's excellent async_trait macro is being @@ -481,6 +482,8 @@ pub trait ServerOsApi: Send + Sync { quit_cb: Box, RunCommand) + Send>, // u32 is the exit status ) -> Result<(RawFd, RawFd)>; fn clear_terminal_id(&self, terminal_id: u32) -> Result<()>; + fn cache_resizes(&mut self) {} + fn apply_cached_resizes(&mut self) {} } impl ServerOsApi for ServerOsInputOutput { @@ -491,6 +494,10 @@ impl ServerOsApi for ServerOsInputOutput { id, rows, cols ) }; + if let Some(cached_resizes) = self.cached_resizes.lock().unwrap().as_mut() { + cached_resizes.insert(id, (cols, rows)); + return Ok(()); + } match self .terminal_id_to_raw_fd @@ -752,6 +759,19 @@ impl ServerOsApi for ServerOsInputOutput { .remove(&terminal_id); Ok(()) } + fn cache_resizes(&mut self) { + if self.cached_resizes.lock().unwrap().is_none() { + *self.cached_resizes.lock().unwrap() = Some(BTreeMap::new()); + } + } + fn apply_cached_resizes(&mut self) { + let mut cached_resizes = self.cached_resizes.lock().unwrap().take(); + if let Some(cached_resizes) = cached_resizes.as_mut() { + for (terminal_id, (cols, rows)) in cached_resizes.iter() { + let _ = self.set_terminal_size_using_terminal_id(*terminal_id, *cols, *rows); + } + } + } } impl Clone for Box { @@ -767,6 +787,7 @@ pub fn get_server_os_input() -> Result { orig_termios, client_senders: Arc::new(Mutex::new(HashMap::new())), terminal_id_to_raw_fd: Arc::new(Mutex::new(BTreeMap::new())), + cached_resizes: Arc::new(Mutex::new(None)), }) } diff --git a/zellij-server/src/panes/active_panes.rs b/zellij-server/src/panes/active_panes.rs index 3fce0a9f..c726bafe 100644 --- a/zellij-server/src/panes/active_panes.rs +++ b/zellij-server/src/panes/active_panes.rs @@ -3,6 +3,7 @@ use crate::tab::Pane; use crate::{os_input_output::ServerOsApi, panes::PaneId, ClientId}; use std::collections::{BTreeMap, HashMap}; +#[derive(Clone)] pub struct ActivePanes { active_panes: HashMap, os_api: Box, diff --git a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs index 2245ee16..afeecc8d 100644 --- a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs +++ b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs @@ -703,6 +703,56 @@ impl<'a> FloatingPaneGrid<'a> { .copied(); next_index } + pub fn next_selectable_pane_id(&self, current_pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let mut panes: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { + if a_pane.y() == b_pane.y() { + a_pane.x().cmp(&b_pane.x()) + } else { + a_pane.y().cmp(&b_pane.y()) + } + }); + let active_pane_position = panes + .iter() + .position(|(id, _)| id == current_pane_id) + .unwrap(); + + let next_active_pane_id = panes + .get(active_pane_position + 1) + .or_else(|| panes.get(0)) + .map(|p| p.0) + .unwrap(); + Some(next_active_pane_id) + } + pub fn previous_selectable_pane_id(&self, current_pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let mut panes: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| { + if a_pane.y() == b_pane.y() { + a_pane.x().cmp(&b_pane.x()) + } else { + a_pane.y().cmp(&b_pane.y()) + } + }); + let active_pane_position = panes.iter().position(|(id, _)| id == current_pane_id)?; + + let last_pane = panes.last()?; + let previous_active_pane_id = if active_pane_position == 0 { + last_pane.0 + } else { + panes.get(active_pane_position - 1)?.0 + }; + Some(previous_active_pane_id) + } pub fn find_room_for_new_pane(&self) -> Option { let panes = self.panes.borrow(); let pane_geoms: Vec = panes.values().map(|p| p.position_and_size()).collect(); @@ -766,6 +816,7 @@ fn half_size_middle_geom(space: &Viewport, offset: usize) -> PaneGeom { y: space.y + (space.rows as f64 / 4.0).round() as usize + offset, cols: Dimension::fixed(space.cols / 2), rows: Dimension::fixed(space.rows / 2), + is_stacked: false, }; geom.cols.set_inner(space.cols / 2); geom.rows.set_inner(space.rows / 2); @@ -778,6 +829,7 @@ fn half_size_top_left_geom(space: &Viewport, offset: usize) -> PaneGeom { y: space.y + 2 + offset, cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), + is_stacked: false, }; geom.cols.set_inner(space.cols / 3); geom.rows.set_inner(space.rows / 3); @@ -790,6 +842,7 @@ fn half_size_top_right_geom(space: &Viewport, offset: usize) -> PaneGeom { y: space.y + 2 + offset, cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), + is_stacked: false, }; geom.cols.set_inner(space.cols / 3); geom.rows.set_inner(space.rows / 3); @@ -802,6 +855,7 @@ fn half_size_bottom_left_geom(space: &Viewport, offset: usize) -> PaneGeom { y: ((space.y + space.rows) - (space.rows / 3) - 2).saturating_sub(offset), cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), + is_stacked: false, }; geom.cols.set_inner(space.cols / 3); geom.rows.set_inner(space.rows / 3); @@ -814,6 +868,7 @@ fn half_size_bottom_right_geom(space: &Viewport, offset: usize) -> PaneGeom { y: ((space.y + space.rows) - (space.rows / 3) - 2).saturating_sub(offset), cols: Dimension::fixed(space.cols / 3), rows: Dimension::fixed(space.rows / 3), + is_stacked: false, }; geom.cols.set_inner(space.cols / 3); geom.rows.set_inner(space.rows / 3); diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index 47b3e2f2..399d16c9 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -25,7 +25,7 @@ use zellij_utils::{ data::{ModeInfo, Style}, errors::prelude::*, input::command::RunCommand, - input::layout::FloatingPanesLayout, + input::layout::FloatingPaneLayout, pane_size::{Dimension, Offset, PaneGeom, Size, Viewport}, }; @@ -200,6 +200,14 @@ impl FloatingPanes { pub fn active_pane_id(&self, client_id: ClientId) -> Option { self.active_panes.get(&client_id).copied() } + pub fn active_pane_id_or_focused_pane_id(&self, client_id: Option) -> Option { + // returns the focused pane of any client_id - should be safe because the way things are + // set up at the time of writing, all clients are focused on the same floating pane due to + // z_index issues + client_id + .and_then(|client_id| self.active_panes.get(&client_id).copied()) + .or_else(|| self.panes.keys().next().copied()) + } pub fn toggle_show_panes(&mut self, should_show_floating_panes: bool) { self.show_panes = should_show_floating_panes; if should_show_floating_panes { @@ -227,7 +235,7 @@ impl FloatingPanes { } pub fn position_floating_pane_layout( &mut self, - floating_pane_layout: &FloatingPanesLayout, + floating_pane_layout: &FloatingPaneLayout, ) -> PaneGeom { let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); @@ -239,32 +247,32 @@ impl FloatingPanes { ); let mut position = floating_pane_grid.find_room_for_new_pane().unwrap(); // TODO: no unwrap if let Some(x) = &floating_pane_layout.x { - position.x = x.to_position(display_area.cols); + position.x = x.to_position(viewport.cols); } if let Some(y) = &floating_pane_layout.y { - position.y = y.to_position(display_area.rows); + position.y = y.to_position(viewport.rows); } if let Some(width) = &floating_pane_layout.width { - position.cols = Dimension::fixed(width.to_position(display_area.cols)); + position.cols = Dimension::fixed(width.to_position(viewport.cols)); } if let Some(height) = &floating_pane_layout.height { - position.rows = Dimension::fixed(height.to_position(display_area.rows)); + position.rows = Dimension::fixed(height.to_position(viewport.rows)); } - if position.cols.as_usize() > display_area.cols { - position.cols = Dimension::fixed(display_area.cols); + if position.cols.as_usize() > viewport.cols { + position.cols = Dimension::fixed(viewport.cols); } - if position.rows.as_usize() > display_area.rows { - position.rows = Dimension::fixed(display_area.rows); + if position.rows.as_usize() > viewport.rows { + position.rows = Dimension::fixed(viewport.rows); } - if position.x + position.cols.as_usize() > display_area.cols { + if position.x + position.cols.as_usize() > viewport.cols { position.x = position .x - .saturating_sub((position.x + position.cols.as_usize()) - display_area.cols); + .saturating_sub((position.x + position.cols.as_usize()) - viewport.cols); } - if position.y + position.rows.as_usize() > display_area.rows { + if position.y + position.rows.as_usize() > viewport.rows { position.y = position .y - .saturating_sub((position.y + position.rows.as_usize()) - display_area.rows); + .saturating_sub((position.y + position.rows.as_usize()) - viewport.rows); } position } @@ -333,6 +341,9 @@ impl FloatingPanes { &active_panes, multiple_users_exist_in_session, Some(z_index + 1), // +1 because 0 is reserved for non-floating panes + false, + false, + true, ); for client_id in &connected_clients { let client_mode = self @@ -570,6 +581,50 @@ impl FloatingPanes { self.set_force_render(); } } + pub fn move_active_pane( + &mut self, + search_backwards: bool, + os_api: &mut Box, + client_id: ClientId, + ) { + let active_pane_id = self.get_active_pane_id(client_id).unwrap(); + + let new_position_id = { + let pane_grid = FloatingPaneGrid::new( + &mut self.panes, + &mut self.desired_pane_positions, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if search_backwards { + pane_grid.previous_selectable_pane_id(&active_pane_id) + } else { + pane_grid.next_selectable_pane_id(&active_pane_id) + } + }; + if let Some(new_position_id) = new_position_id { + let current_position = self.panes.get(&active_pane_id).unwrap(); + let prev_geom = current_position.position_and_size(); + let prev_geom_override = current_position.geom_override(); + + let new_position = self.panes.get_mut(&new_position_id).unwrap(); + let next_geom = new_position.position_and_size(); + let next_geom_override = new_position.geom_override(); + new_position.set_geom(prev_geom); + if let Some(geom) = prev_geom_override { + new_position.set_geom_override(geom); + } + new_position.set_should_render(true); + + let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + current_position.set_geom(next_geom); + if let Some(geom) = next_geom_override { + current_position.set_geom_override(geom); + } + current_position.set_should_render(true); + let _ = self.set_pane_frames(os_api); + } + } pub fn move_clients_out_of_pane(&mut self, pane_id: PaneId) { let active_panes: Vec<(ClientId, PaneId)> = self .active_panes @@ -735,6 +790,17 @@ impl FloatingPanes { pub fn get_panes(&self) -> impl Iterator)> { self.panes.iter() } + pub fn visible_panes_count(&self) -> usize { + self.panes.len() + } + pub fn drain(&mut self) -> BTreeMap> { + self.z_indices.clear(); + self.desired_pane_positions.clear(); + match self.panes.iter().next().map(|(pid, _p)| *pid) { + Some(first_pid) => self.panes.split_off(&first_pid), + None => BTreeMap::new(), + } + } fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) { let clients_in_pane: Vec = self .active_panes @@ -748,4 +814,36 @@ impl FloatingPanes { .insert(client_id, to_pane_id, &mut self.panes); } } + pub fn reapply_pane_focus(&mut self) { + if let Some(focused_pane) = self.first_active_floating_pane_id() { + // floating pane focus is the same for all clients + self.focus_pane_for_all_clients(focused_pane); + } + } + pub fn switch_active_pane_with(&mut self, os_api: &mut Box, pane_id: PaneId) { + if let Some(active_pane_id) = self.first_active_floating_pane_id() { + let current_position = self.panes.get(&active_pane_id).unwrap(); + let prev_geom = current_position.position_and_size(); + let prev_geom_override = current_position.geom_override(); + + let new_position = self.panes.get_mut(&pane_id).unwrap(); + let next_geom = new_position.position_and_size(); + let next_geom_override = new_position.geom_override(); + new_position.set_geom(prev_geom); + if let Some(geom) = prev_geom_override { + new_position.set_geom_override(geom); + } + resize_pty!(new_position, os_api, self.senders).unwrap(); + new_position.set_should_render(true); + + let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + current_position.set_geom(next_geom); + if let Some(geom) = next_geom_override { + current_position.set_geom_override(geom); + } + resize_pty!(current_position, os_api, self.senders).unwrap(); + current_position.set_should_render(true); + self.focus_pane_for_all_clients(active_pane_id); + } + } } diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index babb66b6..c6896f90 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -16,6 +16,7 @@ use zellij_utils::{ channels::SenderWithContext, data::{Event, InputMode, Mouse, Palette, PaletteColor, Style}, errors::prelude::*, + input::layout::Run, pane_size::PaneGeom, shared::make_terminal_title, vte, @@ -65,6 +66,7 @@ pub(crate) struct PluginPane { frame: HashMap, borderless: bool, pane_frame_color_override: Option<(PaletteColor, Option)>, + invoked_with: Option, } impl PluginPane { @@ -80,6 +82,7 @@ impl PluginPane { link_handler: Rc>, character_cell_size: Rc>>, style: Style, + invoked_with: Option, ) -> Self { Self { pid, @@ -104,6 +107,7 @@ impl PluginPane { grids: HashMap::new(), style, pane_frame_color_override: None, + invoked_with, } } } @@ -222,6 +226,11 @@ impl Pane for PluginPane { if self.should_render.get(&client_id).copied().unwrap_or(false) { let content_x = self.get_content_x(); let content_y = self.get_content_y(); + let rows = self.get_content_rows(); + let columns = self.get_content_columns(); + if rows < 1 || columns < 1 { + return Ok(None); + } if let Some(grid) = self.grids.get_mut(&client_id) { match grid.render(content_x, content_y, &self.style) { Ok(rendered_assets) => { @@ -265,8 +274,15 @@ impl Pane for PluginPane { self.pane_name.clone() }; + let mut frame_geom = self.current_geom(); + if !frame_params.should_draw_pane_frames { + // in this case the width of the frame needs not include the pane corners + frame_geom + .cols + .set_inner(frame_geom.cols.as_usize().saturating_sub(1)); + } let mut frame = PaneFrame::new( - self.current_geom().into(), + frame_geom.into(), grid.scrollback_position_and_length(), pane_title, frame_params, @@ -491,6 +507,12 @@ impl Pane for PluginPane { .as_ref() .map(|(color, _text)| *color) } + fn invoked_with(&self) -> &Option { + &self.invoked_with + } + fn set_title(&mut self, title: String) { + self.pane_title = title; + } } impl PluginPane { diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index d206245a..e9fffb18 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -18,6 +18,7 @@ use zellij_utils::pane_size::Offset; use zellij_utils::{ data::{InputMode, Palette, PaletteColor, Style}, errors::prelude::*, + input::layout::Run, pane_size::PaneGeom, pane_size::SizeInPixels, position::Position, @@ -111,6 +112,7 @@ pub struct TerminalPane { banner: Option, // a banner to be rendered inside this TerminalPane, used for panes // held on startup and can possibly be used to display some errors pane_frame_color_override: Option<(PaletteColor, Option)>, + invoked_with: Option, } impl Pane for TerminalPane { @@ -165,6 +167,10 @@ impl Pane for TerminalPane { } fn cursor_coordinates(&self) -> Option<(usize, usize)> { // (x, y) + if self.get_content_rows() < 1 || self.get_content_columns() < 1 { + // do not render cursor if there's no room for it + return None; + } let Offset { top, left, .. } = self.content_offset; self.grid .cursor_coordinates() @@ -283,6 +289,11 @@ impl Pane for TerminalPane { if self.should_render() { let content_x = self.get_content_x(); let content_y = self.get_content_y(); + let rows = self.get_content_rows(); + let columns = self.get_content_columns(); + if rows < 1 || columns < 1 { + return Ok(None); + } match self.grid.render(content_x, content_y, &self.style) { Ok(rendered_assets) => { self.set_should_render(false); @@ -347,8 +358,15 @@ impl Pane for TerminalPane { self.pane_name.clone() }; + let mut frame_geom = self.current_geom(); + if !frame_params.should_draw_pane_frames { + // in this case the width of the frame needs not include the pane corners + frame_geom + .cols + .set_inner(frame_geom.cols.as_usize().saturating_sub(1)); + } let mut frame = PaneFrame::new( - self.current_geom().into(), + frame_geom.into(), self.grid.scrollback_position_and_length(), pane_title, frame_params, @@ -690,6 +708,12 @@ impl Pane for TerminalPane { .as_ref() .map(|(color, _text)| *color) } + fn invoked_with(&self) -> &Option { + &self.invoked_with + } + fn set_title(&mut self, title: String) { + self.pane_title = title; + } } impl TerminalPane { @@ -706,6 +730,7 @@ impl TerminalPane { terminal_emulator_colors: Rc>, terminal_emulator_color_codes: Rc>>, initial_pane_title: Option, + invoked_with: Option, ) -> TerminalPane { let initial_pane_title = initial_pane_title.unwrap_or_else(|| format!("Pane #{}", pane_index)); @@ -739,6 +764,7 @@ impl TerminalPane { is_held: None, banner: None, pane_frame_color_override: None, + invoked_with, } } pub fn get_x(&self) -> usize { @@ -780,6 +806,10 @@ impl TerminalPane { } pub fn cursor_coordinates(&self) -> Option<(usize, usize)> { // (x, y) + if self.get_content_rows() < 1 || self.get_content_columns() < 1 { + // do not render cursor if there's no room for it + return None; + } self.grid.cursor_coordinates() } fn render_first_run_banner(&mut self) { diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index cad53e3f..bd6470c9 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -1,4 +1,5 @@ mod pane_resizer; +mod stacked_panes; mod tiled_pane_grid; use crate::resize_pty; @@ -15,6 +16,7 @@ use crate::{ ui::pane_contents_and_ui::PaneContentsAndUi, ClientId, }; +use stacked_panes::StackedPanes; use zellij_utils::{ data::{ModeInfo, ResizeStrategy, Style}, errors::prelude::*, @@ -249,6 +251,10 @@ impl TiledPanes { self.set_pane_frames(self.draw_pane_frames); } + pub fn reapply_pane_frames(&mut self) { + // same as set_pane_frames except it reapplies the current situation + self.set_pane_frames(self.draw_pane_frames); + } pub fn set_pane_frames(&mut self, draw_pane_frames: bool) { self.draw_pane_frames = draw_pane_frames; let viewport = *self.viewport.borrow(); @@ -276,7 +282,12 @@ impl TiledPanes { let position_and_size = pane.current_geom(); let (pane_columns_offset, pane_rows_offset) = pane_content_offset(&position_and_size, &viewport); - pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset)); + if !draw_pane_frames && pane.current_geom().is_stacked { + // stacked panes should always leave 1 top row for a title + pane.set_content_offset(Offset::shift_right_and_top(pane_columns_offset, 1)); + } else { + pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset)); + } } resize_pty!(pane, self.os_api, self.senders).unwrap(); @@ -287,7 +298,9 @@ impl TiledPanes { if let Some(active_pane_id) = &self.active_panes.get(&client_id) { if let Some(active_pane) = self.panes.get_mut(active_pane_id) { let full_pane_size = active_pane.position_and_size(); - if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 { + if full_pane_size.rows.as_usize() < MIN_TERMINAL_HEIGHT * 2 + || full_pane_size.is_stacked + { return false; } else { return split(SplitDirection::Horizontal, &full_pane_size).is_some(); @@ -300,7 +313,9 @@ impl TiledPanes { if let Some(active_pane_id) = &self.active_panes.get(&client_id) { if let Some(active_pane) = self.panes.get_mut(active_pane_id) { let full_pane_size = active_pane.position_and_size(); - if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 { + if full_pane_size.cols.as_usize() < MIN_TERMINAL_WIDTH * 2 + || full_pane_size.is_stacked + { return false; } return split(SplitDirection::Vertical, &full_pane_size).is_some(); @@ -312,7 +327,7 @@ impl TiledPanes { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); let active_pane = self.panes.get(active_pane_id).unwrap(); let full_pane_size = active_pane.position_and_size(); - if full_pane_size.rows.is_fixed() { + if full_pane_size.rows.is_fixed() || full_pane_size.is_stacked { return false; } if split(SplitDirection::Horizontal, &full_pane_size).is_some() { @@ -343,7 +358,7 @@ impl TiledPanes { let active_pane_id = &self.active_panes.get(&client_id).unwrap(); let active_pane = self.panes.get(active_pane_id).unwrap(); let full_pane_size = active_pane.position_and_size(); - if full_pane_size.cols.is_fixed() { + if full_pane_size.cols.is_fixed() || full_pane_size.is_stacked { return false; } if split(SplitDirection::Vertical, &full_pane_size).is_some() { @@ -370,7 +385,83 @@ impl TiledPanes { self.relayout(SplitDirection::Horizontal); } } + pub fn focus_pane_for_all_clients(&mut self, pane_id: PaneId) { + let connected_clients: Vec = + self.connected_clients.borrow().iter().copied().collect(); + for client_id in connected_clients { + if self + .panes + .get(&pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&pane_id); + } + self.active_panes + .insert(client_id, pane_id, &mut self.panes); + self.set_pane_active_at(pane_id); + } + self.set_force_render(); + self.reapply_pane_frames(); + } + pub fn reapply_pane_focus(&mut self) { + let connected_clients: Vec = + self.connected_clients.borrow().iter().copied().collect(); + for client_id in connected_clients { + match &self.active_panes.get(&client_id).copied() { + Some(pane_id) => { + if self + .panes + .get(&pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&pane_id); + } + self.active_panes + .insert(client_id, *pane_id, &mut self.panes); + self.set_pane_active_at(*pane_id); + }, + None => { + if let Some(first_pane_id) = self.first_selectable_pane_id() { + let pane_id = first_pane_id; // TODO: combine with above + if self + .panes + .get(&pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap( + &mut self.panes, + &self.panes_to_hide, + ) + .focus_pane(&pane_id); + } + self.active_panes + .insert(client_id, pane_id, &mut self.panes); + self.set_pane_active_at(pane_id); + } + }, + } + } + self.set_force_render(); + self.reapply_pane_frames(); + } pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) { + if self + .panes + .get(&pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&pane_id); + self.reapply_pane_frames(); + } + self.active_panes .insert(client_id, pane_id, &mut self.panes); if self.session_is_mirrored { @@ -384,6 +475,34 @@ impl TiledPanes { } self.reset_boundaries(); } + pub fn focus_pane_at_position(&mut self, position_and_size: PaneGeom, client_id: ClientId) { + if let Some(pane_id) = self + .panes + .iter() + .find(|(_pid, pane)| pane.position_and_size() == position_and_size) + .map(|(pid, _p)| *pid) + { + if let Some(currently_active_pane_id) = self.active_panes.get(&client_id) { + let prev_geom = { + if let Some(currently_focused_pane) = + self.panes.get_mut(currently_active_pane_id) + { + let prev_geom = currently_focused_pane.position_and_size(); + currently_focused_pane.set_geom(position_and_size); + Some(prev_geom) + } else { + None + } + }; + if let Some(prev_geom) = prev_geom { + if let Some(previous_pane) = self.panes.get_mut(&pane_id) { + previous_pane.set_geom(prev_geom); + self.reset_boundaries(); + } + } + } + } + } pub fn focus_pane_if_client_not_focused(&mut self, pane_id: PaneId, client_id: ClientId) { if self.active_panes.get(&client_id).is_none() { self.focus_pane(pane_id, client_id) @@ -448,8 +567,20 @@ impl TiledPanes { .map(|(client_id, pane_id)| (*client_id, *pane_id)) .collect() }; + let (stacked_pane_ids_under_flexible_pane, stacked_pane_ids_over_flexible_pane) = { + // TODO: do not recalculate this every time on render + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .stacked_pane_ids_under_and_over_flexible_panes() + .unwrap() // TODO: no unwrap + }; for (kind, pane) in self.panes.iter_mut() { if !self.panes_to_hide.contains(&pane.pid()) { + let pane_is_stacked_under = + stacked_pane_ids_under_flexible_pane.contains(&pane.pid()); + let pane_is_stacked_over = + stacked_pane_ids_over_flexible_pane.contains(&pane.pid()); + let should_draw_pane_frames = self.draw_pane_frames; + let pane_is_stacked = pane.current_geom().is_stacked; let mut pane_contents_and_ui = PaneContentsAndUi::new( pane, output, @@ -457,6 +588,9 @@ impl TiledPanes { &active_panes, multiple_users_exist_in_session, None, + pane_is_stacked_under, + pane_is_stacked_over, + should_draw_pane_frames, ); for client_id in &connected_clients { let client_mode = self @@ -476,6 +610,22 @@ impl TiledPanes { pane_contents_and_ui .render_pane_frame(*client_id, client_mode, self.session_is_mirrored) .with_context(err_context)?; + } else if pane_is_stacked { + // if we have no pane frames but the pane is stacked, we need to render its + // frame which will amount to only rendering the title line + pane_contents_and_ui + .render_pane_frame(*client_id, client_mode, self.session_is_mirrored) + .with_context(err_context)?; + // we also need to render its boundaries as normal + let boundaries = client_id_to_boundaries + .entry(*client_id) + .or_insert_with(|| Boundaries::new(*self.viewport.borrow())); + pane_contents_and_ui.render_pane_boundaries( + *client_id, + client_mode, + boundaries, + self.session_is_mirrored, + ); } else { let boundaries = client_id_to_boundaries .entry(*client_id) @@ -542,6 +692,8 @@ impl TiledPanes { Err(e) => match e.downcast_ref::() { Some(ZellijError::PaneSizeUnchanged) => {}, // ignore unchanged layout _ => { + // display area still changed, even if we had an error + display_area.cols = cols; Err::<(), _>(anyError::msg(e)) .context("failed to resize tab horizontally") .non_fatal(); @@ -557,6 +709,8 @@ impl TiledPanes { Err(e) => match e.downcast_ref::() { Some(ZellijError::PaneSizeUnchanged) => {}, // ignore unchanged layout _ => { + // display area still changed, even if we had an error + display_area.rows = rows; Err::<(), _>(anyError::msg(e)) .context("failed to resize tab vertically") .non_fatal(); @@ -629,13 +783,26 @@ impl TiledPanes { let connected_clients: Vec = { self.connected_clients.borrow().iter().copied().collect() }; let active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - let next_active_pane_id = pane_grid.next_selectable_pane_id(&active_pane_id); + let next_active_pane_id = { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + pane_grid.next_selectable_pane_id(&active_pane_id) + }; + if self + .panes + .get(&next_active_pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&next_active_pane_id); + self.reapply_pane_frames(); + } + for client_id in connected_clients { self.active_panes .insert(client_id, next_active_pane_id, &mut self.panes); @@ -647,13 +814,26 @@ impl TiledPanes { let connected_clients: Vec = { self.connected_clients.borrow().iter().copied().collect() }; let active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - let next_active_pane_id = pane_grid.previous_selectable_pane_id(&active_pane_id); + let next_active_pane_id = { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + pane_grid.previous_selectable_pane_id(&active_pane_id) + }; + + if self + .panes + .get(&next_active_pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&next_active_pane_id); + self.reapply_pane_frames(); + } for client_id in connected_clients { self.active_panes .insert(client_id, next_active_pane_id, &mut self.panes); @@ -716,13 +896,15 @@ impl TiledPanes { pub fn move_focus_down(&mut self, client_id: ClientId) -> bool { match self.get_active_pane_id(client_id) { Some(active_pane_id) => { - let pane_grid = TiledPaneGrid::new( + let mut pane_grid = TiledPaneGrid::new( &mut self.panes, &self.panes_to_hide, *self.display_area.borrow(), *self.viewport.borrow(), ); - let next_index = pane_grid.next_selectable_pane_id_below(&active_pane_id); + let next_index = pane_grid + .next_selectable_pane_id_below(&active_pane_id) + .or_else(|| pane_grid.progress_stack_down_if_in_stack(&active_pane_id)); match next_index { Some(p) => { // render previously active pane so that its frame does not remain actively @@ -732,12 +914,16 @@ impl TiledPanes { .get_mut(self.active_panes.get(&client_id).unwrap()) .unwrap(); + let previously_active_pane_is_stacked = + previously_active_pane.current_geom().is_stacked; previously_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) previously_active_pane.render_full_viewport(); let next_active_pane = self.panes.get_mut(&p).unwrap(); + let next_active_pane_is_stacked = + next_active_pane.current_geom().is_stacked; next_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) @@ -745,6 +931,13 @@ impl TiledPanes { self.focus_pane(p, client_id); self.set_pane_active_at(p); + if previously_active_pane_is_stacked || next_active_pane_is_stacked { + // we do this because a stack pane focus change also changes its + // geometry and we need to let the pty know about this (like in a + // normal size change) + self.focus_pane_for_all_clients(p); // TODO: for all client *in stack* + self.reapply_pane_frames(); + } true }, @@ -757,13 +950,15 @@ impl TiledPanes { pub fn move_focus_up(&mut self, client_id: ClientId) -> bool { match self.get_active_pane_id(client_id) { Some(active_pane_id) => { - let pane_grid = TiledPaneGrid::new( + let mut pane_grid = TiledPaneGrid::new( &mut self.panes, &self.panes_to_hide, *self.display_area.borrow(), *self.viewport.borrow(), ); - let next_index = pane_grid.next_selectable_pane_id_above(&active_pane_id); + let next_index = pane_grid + .next_selectable_pane_id_above(&active_pane_id) + .or_else(|| pane_grid.progress_stack_up_if_in_stack(&active_pane_id)); match next_index { Some(p) => { // render previously active pane so that its frame does not remain actively @@ -773,12 +968,16 @@ impl TiledPanes { .get_mut(self.active_panes.get(&client_id).unwrap()) .unwrap(); + let previously_active_pane_is_stacked = + previously_active_pane.current_geom().is_stacked; previously_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) previously_active_pane.render_full_viewport(); let next_active_pane = self.panes.get_mut(&p).unwrap(); + let next_active_pane_is_stacked = + next_active_pane.current_geom().is_stacked; next_active_pane.set_should_render(true); // we render the full viewport to remove any ui elements that might have been // there before (eg. another user's cursor) @@ -786,6 +985,13 @@ impl TiledPanes { self.focus_pane(p, client_id); self.set_pane_active_at(p); + if previously_active_pane_is_stacked || next_active_pane_is_stacked { + // we do this because a stack pane focus change also changes its + // geometry and we need to let the pty know about this (like in a + // normal size change) + self.focus_pane_for_all_clients(p); // TODO: for all client *in stack* + self.reapply_pane_frames(); + } true }, @@ -836,15 +1042,66 @@ impl TiledPanes { None => false, } } - pub fn move_active_pane(&mut self, client_id: ClientId) { + pub fn switch_active_pane_with(&mut self, pane_id: PaneId) { + if let Some(active_pane_id) = self.first_active_pane_id() { + if let PaneId::Plugin(_) = active_pane_id { + // we do not implicitly change the location of plugin panes + // TODO: we might want to make this configurable through a layout property or a + // plugin API + return; + } + let current_position = self.panes.get(&active_pane_id).unwrap(); + let prev_geom = current_position.position_and_size(); + let prev_geom_override = current_position.geom_override(); + + let new_position = self.panes.get_mut(&pane_id).unwrap(); + let next_geom = new_position.position_and_size(); + let next_geom_override = new_position.geom_override(); + new_position.set_geom(prev_geom); + if let Some(geom) = prev_geom_override { + new_position.set_geom_override(geom); + } + resize_pty!(new_position, self.os_api, self.senders).unwrap(); + new_position.set_should_render(true); + + let current_position = self.panes.get_mut(&active_pane_id).unwrap(); + current_position.set_geom(next_geom); + if let Some(geom) = next_geom_override { + current_position.set_geom_override(geom); + } + resize_pty!(current_position, self.os_api, self.senders).unwrap(); + current_position.set_should_render(true); + self.focus_pane_for_all_clients(active_pane_id); + self.set_pane_frames(self.draw_pane_frames); + } + } + pub fn move_active_pane(&mut self, search_backwards: bool, client_id: ClientId) { let active_pane_id = self.get_active_pane_id(client_id).unwrap(); - let pane_grid = TiledPaneGrid::new( - &mut self.panes, - &self.panes_to_hide, - *self.display_area.borrow(), - *self.viewport.borrow(), - ); - let new_position_id = pane_grid.next_selectable_pane_id(&active_pane_id); + + let new_position_id = { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + if search_backwards { + pane_grid.previous_selectable_pane_id(&active_pane_id) + } else { + pane_grid.next_selectable_pane_id(&active_pane_id) + } + }; + if self + .panes + .get(&new_position_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&new_position_id); + self.reapply_pane_frames(); + } + let current_position = self.panes.get(&active_pane_id).unwrap(); let prev_geom = current_position.position_and_size(); let prev_geom_override = current_position.geom_override(); @@ -870,13 +1127,15 @@ impl TiledPanes { } pub fn move_active_pane_down(&mut self, client_id: ClientId) { if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let pane_grid = TiledPaneGrid::new( + let mut pane_grid = TiledPaneGrid::new( &mut self.panes, &self.panes_to_hide, *self.display_area.borrow(), *self.viewport.borrow(), ); - let next_index = pane_grid.next_selectable_pane_id_below(&active_pane_id); + let next_index = pane_grid + .next_selectable_pane_id_below(&active_pane_id) + .or_else(|| pane_grid.progress_stack_down_if_in_stack(&active_pane_id)); if let Some(p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); let current_position = self.panes.get(active_pane_id).unwrap(); @@ -978,13 +1237,15 @@ impl TiledPanes { } pub fn move_active_pane_up(&mut self, client_id: ClientId) { if let Some(active_pane_id) = self.get_active_pane_id(client_id) { - let pane_grid = TiledPaneGrid::new( + let mut pane_grid = TiledPaneGrid::new( &mut self.panes, &self.panes_to_hide, *self.display_area.borrow(), *self.viewport.borrow(), ); - let next_index = pane_grid.next_selectable_pane_id_above(&active_pane_id); + let next_index = pane_grid + .next_selectable_pane_id_above(&active_pane_id) + .or_else(|| pane_grid.progress_stack_up_if_in_stack(&active_pane_id)); if let Some(p) = next_index { let active_pane_id = self.active_panes.get(&client_id).unwrap(); let current_position = self.panes.get(active_pane_id).unwrap(); @@ -1033,11 +1294,21 @@ impl TiledPanes { .map(|(pane_id, _pane)| **pane_id); match next_active_pane_id { - Some(next_active_pane) => { + Some(next_active_pane_id) => { + if self + .panes + .get(&next_active_pane_id) + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false) + { + let _ = StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .focus_pane(&next_active_pane_id); + self.reapply_pane_frames(); + } for (client_id, active_pane_id) in active_panes { if active_pane_id == pane_id { self.active_panes - .insert(client_id, next_active_pane, &mut self.panes); + .insert(client_id, next_active_pane_id, &mut self.panes); } } }, @@ -1160,13 +1431,13 @@ impl TiledPanes { viewport_pane.set_geom_override(viewport_pane.position_and_size()); } let viewport = { *self.viewport.borrow() }; - let active_terminal = self.get_pane_mut(active_pane_id).unwrap(); + let active_pane = self.get_pane_mut(active_pane_id).unwrap(); let full_screen_geom = PaneGeom { x: viewport.x, y: viewport.y, ..Default::default() }; - active_terminal.set_geom_override(full_screen_geom); + active_pane.set_geom_override(full_screen_geom); } let connected_client_list: Vec = { self.connected_clients.borrow().iter().copied().collect() }; @@ -1196,6 +1467,9 @@ impl TiledPanes { pub fn panes_to_hide_count(&self) -> usize { self.panes_to_hide.len() } + pub fn visible_panes_count(&self) -> usize { + self.panes.len().saturating_sub(self.panes_to_hide.len()) + } pub fn add_to_hidden_panels(&mut self, pid: PaneId) { self.panes_to_hide.insert(pid); } @@ -1208,6 +1482,18 @@ impl TiledPanes { pub fn focus_all_panes(&mut self) { self.active_panes.focus_all_panes(&mut self.panes); } + pub fn drain(&mut self) -> BTreeMap> { + match self.panes.iter().next().map(|(pid, _p)| *pid) { + Some(first_pid) => self.panes.split_off(&first_pid), + None => BTreeMap::new(), + } + } + pub fn active_panes(&self) -> ActivePanes { + self.active_panes.clone() + } + pub fn set_active_panes(&mut self, active_panes: ActivePanes) { + self.active_panes = active_panes; + } fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) { let clients_in_pane: Vec = self .active_panes diff --git a/zellij-server/src/panes/tiled_panes/pane_resizer.rs b/zellij-server/src/panes/tiled_panes/pane_resizer.rs index c01e42e4..2a2b5b6f 100644 --- a/zellij-server/src/panes/tiled_panes/pane_resizer.rs +++ b/zellij-server/src/panes/tiled_panes/pane_resizer.rs @@ -1,3 +1,4 @@ +use super::stacked_panes::StackedPanes; use crate::{panes::PaneId, tab::Pane}; use cassowary::{ strength::{REQUIRED, STRONG}, @@ -35,8 +36,8 @@ type Grid = Vec>; impl<'a> PaneResizer<'a> { pub fn new(panes: Rc>>>) -> Self { let mut vars = HashMap::new(); - for &k in panes.borrow().keys() { - vars.insert(k, Variable::new()); + for &pane_id in panes.borrow().keys() { + vars.insert(pane_id, Variable::new()); } PaneResizer { panes, @@ -129,32 +130,64 @@ impl<'a> PaneResizer<'a> { fn apply_spans(&mut self, spans: Vec) -> Result<()> { let err_context = || format!("Failed to apply spans"); - let mut panes = self.panes.borrow_mut(); let mut geoms_changed = false; for span in spans { - let pane = panes.get_mut(&span.pid).unwrap(); - let current_geom = pane.position_and_size(); - let new_geom = match span.direction { - SplitDirection::Horizontal => PaneGeom { - x: span.pos, - cols: span.size, - ..pane.current_geom() - }, - SplitDirection::Vertical => PaneGeom { - y: span.pos, - rows: span.size, - ..pane.current_geom() - }, - }; - if new_geom.rows.as_usize() != current_geom.rows.as_usize() - || new_geom.cols.as_usize() != current_geom.cols.as_usize() - { - geoms_changed = true; - } - if pane.geom_override().is_some() { - pane.set_geom_override(new_geom); + let pane_is_stacked = self + .panes + .borrow() + .get(&span.pid) + .unwrap() + .current_geom() + .is_stacked; + if pane_is_stacked { + let current_geom = StackedPanes::new(self.panes.clone()) + .position_and_size_of_stack(&span.pid) + .unwrap(); + let new_geom = match span.direction { + SplitDirection::Horizontal => PaneGeom { + x: span.pos, + cols: span.size, + ..current_geom + }, + SplitDirection::Vertical => PaneGeom { + y: span.pos, + rows: span.size, + ..current_geom + }, + }; + StackedPanes::new(self.panes.clone()).resize_panes_in_stack(&span.pid, new_geom)?; + // TODO: test with geom_override (fullscreen) + if new_geom.rows.as_usize() != current_geom.rows.as_usize() + || new_geom.cols.as_usize() != current_geom.cols.as_usize() + { + geoms_changed = true; + } } else { - pane.set_geom(new_geom); + let mut panes = self.panes.borrow_mut(); + let pane = panes.get_mut(&span.pid).unwrap(); + let current_geom = pane.position_and_size(); + let new_geom = match span.direction { + SplitDirection::Horizontal => PaneGeom { + x: span.pos, + cols: span.size, + ..pane.current_geom() + }, + SplitDirection::Vertical => PaneGeom { + y: span.pos, + rows: span.size, + ..pane.current_geom() + }, + }; + if new_geom.rows.as_usize() != current_geom.rows.as_usize() + || new_geom.cols.as_usize() != current_geom.cols.as_usize() + { + geoms_changed = true; + } + if pane.geom_override().is_some() { + pane.set_geom_override(new_geom); + } else { + pane.set_geom(new_geom); + } } } if geoms_changed { @@ -175,7 +208,7 @@ impl<'a> PaneResizer<'a> { .panes .borrow() .values() - .map(|p| self.get_span(!direction, p.as_ref())) + .filter_map(|p| self.get_span(!direction, p.as_ref())) .collect(); let mut last_edge = 0; @@ -197,38 +230,52 @@ impl<'a> PaneResizer<'a> { .panes .borrow() .values() - .filter(|p| { - let s = self.get_span(!direction, p.as_ref()); - let span_bounds = (s.pos, s.pos + s.size.as_usize()); - bwn(span_bounds.0, boundary) - || (bwn(boundary.0, span_bounds) - && (bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1)) + .filter(|p| match self.get_span(!direction, p.as_ref()) { + Some(s) => { + let span_bounds = (s.pos, s.pos + s.size.as_usize()); + bwn(span_bounds.0, boundary) + || (bwn(boundary.0, span_bounds) + && (bwn(boundary.1, span_bounds) || boundary.1 == span_bounds.1)) + }, + None => false, }) - .map(|p| self.get_span(direction, p.as_ref())) + .filter_map(|p| self.get_span(direction, p.as_ref())) .collect(); spans.sort_unstable_by_key(|s| s.pos); spans } - fn get_span(&self, direction: SplitDirection, pane: &dyn Pane) -> Span { - let pas = pane.current_geom(); - // let size_var = self.vars[&pane.pid()]; + fn get_span(&self, direction: SplitDirection, pane: &dyn Pane) -> Option { + let position_and_size = { + let pas = pane.current_geom(); + if pas.is_stacked && pas.rows.is_percent() { + // this is the main pane of the stack + StackedPanes::new(self.panes.clone()).position_and_size_of_stack(&pane.pid()) + } else if pas.is_stacked { + // this is a one-liner stacked pane and should be handled as the same rect with + // the rest of the stack, represented by the main pane in the if branch above + None + } else { + // non-stacked pane, treat normally + Some(pas) + } + }?; let size_var = *self.vars.get(&pane.pid()).unwrap(); match direction { - SplitDirection::Horizontal => Span { + SplitDirection::Horizontal => Some(Span { pid: pane.pid(), direction, - pos: pas.x, - size: pas.cols, + pos: position_and_size.x, + size: position_and_size.cols, size_var, - }, - SplitDirection::Vertical => Span { + }), + SplitDirection::Vertical => Some(Span { pid: pane.pid(), direction, - pos: pas.y, - size: pas.rows, + pos: position_and_size.y, + size: position_and_size.rows, size_var, - }, + }), } } } @@ -249,7 +296,7 @@ fn constrain_spans(space: usize, spans: &[Span]) -> HashSet { + panes: Rc>>>, +} + +impl<'a> StackedPanes<'a> { + pub fn new(panes: Rc>>>) -> Self { + StackedPanes { panes } + } + pub fn new_from_btreemap( + panes: impl IntoIterator)>, + panes_to_hide: &HashSet, + ) -> Self { + let panes: HashMap<_, _> = panes + .into_iter() + .filter(|(p_id, _)| !panes_to_hide.contains(p_id)) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let panes = Rc::new(RefCell::new(panes)); + StackedPanes { panes } + } + pub fn move_down( + &mut self, + source_pane_id: &PaneId, + destination_pane_id: &PaneId, + ) -> Result<()> { + let err_context = || format!("Failed to move stacked pane focus down"); + let source_pane_is_stacked = self + .panes + .borrow() + .get(source_pane_id) + .with_context(err_context)? + .position_and_size() + .is_stacked; + let destination_pane_is_stacked = self + .panes + .borrow() + .get(destination_pane_id) + .with_context(err_context)? + .position_and_size() + .is_stacked; + if source_pane_is_stacked && destination_pane_is_stacked { + let mut panes = self.panes.borrow_mut(); + let source_pane = panes.get_mut(source_pane_id).with_context(err_context)?; + let mut source_pane_geom = source_pane.position_and_size(); + let mut destination_pane_geom = source_pane_geom.clone(); + destination_pane_geom.y = source_pane_geom.y + 1; + source_pane_geom.rows = Dimension::fixed(1); + source_pane.set_geom(source_pane_geom); + let destination_pane = panes + .get_mut(&destination_pane_id) + .with_context(err_context)?; + destination_pane.set_geom(destination_pane_geom); + } else if destination_pane_is_stacked { + // we're moving down to the highest pane in the stack, we need to expand it and shrink the + // expanded stack pane + self.make_highest_pane_in_stack_flexible(destination_pane_id)?; + } + Ok(()) + } + pub fn move_up(&mut self, source_pane_id: &PaneId, destination_pane_id: &PaneId) -> Result<()> { + let err_context = || format!("Failed to move stacked pane focus up"); + let source_pane_is_stacked = self + .panes + .borrow() + .get(source_pane_id) + .with_context(err_context)? + .position_and_size() + .is_stacked; + let destination_pane_is_stacked = self + .panes + .borrow() + .get(destination_pane_id) + .with_context(err_context)? + .position_and_size() + .is_stacked; + if source_pane_is_stacked && destination_pane_is_stacked { + let mut panes = self.panes.borrow_mut(); + let source_pane = panes.get_mut(source_pane_id).with_context(err_context)?; + let mut source_pane_geom = source_pane.position_and_size(); + let mut destination_pane_geom = source_pane_geom.clone(); + source_pane_geom.y = (source_pane_geom.y + source_pane_geom.rows.as_usize()) - 1; // -1 because we want to be at the last line of the source pane, not the next line over + source_pane_geom.rows = Dimension::fixed(1); + source_pane.set_geom(source_pane_geom); + destination_pane_geom.y -= 1; + let destination_pane = panes + .get_mut(&destination_pane_id) + .with_context(err_context)?; + destination_pane.set_geom(destination_pane_geom); + } else if destination_pane_is_stacked { + // we're moving up to the lowest pane in the stack, we need to expand it and shrink the + // expanded stack pane + self.make_lowest_pane_in_stack_flexible(destination_pane_id)?; + } + Ok(()) + } + pub fn focus_pane(&mut self, pane_id: &PaneId) -> Result<()> { + // this function doesn't actually change the focus (since it is controlled elsewhere) + // but rather makes sure pane_id is flexible if it were a one-liner before + let err_context = || format!("Failed to focus stacked pane"); + let all_stacked_pane_positions = + self.positions_in_stack(pane_id).with_context(err_context)?; + + let position_of_flexible_pane = self + .position_of_flexible_pane(&all_stacked_pane_positions) + .with_context(err_context)?; + let (flexible_pane_id, mut flexible_pane) = *all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + if flexible_pane_id != *pane_id { + let mut panes = self.panes.borrow_mut(); + let height_of_flexible_pane = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .map(|(_pid, p)| p.rows) + .with_context(err_context)?; + let position_of_pane_to_focus = all_stacked_pane_positions + .iter() + .position(|(pid, _p)| pid == pane_id) + .with_context(err_context)?; + let (_, mut pane_to_focus) = *all_stacked_pane_positions + .iter() + .nth(position_of_pane_to_focus) + .with_context(err_context)?; + pane_to_focus.rows = height_of_flexible_pane; + panes + .get_mut(pane_id) + .with_context(err_context)? + .set_geom(pane_to_focus); + flexible_pane.rows = Dimension::fixed(1); + panes + .get_mut(&flexible_pane_id) + .with_context(err_context)? + .set_geom(flexible_pane); + + for (i, (pid, _position)) in all_stacked_pane_positions.iter().enumerate() { + if i > position_of_pane_to_focus && i <= position_of_flexible_pane { + // the flexible pane has moved up the stack, we need to push this pane down + let pane = panes.get_mut(pid).with_context(err_context)?; + let mut pane_position_and_size = pane.position_and_size(); + pane_position_and_size.y += height_of_flexible_pane.as_usize() - 1; + pane.set_geom(pane_position_and_size); + } else if i > position_of_flexible_pane && i <= position_of_pane_to_focus { + // the flexible pane has moved down the stack, we need to pull this pane up + let pane = panes.get_mut(pid).with_context(err_context)?; + let mut pane_position_and_size = pane.position_and_size(); + pane_position_and_size.y -= height_of_flexible_pane.as_usize() - 1; + pane.set_geom(pane_position_and_size); + } + } + } + Ok(()) + } + pub fn flexible_pane_id_in_stack(&self, pane_id_in_stack: &PaneId) -> Option { + let all_stacked_pane_positions = self.positions_in_stack(pane_id_in_stack).ok()?; + all_stacked_pane_positions + .iter() + .find(|(_pid, p)| p.rows.is_percent()) + .map(|(pid, _p)| *pid) + } + pub fn position_and_size_of_stack(&self, id: &PaneId) -> Option { + let all_stacked_pane_positions = self.positions_in_stack(id).ok()?; + let position_of_flexible_pane = self + .position_of_flexible_pane(&all_stacked_pane_positions) + .ok()?; + let (_flexible_pane_id, flexible_pane) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane)?; + let (_, first_pane_in_stack) = all_stacked_pane_positions.first()?; + let (_, last_pane_in_stack) = all_stacked_pane_positions.last()?; + let mut rows = flexible_pane.rows; + rows.set_inner( + (last_pane_in_stack.y - first_pane_in_stack.y) + last_pane_in_stack.rows.as_usize(), + ); + Some(PaneGeom { + y: first_pane_in_stack.y, + x: first_pane_in_stack.x, + cols: first_pane_in_stack.cols, + rows, + is_stacked: true, // important because otherwise the minimum stack size will not be + // respected + ..Default::default() + }) + } + pub fn increase_stack_width(&mut self, id: &PaneId, percent: f64) -> Result<()> { + let err_context = || format!("Failed to resize panes in stack"); + let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; + for (pane_id, _pane_position) in all_stacked_pane_positions { + self.panes + .borrow_mut() + .get_mut(&pane_id) + .with_context(err_context)? + .increase_width(percent); + } + Ok(()) + } + pub fn reduce_stack_width(&mut self, id: &PaneId, percent: f64) -> Result<()> { + let err_context = || format!("Failed to resize panes in stack"); + let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; + for (pane_id, _pane_position) in all_stacked_pane_positions { + self.panes + .borrow_mut() + .get_mut(&pane_id) + .with_context(err_context)? + .reduce_width(percent); + } + Ok(()) + } + pub fn increase_stack_height(&mut self, id: &PaneId, percent: f64) -> Result<()> { + let err_context = || format!("Failed to increase_stack_height"); + let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; + let position_of_flexible_pane = self + .position_of_flexible_pane(&all_stacked_pane_positions) + .with_context(err_context)?; + let (flexible_pane_id, _flexible_pane) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + self.panes + .borrow_mut() + .get_mut(flexible_pane_id) + .with_context(err_context)? + .increase_height(percent); + Ok(()) + } + pub fn reduce_stack_height(&mut self, id: &PaneId, percent: f64) -> Result<()> { + let err_context = || format!("Failed to increase_stack_height"); + let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; + let position_of_flexible_pane = self + .position_of_flexible_pane(&all_stacked_pane_positions) + .with_context(err_context)?; + let (flexible_pane_id, _flexible_pane) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + self.panes + .borrow_mut() + .get_mut(flexible_pane_id) + .with_context(err_context)? + .reduce_height(percent); + Ok(()) + } + pub fn min_stack_height(&mut self, id: &PaneId) -> Result { + let err_context = || format!("Failed to increase_stack_height"); + let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; + Ok(all_stacked_pane_positions.len()) + } + pub fn resize_panes_in_stack( + &mut self, + id: &PaneId, + new_full_stack_geom: PaneGeom, + ) -> Result<()> { + let err_context = || format!("Failed to resize panes in stack"); + let all_stacked_pane_positions = self.positions_in_stack(id).with_context(err_context)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + let (flexible_pane_id, flexible_pane) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + let current_rows = all_stacked_pane_positions.len() + (flexible_pane.rows.as_usize() - 1); + let new_rows = new_full_stack_geom.rows.as_usize(); + + let adjust_stack_geoms = |new_flexible_pane_geom: PaneGeom| -> Result<()> { + let new_flexible_pane_geom_rows = new_flexible_pane_geom.rows.as_usize(); + for (i, (pane_id, pane_geom)) in all_stacked_pane_positions.iter().enumerate() { + let mut new_pane_geom = if i == position_of_flexible_pane { + new_flexible_pane_geom + } else { + *pane_geom + }; + new_pane_geom.x = new_full_stack_geom.x; + new_pane_geom.cols = new_full_stack_geom.cols; + if i <= position_of_flexible_pane { + new_pane_geom.y = new_full_stack_geom.y + i; + } else { + new_pane_geom.y = new_full_stack_geom.y + i + (new_flexible_pane_geom_rows - 1); + } + self.panes + .borrow_mut() + .get_mut(&pane_id) + .with_context(err_context)? + .set_geom(new_pane_geom); + } + Ok(()) + }; + + if new_rows >= current_rows { + let extra_rows = new_rows - current_rows; + let mut new_flexible_pane_geom = *flexible_pane; + new_flexible_pane_geom + .rows + .set_inner(new_flexible_pane_geom.rows.as_usize() + extra_rows); + self.panes + .borrow_mut() + .get_mut(&flexible_pane_id) + .with_context(err_context)? + .set_geom(new_flexible_pane_geom); + adjust_stack_geoms(new_flexible_pane_geom)?; + } else { + if new_rows < all_stacked_pane_positions.len() { + // TODO: test this!! we don't want crashes... + return Err(anyhow!("Not enough room for stacked panes")); + } + let rows_deficit = current_rows - new_rows; + let mut new_flexible_pane_geom = *flexible_pane; + new_flexible_pane_geom + .rows + .set_inner(new_flexible_pane_geom.rows.as_usize() - rows_deficit); + self.panes + .borrow_mut() + .get_mut(&flexible_pane_id) + .with_context(err_context)? + .set_geom(new_flexible_pane_geom); + adjust_stack_geoms(new_flexible_pane_geom)?; + } + Ok(()) + } + fn pane_is_one_liner(&self, id: &PaneId) -> Result { + let err_context = || format!("Cannot determin if pane is one liner or not"); + let panes = self.panes.borrow(); + let pane_to_close = panes.get(id).with_context(err_context)?; + Ok(pane_to_close.position_and_size().rows.is_fixed()) + } + fn positions_in_stack(&self, id: &PaneId) -> Result> { + // find the full stack of panes around the given id, sorted by pane location top to bottom + let err_context = || format!("Failed to find stacked panes"); + let panes = self.panes.borrow(); + let pane_in_stack = panes.get(id).with_context(err_context)?; + let mut all_stacked_pane_positions: Vec<(PaneId, PaneGeom)> = panes + .iter() + .filter(|(_pid, p)| p.position_and_size().is_stacked) + .filter(|(_pid, p)| { + p.position_and_size().x == pane_in_stack.position_and_size().x + && p.position_and_size().cols == pane_in_stack.position_and_size().cols + }) + .map(|(pid, p)| (*pid, p.position_and_size())) + .collect(); + all_stacked_pane_positions.sort_by(|(_a_pid, a), (_b_pid, b)| a.y.cmp(&b.y)); + Ok(all_stacked_pane_positions) + } + fn position_of_current_and_flexible_pane( + &self, + current_pane_id: &PaneId, + ) -> Result<(usize, usize)> { + // (current_pane, flexible_pane) + let err_context = || format!("Failed to position_of_current_and_flexible_pane"); + let all_stacked_pane_positions = self.positions_in_stack(current_pane_id)?; + let panes = self.panes.borrow(); + let pane_to_close = panes.get(current_pane_id).with_context(err_context)?; + let position_of_current_pane = + self.position_of_current_pane(&all_stacked_pane_positions, &pane_to_close)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + Ok((position_of_current_pane, position_of_flexible_pane)) + } + fn position_of_current_pane( + &self, + all_stacked_pane_positions: &Vec<(PaneId, PaneGeom)>, + pane_to_close: &Box, + ) -> Result { + let err_context = || format!("Failed to find position of current pane"); + all_stacked_pane_positions + .iter() + .position(|(pid, _p)| pid == &pane_to_close.pid()) + .with_context(err_context) + } + fn position_of_flexible_pane( + &self, + all_stacked_pane_positions: &Vec<(PaneId, PaneGeom)>, + ) -> Result { + let err_context = || format!("Failed to find position of flexible pane"); + all_stacked_pane_positions + .iter() + .position(|(_pid, p)| p.rows.is_percent()) + .with_context(err_context) + } + pub fn fill_space_over_pane_in_stack(&mut self, id: &PaneId) -> Result { + if self.pane_is_one_liner(id)? { + self.fill_space_over_one_liner_pane(id) + } else { + self.fill_space_over_visible_stacked_pane(id) + } + } + pub fn stacked_pane_ids_under_and_over_flexible_panes( + &self, + ) -> Result<(HashSet, HashSet)> { + let mut stacked_pane_ids_under_flexible_panes = HashSet::new(); + let mut stacked_pane_ids_over_flexible_panes = HashSet::new(); + let mut seen = HashSet::new(); + let pane_ids_in_stacks: Vec = { + self.panes + .borrow() + .iter() + .filter(|(_p_id, p)| p.position_and_size().is_stacked) + .map(|(p_id, _p)| *p_id) + .collect() + }; + for pane_id in pane_ids_in_stacks { + if !seen.contains(&pane_id) { + let mut current_pane_is_above_stack = true; + let positions_in_stack = self.positions_in_stack(&pane_id)?; + for (pane_id, pane_geom) in positions_in_stack { + seen.insert(pane_id); + if pane_geom.rows.is_percent() { + // this is the flexible pane + current_pane_is_above_stack = false; + continue; + } + if current_pane_is_above_stack { + stacked_pane_ids_over_flexible_panes.insert(pane_id); + } else { + stacked_pane_ids_under_flexible_panes.insert(pane_id); + } + } + seen.insert(pane_id); + } + } + Ok(( + stacked_pane_ids_under_flexible_panes, + stacked_pane_ids_over_flexible_panes, + )) + } + fn fill_space_over_one_liner_pane(&mut self, id: &PaneId) -> Result { + let (position_of_current_pane, position_of_flexible_pane) = + self.position_of_current_and_flexible_pane(id)?; + if position_of_current_pane > position_of_flexible_pane { + self.fill_space_over_one_liner_pane_above_flexible_pane(id) + } else { + self.fill_space_over_one_liner_pane_below_flexible_pane(id) + } + } + fn fill_space_over_visible_stacked_pane(&mut self, id: &PaneId) -> Result { + let err_context = || format!("Failed to fill_space_over_visible_stacked_pane"); + let all_stacked_pane_positions = self.positions_in_stack(id)?; + let mut panes = self.panes.borrow_mut(); + let pane_to_close = panes.get(id).with_context(err_context)?; + let position_of_current_pane = + self.position_of_current_pane(&all_stacked_pane_positions, &pane_to_close)?; + if all_stacked_pane_positions.len() > position_of_current_pane + 1 { + let mut pane_to_close_position_and_size = pane_to_close.position_and_size(); + pane_to_close_position_and_size + .rows + .set_inner(pane_to_close_position_and_size.rows.as_usize() + 1); + let pane_id_below = all_stacked_pane_positions + .iter() + .nth(position_of_current_pane + 1) + .map(|(pid, _)| *pid) + .with_context(err_context)?; + let pane_below = panes.get_mut(&pane_id_below).with_context(err_context)?; + pane_below.set_geom(pane_to_close_position_and_size); + return Ok(true); + } else if position_of_current_pane > 0 { + let mut pane_to_close_position_and_size = pane_to_close.position_and_size(); + pane_to_close_position_and_size + .rows + .set_inner(pane_to_close_position_and_size.rows.as_usize() + 1); + pane_to_close_position_and_size.y -= 1; + let pane_id_above = all_stacked_pane_positions + .iter() + .nth(position_of_current_pane - 1) + .map(|(pid, _)| *pid) + .with_context(err_context)?; + let pane_above = panes.get_mut(&pane_id_above).with_context(err_context)?; + pane_above.set_geom(pane_to_close_position_and_size); + return Ok(true); + } else { + return Ok(false); + } + } + fn fill_space_over_one_liner_pane_above_flexible_pane(&mut self, id: &PaneId) -> Result { + let err_context = + || format!("Failed to fill_space_over_one_liner_pane_above_flexible_pane"); + let all_stacked_pane_positions = self.positions_in_stack(id)?; + let mut panes = self.panes.borrow_mut(); + let pane_to_close = panes.get(id).with_context(err_context)?; + let position_of_current_pane = + self.position_of_current_pane(&all_stacked_pane_positions, &pane_to_close)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + let id_of_flexible_pane = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .map(|(pid, _p)| *pid) + .with_context(err_context)?; + let flexible_pane = panes + .get_mut(&id_of_flexible_pane) + .with_context(err_context)?; + let mut flexible_pane_position_and_size = flexible_pane.position_and_size(); + flexible_pane_position_and_size + .rows + .set_inner(flexible_pane_position_and_size.rows.as_usize() + 1); + flexible_pane.set_geom(flexible_pane_position_and_size); + for (i, (pid, _position)) in all_stacked_pane_positions.iter().enumerate() { + if i > position_of_flexible_pane && i < position_of_current_pane { + let pane = panes.get_mut(pid).with_context(err_context)?; + let mut pane_position_and_size = pane.position_and_size(); + pane_position_and_size.y += 1; + pane.set_geom(pane_position_and_size); + } + } + Ok(true) + } + fn fill_space_over_one_liner_pane_below_flexible_pane(&mut self, id: &PaneId) -> Result { + let err_context = + || format!("Failed to fill_space_over_one_liner_pane_below_flexible_pane"); + let all_stacked_pane_positions = self.positions_in_stack(id)?; + let mut panes = self.panes.borrow_mut(); + let pane_to_close = panes.get(id).with_context(err_context)?; + let position_of_current_pane = + self.position_of_current_pane(&all_stacked_pane_positions, &pane_to_close)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + let id_of_flexible_pane = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .map(|(pid, _p)| *pid) + .with_context(err_context)?; + let flexible_pane = panes + .get_mut(&id_of_flexible_pane) + .with_context(err_context)?; + let mut flexible_pane_position_and_size = flexible_pane.position_and_size(); + flexible_pane_position_and_size + .rows + .set_inner(flexible_pane_position_and_size.rows.as_usize() + 1); + flexible_pane.set_geom(flexible_pane_position_and_size); + for (i, (pid, _position)) in all_stacked_pane_positions.iter().enumerate() { + if i > position_of_current_pane && i <= position_of_flexible_pane { + let pane = panes.get_mut(pid).with_context(err_context)?; + let mut pane_position_and_size = pane.position_and_size(); + pane_position_and_size.y = pane_position_and_size.y.saturating_sub(1); + pane.set_geom(pane_position_and_size); + } + } + Ok(true) + } + fn make_lowest_pane_in_stack_flexible(&mut self, destination_pane_id: &PaneId) -> Result<()> { + let err_context = || format!("Failed to make_lowest_pane_flexible"); + let mut all_stacked_pane_positions = self.positions_in_stack(destination_pane_id)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + if position_of_flexible_pane != all_stacked_pane_positions.len().saturating_sub(1) { + let mut panes = self.panes.borrow_mut(); + let height_of_flexible_pane = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .map(|(_pid, p)| p.rows) + .with_context(err_context)?; + let (lowest_pane_id, mut lowest_pane_geom) = all_stacked_pane_positions + .last_mut() + .with_context(err_context)?; + lowest_pane_geom.rows = height_of_flexible_pane; + panes + .get_mut(lowest_pane_id) + .with_context(err_context)? + .set_geom(lowest_pane_geom); + let (flexible_pane_id, mut flexible_pane_geom) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + flexible_pane_geom.rows = Dimension::fixed(1); + panes + .get_mut(flexible_pane_id) + .with_context(err_context)? + .set_geom(flexible_pane_geom); + for (i, (pid, _position)) in all_stacked_pane_positions.iter().enumerate() { + if i > position_of_flexible_pane { + let pane = panes.get_mut(pid).with_context(err_context)?; + let mut pane_position_and_size = pane.position_and_size(); + pane_position_and_size.y = pane_position_and_size + .y + .saturating_sub(height_of_flexible_pane.as_usize() - 1); + pane.set_geom(pane_position_and_size); + } + } + } + Ok(()) + } + fn make_highest_pane_in_stack_flexible(&mut self, destination_pane_id: &PaneId) -> Result<()> { + let err_context = || format!("Failed to make_lowest_pane_flexible"); + let mut all_stacked_pane_positions = self.positions_in_stack(destination_pane_id)?; + let position_of_flexible_pane = + self.position_of_flexible_pane(&all_stacked_pane_positions)?; + if position_of_flexible_pane != 0 { + let mut panes = self.panes.borrow_mut(); + let height_of_flexible_pane = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .map(|(_pid, p)| p.rows) + .with_context(err_context)?; + let (highest_pane_id, mut highest_pane_geom) = all_stacked_pane_positions + .first_mut() + .with_context(err_context)?; + let y_of_whole_stack = highest_pane_geom.y; + highest_pane_geom.rows = height_of_flexible_pane; + panes + .get_mut(highest_pane_id) + .with_context(err_context)? + .set_geom(highest_pane_geom); + let (flexible_pane_id, mut flexible_pane_geom) = all_stacked_pane_positions + .iter() + .nth(position_of_flexible_pane) + .with_context(err_context)?; + flexible_pane_geom.rows = Dimension::fixed(1); + panes + .get_mut(flexible_pane_id) + .with_context(err_context)? + .set_geom(flexible_pane_geom); + for (i, (pid, _position)) in all_stacked_pane_positions.iter().enumerate() { + if i > 0 { + let pane = panes.get_mut(pid).with_context(err_context)?; + let mut pane_position_and_size = pane.position_and_size(); + pane_position_and_size.y = + y_of_whole_stack + height_of_flexible_pane.as_usize() + (i - 1); + pane.set_geom(pane_position_and_size); + } + } + } + Ok(()) + } +} diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs index e34164f9..8fc40d2b 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -1,5 +1,6 @@ use super::is_inside_viewport; use super::pane_resizer::PaneResizer; +use super::stacked_panes::StackedPanes; use crate::tab::{MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH}; use crate::{panes::PaneId, tab::Pane}; use std::cmp::Reverse; @@ -49,42 +50,32 @@ impl<'a> TiledPaneGrid<'a> { } } - /// Calculates an area for each pane and sums them all. - /// - /// Returns the product of "rows * columns", summed across all panes. - #[cfg(debug_assertions)] - fn total_panes_area(&self) -> f64 { - let mut summed_area: f64 = 0.0; - - for pane in self.panes.clone().borrow().values() { - let geom = pane.current_geom(); - summed_area += match (geom.rows.as_percent(), geom.cols.as_percent()) { - (Some(rows), Some(cols)) => rows * cols, - _ => continue, - }; - } - - summed_area / (100.0 * 100.0) - } - pub fn layout(&mut self, direction: SplitDirection, space: usize) -> Result<()> { let mut pane_resizer = PaneResizer::new(self.panes.clone()); pane_resizer.layout(direction, space) } + fn get_pane_geom(&self, pane_id: &PaneId) -> Option { + let panes = self.panes.borrow(); + let pane_to_check = panes.get(pane_id)?; + let pane_geom = pane_to_check.current_geom(); + if pane_geom.is_stacked { + StackedPanes::new(self.panes.clone()).position_and_size_of_stack(&pane_id) + } else { + Some(pane_geom) + } + } fn pane_is_flexible(&self, direction: SplitDirection, pane_id: &PaneId) -> Result { let err_context = || format!("failed to determine if pane {pane_id:?} is flexible in {direction:?}"); - let panes = self.panes.borrow(); - let pane_to_check = panes - .get(pane_id) + let pane_geom = self + .get_pane_geom(pane_id) .with_context(|| no_pane_id(pane_id)) .with_context(err_context)?; - let geom = pane_to_check.current_geom(); Ok(!match direction { - SplitDirection::Vertical => geom.rows, - SplitDirection::Horizontal => geom.cols, + SplitDirection::Vertical => pane_geom.rows, + SplitDirection::Horizontal => pane_geom.cols, } .is_fixed()) } @@ -99,12 +90,12 @@ impl<'a> TiledPaneGrid<'a> { let neighbor_terminal_borders: HashSet<_> = if direction.is_horizontal() { neighbor_terminals .iter() - .map(|t| self.panes.borrow().get(t).unwrap().y()) + .filter_map(|t| self.get_pane_geom(t).map(|p| p.y)) .collect() } else { neighbor_terminals .iter() - .map(|t| self.panes.borrow().get(t).unwrap().x()) + .filter_map(|t| self.get_pane_geom(t).map(|p| p.x)) .collect() }; @@ -300,12 +291,12 @@ impl<'a> TiledPaneGrid<'a> { let neighbor_terminal_borders: HashSet<_> = if direction.is_horizontal() { neighbor_terminals .iter() - .map(|t| self.panes.borrow().get(t).unwrap().y()) + .filter_map(|p| self.get_pane_geom(p).map(|p| p.y)) .collect() } else { neighbor_terminals .iter() - .map(|t| self.panes.borrow().get(t).unwrap().x()) + .filter_map(|p| self.get_pane_geom(p).map(|p| p.x)) .collect() }; @@ -408,34 +399,34 @@ impl<'a> TiledPaneGrid<'a> { ]; // For the borrow checker { - let panes = self.panes.borrow(); - let active_pane = panes - .get(pane_id) + // let panes = self.panes.borrow(); + let active_pane = self + .get_pane_geom(pane_id) .with_context(|| no_pane_id(pane_id)) .with_context(err_context)?; for p_id in self.viewport_pane_ids_directly_below(pane_id) { - let pane = panes - .get(&p_id) + let pane = self + .get_pane_geom(&p_id) .with_context(|| no_pane_id(&p_id)) .with_context(err_context)?; - if active_pane.x() + active_pane.cols() == pane.x() { + if active_pane.x + active_pane.cols.as_usize() == pane.x { // right aligned aligned_panes[0] = Some(p_id); - } else if active_pane.x() == pane.x() + pane.cols() { + } else if active_pane.x == pane.x + pane.cols.as_usize() { // left aligned aligned_panes[1] = Some(p_id); } } for p_id in self.viewport_pane_ids_directly_above(pane_id) { - let pane = panes - .get(&p_id) + let pane = self + .get_pane_geom(&p_id) .with_context(|| no_pane_id(&p_id)) .with_context(err_context)?; - if active_pane.x() + active_pane.cols() == pane.x() { + if active_pane.x + active_pane.cols.as_usize() == pane.x { // right aligned aligned_panes[2] = Some(p_id); - } else if active_pane.x() == pane.x() + pane.cols() { + } else if active_pane.x == pane.x + pane.cols.as_usize() { // left aligned aligned_panes[3] = Some(p_id); } @@ -467,8 +458,15 @@ impl<'a> TiledPaneGrid<'a> { ..strategy }; - if self.can_change_pane_size(pane_id, &main_strategy, change_by)? - && self.can_change_pane_size(pane_id, &sub_strategy, change_by)? + // TODO: instead of unwrap_or(false) here we need to do the same with the fixed + // panes error above, only make sure that we only error if we cannot resize in + // any directions and have blocking fixed panes + if self + .can_change_pane_size(pane_id, &main_strategy, change_by) + .unwrap_or(false) + && self + .can_change_pane_size(pane_id, &sub_strategy, change_by) + .unwrap_or(false) { let result = self .change_pane_size(pane_id, &main_strategy, change_by) @@ -512,16 +510,6 @@ impl<'a> TiledPaneGrid<'a> { return Ok(false); } - #[cfg(debug_assertions)] - { - let area = self.total_panes_area() * 100.0; - debug_assert!( - f64::abs(area - 100.0) < 1.0, // Tolerate a little rounding error - "area consumed by panes doesn't fill the viewport! Total area is {area} % - During operation: '{strategy}', on pane {pane_id:?}", - ); - } - Ok(true) } @@ -529,16 +517,15 @@ impl<'a> TiledPaneGrid<'a> { let err_context = || format!("failed to determine if pane {pane_id:?} can reduce width by {reduce_by} %"); - let panes = self.panes.borrow(); - let pane = panes - .get(pane_id) + let pane = self + .get_pane_geom(pane_id) .with_context(|| no_pane_id(pane_id)) .with_context(err_context)?; - let current_fixed_cols = pane.position_and_size().cols.as_usize(); + let current_fixed_cols = pane.cols.as_usize(); let will_reduce_by = ((self.display_area.cols as f64 / 100.0) * reduce_by) as usize; - if current_fixed_cols.saturating_sub(will_reduce_by) < pane.min_width() { + if current_fixed_cols.saturating_sub(will_reduce_by) < MIN_TERMINAL_WIDTH { Ok(false) - } else if let Some(cols) = pane.position_and_size().cols.as_percent() { + } else if let Some(cols) = pane.cols.as_percent() { Ok(cols - reduce_by >= RESIZE_PERCENT) } else { Ok(false) @@ -549,16 +536,20 @@ impl<'a> TiledPaneGrid<'a> { format!("failed to determine if pane {pane_id:?} can reduce height by {reduce_by} %") }; - let panes = self.panes.borrow(); - let pane = panes - .get(pane_id) + let pane = self + .get_pane_geom(pane_id) .with_context(|| no_pane_id(pane_id)) .with_context(err_context)?; - let current_fixed_rows = pane.position_and_size().rows.as_usize(); + let min_terminal_height = if pane.is_stacked { + StackedPanes::new(self.panes.clone()).min_stack_height(pane_id)? + } else { + MIN_TERMINAL_HEIGHT + }; + let current_fixed_rows = pane.rows.as_usize(); let will_reduce_by = ((self.display_area.rows as f64 / 100.0) * reduce_by) as usize; - if current_fixed_rows.saturating_sub(will_reduce_by) < pane.min_height() { + if current_fixed_rows.saturating_sub(will_reduce_by) < min_terminal_height { Ok(false) - } else if let Some(rows) = pane.position_and_size().rows.as_percent() { + } else if let Some(rows) = pane.rows.as_percent() { Ok(rows - reduce_by >= RESIZE_PERCENT) } else { Ok(false) @@ -567,26 +558,70 @@ impl<'a> TiledPaneGrid<'a> { fn reduce_pane_height(&mut self, id: &PaneId, percent: f64) { if self.can_reduce_pane_height(id, percent).unwrap() { - let mut panes = self.panes.borrow_mut(); - let terminal = panes.get_mut(id).unwrap(); - terminal.reduce_height(percent); + let current_pane_is_stacked = self + .panes + .borrow() + .get(id) + .unwrap() + .current_geom() + .is_stacked; + if current_pane_is_stacked { + let _ = StackedPanes::new(self.panes.clone()).reduce_stack_height(&id, percent); + } else { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.reduce_height(percent); + } } } fn increase_pane_height(&mut self, id: &PaneId, percent: f64) { - let mut panes = self.panes.borrow_mut(); - let terminal = panes.get_mut(id).unwrap(); - terminal.increase_height(percent); + let current_pane_is_stacked = self + .panes + .borrow() + .get(id) + .unwrap() + .current_geom() + .is_stacked; + if current_pane_is_stacked { + let _ = StackedPanes::new(self.panes.clone()).increase_stack_height(&id, percent); + } else { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.increase_height(percent); + } } fn increase_pane_width(&mut self, id: &PaneId, percent: f64) { - let mut panes = self.panes.borrow_mut(); - let terminal = panes.get_mut(id).unwrap(); - terminal.increase_width(percent); + let current_pane_is_stacked = self + .panes + .borrow() + .get(id) + .unwrap() + .current_geom() + .is_stacked; + if current_pane_is_stacked { + let _ = StackedPanes::new(self.panes.clone()).increase_stack_width(&id, percent); + } else { + let mut panes = self.panes.borrow_mut(); + let pane = panes.get_mut(id).unwrap(); + pane.increase_width(percent); + } } fn reduce_pane_width(&mut self, id: &PaneId, percent: f64) { if self.can_reduce_pane_width(id, percent).unwrap() { - let mut panes = self.panes.borrow_mut(); - let terminal = panes.get_mut(id).unwrap(); - terminal.reduce_width(percent); + let current_pane_is_stacked = self + .panes + .borrow() + .get(id) + .unwrap() + .current_geom() + .is_stacked; + if current_pane_is_stacked { + let _ = StackedPanes::new(self.panes.clone()).reduce_stack_width(&id, percent); + } else { + let mut panes = self.panes.borrow_mut(); + let terminal = panes.get_mut(id).unwrap(); + terminal.reduce_width(percent); + } } } @@ -597,27 +632,38 @@ impl<'a> TiledPaneGrid<'a> { fn pane_ids_directly_next_to(&self, id: &PaneId, direction: &Direction) -> Result> { let err_context = || format!("failed to find panes {direction} from pane {id:?}"); - let panes = self.panes.borrow(); let mut ids = vec![]; - let terminal_to_check = panes - .get(id) + let pane_geom_to_check = self + .get_pane_geom(id) .with_context(|| no_pane_id(id)) .with_context(err_context)?; - for (&pid, terminal) in panes.iter() { + let panes = self.panes.borrow(); + let mut seen = HashSet::new(); + for pid in panes.keys() { + let pane = self + .get_pane_geom(pid) + .with_context(|| no_pane_id(id)) + .with_context(err_context)?; + if seen.contains(&pane) { + continue; + } else { + seen.insert(pane); + } if match direction { - Direction::Left => (terminal.x() + terminal.cols()) == terminal_to_check.x(), + Direction::Left => (pane.x + pane.cols.as_usize()) == pane_geom_to_check.x, Direction::Down => { - terminal.y() == (terminal_to_check.y() + terminal_to_check.rows()) + pane.y == (pane_geom_to_check.y + pane_geom_to_check.rows.as_usize()) }, - Direction::Up => (terminal.y() + terminal.rows()) == terminal_to_check.y(), + Direction::Up => (pane.y + pane.rows.as_usize()) == pane_geom_to_check.y, Direction::Right => { - terminal.x() == (terminal_to_check.x() + terminal_to_check.cols()) + pane.x == (pane_geom_to_check.x + pane_geom_to_check.cols.as_usize()) }, } { - ids.push(pid); + ids.push(*pid); } } + Ok(ids) } @@ -629,29 +675,38 @@ impl<'a> TiledPaneGrid<'a> { ) -> Result> { let err_context = || format!("failed to find panes aligned {direction} with {pane_id:?}"); - let panes = self.panes.borrow(); - let pane_to_check = panes - .get(pane_id) + let pane_to_check = self + .get_pane_geom(pane_id) .with_context(|| no_pane_id(pane_id)) .with_context(err_context)?; let mut result = vec![]; - for (p_id, pane) in panes.iter() { - if p_id == pane_id { + let panes = self.panes.borrow(); + let mut seen = HashSet::new(); + for (pid, _pane) in panes.iter() { + let pane = self + .get_pane_geom(pid) + .with_context(|| no_pane_id(pane_id)) + .with_context(err_context)?; + if seen.contains(&pane) || pid == pane_id { continue; + } else { + seen.insert(pane); } if match direction { - Direction::Left => pane.x() == pane_to_check.x(), + Direction::Left => pane.x == pane_to_check.x, Direction::Down => { - (pane.y() + pane.rows()) == (pane_to_check.y() + pane_to_check.rows()) + (pane.y + pane.rows.as_usize()) + == (pane_to_check.y + pane_to_check.rows.as_usize()) }, - Direction::Up => pane.y() == pane_to_check.y(), + Direction::Up => pane.y == pane_to_check.y, Direction::Right => { - (pane.x() + pane.cols()) == (pane_to_check.x() + pane_to_check.cols()) + (pane.x + pane.cols.as_usize()) + == (pane_to_check.x + pane_to_check.cols.as_usize()) }, } { - result.push(*p_id) + result.push(*pid) } } Ok(result) @@ -671,9 +726,8 @@ impl<'a> TiledPaneGrid<'a> { let input_error = anyhow!("Invalid combination of alignment ({alignment}) and direction ({direction})"); - let panes = self.panes.borrow(); - let pane_to_check = panes - .get(id) + let pane_to_check = self + .get_pane_geom(id) .with_context(|| no_pane_id(id)) .with_context(err_context)?; let mut result = vec![]; @@ -682,7 +736,7 @@ impl<'a> TiledPaneGrid<'a> { .and_then(|pane_ids| { Ok(pane_ids .iter() - .map(|p_id| panes.get(p_id).unwrap()) // <-- TODO: Bad unwrap! + .filter_map(|p_id| self.get_pane_geom(p_id).map(|pane_geom| (*p_id, pane_geom))) .collect()) }) .with_context(err_context)?; @@ -693,23 +747,26 @@ impl<'a> TiledPaneGrid<'a> { use Direction::Up as U; match (alignment, direction) { - (&R, &U) | (&L, &U) => aligned_panes.sort_by_key(|a| Reverse(a.y())), - (&R, &D) | (&L, &D) => aligned_panes.sort_by_key(|a| a.y()), - (&D, &L) | (&U, &L) => aligned_panes.sort_by_key(|a| Reverse(a.x())), - (&D, &R) | (&U, &R) => aligned_panes.sort_by_key(|a| a.x()), + (&R, &U) | (&L, &U) => aligned_panes.sort_by_key(|(_, a)| Reverse(a.y)), + (&R, &D) | (&L, &D) => aligned_panes.sort_by_key(|(_, a)| a.y), + (&D, &L) | (&U, &L) => aligned_panes.sort_by_key(|(_, a)| Reverse(a.x)), + (&D, &R) | (&U, &R) => aligned_panes.sort_by_key(|(_, a)| a.x), _ => return Err(input_error).with_context(err_context), }; - for pane in aligned_panes { - let pane_to_check = result.last().unwrap_or(&pane_to_check); + for (pid, pane) in aligned_panes { + let pane_to_check = result + .last() + .map(|(_pid, pane)| pane) + .unwrap_or(&pane_to_check); if match (alignment, direction) { - (&R, &U) | (&L, &U) => (pane.y() + pane.rows()) == pane_to_check.y(), - (&R, &D) | (&L, &D) => pane.y() == (pane_to_check.y() + pane_to_check.rows()), - (&D, &L) | (&U, &L) => (pane.x() + pane.cols()) == pane_to_check.x(), - (&D, &R) | (&U, &R) => pane.x() == (pane_to_check.x() + pane_to_check.cols()), + (&R, &U) | (&L, &U) => (pane.y + pane.rows.as_usize()) == pane_to_check.y, + (&R, &D) | (&L, &D) => pane.y == (pane_to_check.y + pane_to_check.rows.as_usize()), + (&D, &L) | (&U, &L) => (pane.x + pane.cols.as_usize()) == pane_to_check.x, + (&D, &R) | (&U, &R) => pane.x == (pane_to_check.x + pane_to_check.cols.as_usize()), _ => return Err(input_error).with_context(err_context), } { - result.push(pane); + result.push((pid, pane)); } } @@ -720,12 +777,12 @@ impl<'a> TiledPaneGrid<'a> { &R => self.viewport.x + self.viewport.cols, }; - for pane in &result { + for (_, pane) in &result { let pane_boundary = match direction { - &L => pane.x() + pane.cols(), - &D => pane.y(), - &U => pane.y() + pane.rows(), - &R => pane.x(), + &L => pane.x + pane.cols.as_usize(), + &D => pane.y, + &U => pane.y + pane.rows.as_usize(), + &R => pane.x, }; if border.get(&pane_boundary).is_some() { match direction { @@ -742,24 +799,24 @@ impl<'a> TiledPaneGrid<'a> { } } } - result.retain(|pane| match direction { - &L => pane.x() >= resize_border, - &D => (pane.y() + pane.rows()) <= resize_border, - &U => pane.y() >= resize_border, - &R => (pane.x() + pane.cols()) <= resize_border, + result.retain(|(_pid, pane)| match direction { + &L => pane.x >= resize_border, + &D => (pane.y + pane.rows.as_usize()) <= resize_border, + &U => pane.y >= resize_border, + &R => (pane.x + pane.cols.as_usize()) <= resize_border, }); let resize_border = if result.is_empty() { match direction { - &L => pane_to_check.x(), - &D => pane_to_check.y() + pane_to_check.rows(), - &U => pane_to_check.y(), - &R => pane_to_check.x() + pane_to_check.cols(), + &L => pane_to_check.x, + &D => pane_to_check.y + pane_to_check.rows.as_usize(), + &U => pane_to_check.y, + &R => pane_to_check.x + pane_to_check.cols.as_usize(), } } else { resize_border }; - let pane_ids: Vec = result.iter().map(|t| t.pid()).collect(); + let pane_ids: Vec = result.iter().map(|(pid, _t)| *pid).collect(); Ok((resize_border, pane_ids)) } @@ -770,9 +827,8 @@ impl<'a> TiledPaneGrid<'a> { left_border_x: usize, right_border_x: usize, ) -> bool { - let panes = self.panes.borrow(); - let pane = panes.get(id).unwrap(); - pane.x() >= left_border_x && pane.x() + pane.cols() <= right_border_x + let pane = self.get_pane_geom(id).unwrap(); + pane.x >= left_border_x && pane.x + pane.cols.as_usize() <= right_border_x } fn pane_is_between_horizontal_borders( @@ -781,9 +837,8 @@ impl<'a> TiledPaneGrid<'a> { top_border_y: usize, bottom_border_y: usize, ) -> bool { - let panes = self.panes.borrow(); - let pane = panes.get(id).unwrap(); - pane.y() >= top_border_y && pane.y() + pane.rows() <= bottom_border_y + let pane = self.get_pane_geom(id).unwrap(); + pane.y >= top_border_y && pane.y + pane.rows.as_usize() <= bottom_border_y } fn viewport_pane_ids_directly_above(&self, pane_id: &PaneId) -> Vec { @@ -863,7 +918,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, p)| p.selectable()) .map(|(p_id, p)| (*p_id, p)) .collect(); - let next_index = panes + let next_pane = panes .iter() .enumerate() .filter(|(_, (_, c))| { @@ -871,9 +926,83 @@ impl<'a> TiledPaneGrid<'a> { && c.horizontally_overlaps_with(Box::as_ref(current_pane)) }) .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid) - .copied(); - next_index + .map(|(_, (_, pane))| pane); + let next_pane_is_stacked = next_pane + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false); + if next_pane_is_stacked { + if let Some(next_pane_id) = next_pane.map(|p| p.pid()) { + return StackedPanes::new(self.panes.clone()) + .flexible_pane_id_in_stack(&next_pane_id); + } + } + next_pane.map(|p| p.pid()) + } + pub fn progress_stack_up_if_in_stack(&mut self, source_pane_id: &PaneId) -> Option { + let destination_pane_id_in_stack = { + let panes = self.panes.borrow(); + let source_pane = panes.get(source_pane_id)?; + let pane_list: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let destination_pane_id = pane_list + .iter() + .enumerate() + .filter(|(_, (_, c))| { + c.is_directly_above(Box::as_ref(source_pane)) + && c.vertically_overlaps_with(Box::as_ref(source_pane)) + && c.current_geom().is_stacked + }) + .max_by_key(|(_, (_, c))| c.active_at()) + .map(|(_, (pid, _))| pid) + .copied(); + destination_pane_id + }; + + match destination_pane_id_in_stack { + Some(destination_pane_id) => { + StackedPanes::new(self.panes.clone()) + .move_up(source_pane_id, &destination_pane_id) + .ok()?; + Some(destination_pane_id) + }, + None => None, + } + } + pub fn progress_stack_down_if_in_stack(&mut self, source_pane_id: &PaneId) -> Option { + let destination_pane_id_in_stack = { + let panes = self.panes.borrow(); + let source_pane = panes.get(source_pane_id)?; + let pane_list: Vec<(PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable()) + .map(|(p_id, p)| (*p_id, p)) + .collect(); + let destination_pane_id = pane_list + .iter() + .enumerate() + .filter(|(_, (_, c))| { + c.is_directly_below(Box::as_ref(source_pane)) + && c.vertically_overlaps_with(Box::as_ref(source_pane)) + && c.current_geom().is_stacked + }) + .max_by_key(|(_, (_, c))| c.active_at()) + .map(|(_, (pid, _))| pid) + .copied(); + destination_pane_id + }; + + match destination_pane_id_in_stack { + Some(destination_pane_id) => { + StackedPanes::new(self.panes.clone()) + .move_down(source_pane_id, &destination_pane_id) + .ok()?; + Some(destination_pane_id) + }, + None => None, + } } pub fn next_selectable_pane_id_below(&self, current_pane_id: &PaneId) -> Option { let panes = self.panes.borrow(); @@ -889,6 +1018,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, (_, c))| { c.is_directly_below(Box::as_ref(current_pane)) && c.vertically_overlaps_with(Box::as_ref(current_pane)) + && !c.current_geom().is_stacked }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -909,6 +1039,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, (_, c))| { c.is_directly_above(Box::as_ref(current_pane)) && c.vertically_overlaps_with(Box::as_ref(current_pane)) + && !c.current_geom().is_stacked }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -923,7 +1054,7 @@ impl<'a> TiledPaneGrid<'a> { .filter(|(_, p)| p.selectable()) .map(|(p_id, p)| (*p_id, p)) .collect(); - let next_index = panes + let next_pane = panes .iter() .enumerate() .filter(|(_, (_, c))| { @@ -931,16 +1062,33 @@ impl<'a> TiledPaneGrid<'a> { && c.horizontally_overlaps_with(Box::as_ref(current_pane)) }) .max_by_key(|(_, (_, c))| c.active_at()) - .map(|(_, (pid, _))| pid) + .map(|(_, (_pid, pane))| pane) .copied(); - next_index + let next_pane_is_stacked = next_pane + .map(|p| p.current_geom().is_stacked) + .unwrap_or(false); + if next_pane_is_stacked { + if let Some(next_pane_id) = next_pane.map(|p| p.pid()) { + return StackedPanes::new(self.panes.clone()) + .flexible_pane_id_in_stack(&next_pane_id); + } + } + next_pane.map(|p| p.pid()) } fn horizontal_borders(&self, pane_ids: &[PaneId]) -> HashSet { pane_ids.iter().fold(HashSet::new(), |mut borders, p| { let panes = self.panes.borrow(); let pane = panes.get(p).unwrap(); - borders.insert(pane.y()); - borders.insert(pane.y() + pane.rows()); + if pane.current_geom().is_stacked { + let pane_geom = StackedPanes::new(self.panes.clone()) + .position_and_size_of_stack(&pane.pid()) + .unwrap(); + borders.insert(pane_geom.y); + borders.insert(pane_geom.y + pane_geom.rows.as_usize()); + } else { + borders.insert(pane.y()); + borders.insert(pane.y() + pane.rows()); + } borders }) } @@ -1098,14 +1246,23 @@ impl<'a> TiledPaneGrid<'a> { pub fn fill_space_over_pane(&mut self, id: PaneId) -> bool { // true => successfully filled space over pane // false => didn't succeed, so didn't do anything - let (freed_width, freed_height) = { + let (freed_width, freed_height, pane_to_close_is_stacked) = { let panes = self.panes.borrow_mut(); let pane_to_close = panes.get(&id).unwrap(); let freed_space = pane_to_close.position_and_size(); let freed_width = freed_space.cols.as_percent(); let freed_height = freed_space.rows.as_percent(); - (freed_width, freed_height) + let pane_to_close_is_stacked = pane_to_close.current_geom().is_stacked; + (freed_width, freed_height, pane_to_close_is_stacked) }; + if pane_to_close_is_stacked { + let successfully_filled_space = StackedPanes::new(self.panes.clone()) + .fill_space_over_pane_in_stack(&id) + .unwrap_or(false); + if successfully_filled_space { + return true; + } + } if let (Some(freed_width), Some(freed_height)) = (freed_width, freed_height) { if let Some((panes_to_grow, direction)) = self.find_panes_to_grow(id) { self.grow_panes(&panes_to_grow, direction, (freed_width, freed_height)); @@ -1126,8 +1283,10 @@ impl<'a> TiledPaneGrid<'a> { cursor_height_width_ratio: Option, ) -> Option<(PaneId, SplitDirection)> { let panes = self.panes.borrow(); - let pane_sequence: Vec<(&PaneId, &&mut Box)> = - panes.iter().filter(|(_, p)| p.selectable()).collect(); + let pane_sequence: Vec<(&PaneId, &&mut Box)> = panes + .iter() + .filter(|(_, p)| p.selectable() && !p.current_geom().is_stacked) + .collect(); let (_largest_pane_size, pane_id_to_split) = pane_sequence.iter().fold( (0, None), |(current_largest_pane_size, current_pane_id_to_split), id_and_pane_to_check| { diff --git a/zellij-server/src/panes/unit/search_in_pane_tests.rs b/zellij-server/src/panes/unit/search_in_pane_tests.rs index fb03b491..7e10e5f9 100644 --- a/zellij-server/src/panes/unit/search_in_pane_tests.rs +++ b/zellij-server/src/panes/unit/search_in_pane_tests.rs @@ -40,6 +40,7 @@ fn create_pane() -> TerminalPane { Rc::new(RefCell::new(Palette::default())), terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let content = read_fixture(); terminal_pane.handle_pty_bytes(content); diff --git a/zellij-server/src/panes/unit/terminal_pane_tests.rs b/zellij-server/src/panes/unit/terminal_pane_tests.rs index 744d2e90..291d65eb 100644 --- a/zellij-server/src/panes/unit/terminal_pane_tests.rs +++ b/zellij-server/src/panes/unit/terminal_pane_tests.rs @@ -48,6 +48,7 @@ pub fn scrolling_inside_a_pane() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let mut text_to_fill_pane = String::new(); for i in 0..30 { @@ -89,6 +90,7 @@ pub fn sixel_image_inside_terminal_pane() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let sixel_image_bytes = "\u{1b}Pq #0;2;0;0;0#1;2;100;100;0#2;2;0;100;0 @@ -130,6 +132,7 @@ pub fn partial_sixel_image_inside_terminal_pane() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let pane_content = read_fixture("sixel-image-500px.six"); terminal_pane.handle_pty_bytes(pane_content); @@ -165,6 +168,7 @@ pub fn overflowing_sixel_image_inside_terminal_pane() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let pane_content = read_fixture("sixel-image-500px.six"); terminal_pane.handle_pty_bytes(pane_content); @@ -199,6 +203,7 @@ pub fn scrolling_through_a_sixel_image() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let mut text_to_fill_pane = String::new(); for i in 0..30 { @@ -244,6 +249,7 @@ pub fn multiple_sixel_images_in_pane() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let mut text_to_fill_pane = String::new(); for i in 0..5 { @@ -287,6 +293,7 @@ pub fn resizing_pane_with_sixel_images() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let mut text_to_fill_pane = String::new(); for i in 0..5 { @@ -333,6 +340,7 @@ pub fn changing_character_cell_size_with_sixel_images() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let mut text_to_fill_pane = String::new(); for i in 0..5 { @@ -384,6 +392,7 @@ pub fn keep_working_after_corrupted_sixel_image() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index let sixel_image_bytes = "\u{1b}PI AM CORRUPTED BWAHAHAq @@ -433,6 +442,7 @@ pub fn pane_with_frame_position_is_on_frame() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index terminal_pane.set_content_offset(Offset::frame(1)); @@ -518,6 +528,7 @@ pub fn pane_with_bottom_and_right_borders_position_is_on_frame() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index terminal_pane.set_content_offset(Offset::shift(1, 1)); @@ -603,6 +614,7 @@ pub fn frameless_pane_position_is_on_frame() { terminal_emulator_colors, terminal_emulator_color_codes, None, + None, ); // 0 is the pane index terminal_pane.set_content_offset(Offset::default()); diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs index ba46a545..48d6e3a1 100644 --- a/zellij-server/src/plugins/mod.rs +++ b/zellij-server/src/plugins/mod.rs @@ -12,7 +12,7 @@ use zellij_utils::{ errors::{prelude::*, ContextType, PluginContext}, input::{ command::TerminalAction, - layout::{FloatingPanesLayout, Layout, PaneLayout, Run, RunPlugin, RunPluginLocation}, + layout::{FloatingPaneLayout, Layout, Run, RunPlugin, RunPluginLocation, TiledPaneLayout}, plugins::PluginsConfig, }, pane_size::Size, @@ -28,8 +28,8 @@ pub enum PluginInstruction { RemoveClient(ClientId), NewTab( Option, - Option, - Vec, + Option, + Vec, Option, // tab name usize, // tab_index ClientId, @@ -102,6 +102,11 @@ pub(crate) fn plugin_thread_main( .unwrap_or_else(|| layout.new_tab().0) .extract_run_instructions(); let size = Size::default(); + let floating_panes_layout = if floating_panes_layout.is_empty() { + layout.new_tab().1 + } else { + floating_panes_layout + }; let mut extracted_floating_plugins: Vec> = floating_panes_layout .iter() .map(|f| f.run.clone()) diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index f1b0735a..fd67234c 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -15,7 +15,7 @@ use zellij_utils::{ errors::{ContextType, PtyContext}, input::{ command::{RunCommand, TerminalAction}, - layout::{FloatingPanesLayout, Layout, PaneLayout, Run, RunPluginLocation}, + layout::{FloatingPaneLayout, Layout, Run, RunPluginLocation, TiledPaneLayout}, }, }; @@ -49,8 +49,8 @@ pub enum PtyInstruction { GoToTab(TabIndex, ClientId), NewTab( Option, - Option, - Vec, + Option, + Vec, Option, usize, // tab_index HashMap>, // plugin_ids @@ -575,8 +575,8 @@ impl Pty { } pub fn spawn_terminals_for_layout( &mut self, - layout: PaneLayout, - floating_panes_layout: Vec, + layout: TiledPaneLayout, + floating_panes_layout: Vec, default_shell: Option, plugin_ids: HashMap>, tab_index: usize, diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index dce1c9ac..3de62c09 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -165,6 +165,12 @@ pub(crate) fn route_action( .send_to_screen(screen_instr) .with_context(err_context)?; }, + Action::MovePaneBackwards => { + session + .senders + .send_to_screen(ScreenInstruction::MovePaneBackwards(client_id)) + .with_context(err_context)?; + }, Action::DumpScreen(val, full) => { session .senders @@ -436,8 +442,18 @@ pub(crate) fn route_action( .send_to_screen(ScreenInstruction::CloseFocusedPane(client_id)) .with_context(err_context)?; }, - Action::NewTab(tab_layout, floating_panes_layout, tab_name) => { + Action::NewTab( + tab_layout, + floating_panes_layout, + swap_tiled_layouts, + swap_floating_layouts, + tab_name, + ) => { let shell = session.default_shell.clone(); + let swap_tiled_layouts = + swap_tiled_layouts.unwrap_or_else(|| session.layout.swap_tiled_layouts.clone()); + let swap_floating_layouts = swap_floating_layouts + .unwrap_or_else(|| session.layout.swap_floating_layouts.clone()); session .senders .send_to_screen(ScreenInstruction::NewTab( @@ -445,6 +461,7 @@ pub(crate) fn route_action( tab_layout, floating_panes_layout, tab_name, + (swap_tiled_layouts, swap_floating_layouts), client_id, )) .with_context(err_context)?; @@ -480,10 +497,13 @@ pub(crate) fn route_action( .with_context(err_context)?; }, Action::GoToTabName(name, create) => { + let swap_tiled_layouts = session.layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = session.layout.swap_floating_layouts.clone(); session .senders .send_to_screen(ScreenInstruction::GoToTabName( name, + (swap_tiled_layouts, swap_floating_layouts), create, Some(client_id), )) @@ -626,6 +646,18 @@ pub(crate) fn route_action( .with_context(err_context)?; }, Action::ToggleMouseMode => {}, // Handled client side + Action::PreviousSwapLayout => { + session + .senders + .send_to_screen(ScreenInstruction::PreviousSwapLayout(client_id)) + .with_context(err_context)?; + }, + Action::NextSwapLayout => { + session + .senders + .send_to_screen(ScreenInstruction::NextSwapLayout(client_id)) + .with_context(err_context)?; + }, } Ok(should_break) } diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 9a196f35..a91354ee 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -12,7 +12,9 @@ use zellij_utils::input::options::Clipboard; use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::{ input::command::TerminalAction, - input::layout::{FloatingPanesLayout, PaneLayout, RunPluginLocation}, + input::layout::{ + FloatingPaneLayout, RunPluginLocation, SwapFloatingLayout, SwapTiledLayout, TiledPaneLayout, + }, position::Position, }; @@ -149,6 +151,7 @@ pub enum ScreenInstruction { MoveFocusRight(ClientId), MoveFocusRightOrNextTab(ClientId), MovePane(ClientId), + MovePaneBackwards(ClientId), MovePaneUp(ClientId), MovePaneDown(ClientId), MovePaneRight(ClientId), @@ -177,14 +180,15 @@ pub enum ScreenInstruction { UndoRenamePane(ClientId), NewTab( Option, - Option, - Vec, + Option, + Vec, Option, + (Vec, Vec), // swap layouts ClientId, ), ApplyLayout( - PaneLayout, - Vec, + TiledPaneLayout, + Vec, Vec<(u32, HoldForCommand)>, // new pane pids Vec<(u32, HoldForCommand)>, // new floating pane pids HashMap>, @@ -196,7 +200,12 @@ pub enum ScreenInstruction { ToggleActiveSyncTab(ClientId), CloseTab(ClientId), GoToTab(u32, Option), // this Option is a hacky workaround, please do not copy this behaviour - GoToTabName(String, bool, Option), + GoToTabName( + String, + (Vec, Vec), // swap layouts + bool, + Option, + ), ToggleTab(ClientId), UpdateTabName(Vec, ClientId), UndoRenameTab(ClientId), @@ -231,6 +240,8 @@ pub enum ScreenInstruction { SearchToggleWrap(ClientId), AddRedPaneFrameColorOverride(Vec, Option), // Option => optional error text ClearPaneFrameColorOverride(Vec), + PreviousSwapLayout(ClientId), + NextSwapLayout(ClientId), } impl From<&ScreenInstruction> for ScreenContext { @@ -286,6 +297,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenContext::MoveFocusRightOrNextTab }, ScreenInstruction::MovePane(..) => ScreenContext::MovePane, + ScreenInstruction::MovePaneBackwards(..) => ScreenContext::MovePaneBackwards, ScreenInstruction::MovePaneDown(..) => ScreenContext::MovePaneDown, ScreenInstruction::MovePaneUp(..) => ScreenContext::MovePaneUp, ScreenInstruction::MovePaneRight(..) => ScreenContext::MovePaneRight, @@ -370,6 +382,8 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::ClearPaneFrameColorOverride(..) => { ScreenContext::ClearPaneFrameColorOverride }, + ScreenInstruction::PreviousSwapLayout(..) => ScreenContext::PreviousSwapLayout, + ScreenInstruction::NextSwapLayout(..) => ScreenContext::NextSwapLayout, } } } @@ -430,6 +444,7 @@ pub(crate) struct Screen { default_mode_info: ModeInfo, // TODO: restructure ModeInfo to prevent this duplication style: Style, draw_pane_frames: bool, + auto_layout: bool, session_is_mirrored: bool, copy_options: CopyOptions, } @@ -442,6 +457,7 @@ impl Screen { max_panes: Option, mode_info: ModeInfo, draw_pane_frames: bool, + auto_layout: bool, session_is_mirrored: bool, copy_options: CopyOptions, ) -> Self { @@ -463,6 +479,7 @@ impl Screen { mode_info: BTreeMap::new(), default_mode_info: mode_info, draw_pane_frames, + auto_layout, session_is_mirrored, copy_options, } @@ -903,7 +920,12 @@ impl Screen { } /// Creates a new [`Tab`] in this [`Screen`] - pub fn new_tab(&mut self, tab_index: usize, client_id: ClientId) -> Result<()> { + pub fn new_tab( + &mut self, + tab_index: usize, + swap_layouts: (Vec, Vec), + client_id: ClientId, + ) -> Result<()> { let err_context = || format!("failed to create new tab for client {client_id:?}",); let client_id = if self.get_active_tab(client_id).is_ok() { @@ -932,20 +954,22 @@ impl Screen { self.style, self.default_mode_info.clone(), self.draw_pane_frames, + self.auto_layout, self.connected_clients.clone(), self.session_is_mirrored, client_id, self.copy_options.clone(), self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), + swap_layouts, ); self.tabs.insert(tab_index, tab); Ok(()) } pub fn apply_layout( &mut self, - layout: PaneLayout, - floating_panes_layout: Vec, + layout: TiledPaneLayout, + floating_panes_layout: Vec, new_terminal_ids: Vec<(u32, HoldForCommand)>, new_floating_terminal_ids: Vec<(u32, HoldForCommand)>, new_plugin_ids: HashMap>, @@ -1091,6 +1115,7 @@ impl Screen { .copied() .collect() }; + let (active_swap_layout_name, is_swap_layout_dirty) = tab.swap_layout_info(); tab_data.push(TabInfo { position: tab.position, name: tab.name.clone(), @@ -1100,6 +1125,8 @@ impl Screen { is_sync_panes_active: tab.is_sync_panes_active(), are_floating_panes_visible: tab.are_floating_panes_visible(), other_focused_clients, + active_swap_layout_name, + is_swap_layout_dirty, }); } plugin_updates.push((None, Some(*client_id), Event::TabUpdate(tab_data))); @@ -1355,6 +1382,7 @@ pub(crate) fn screen_thread_main( ) -> Result<()> { let capabilities = config_options.simplified_ui; let draw_pane_frames = config_options.pane_frames.unwrap_or(true); + let auto_layout = config_options.auto_layout.unwrap_or(true); let session_is_mirrored = config_options.mirror_session.unwrap_or(false); let copy_options = CopyOptions::new( config_options.copy_command, @@ -1374,6 +1402,7 @@ pub(crate) fn screen_thread_main( }, ), draw_pane_frames, + auto_layout, session_is_mirrored, copy_options, ); @@ -1476,7 +1505,7 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::ToggleFloatingPanes(client_id, default_shell) => { active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| tab - .toggle_floating_panes(client_id, default_shell), ?); + .toggle_floating_panes(Some(client_id), default_shell), ?); screen.unblock_input()?; screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins @@ -1570,6 +1599,7 @@ pub(crate) fn screen_thread_main( ); screen.unblock_input()?; screen.render()?; + screen.update_tabs()?; // TODO: no every time }, ScreenInstruction::SwitchFocus(client_id) => { active_tab_and_connected_client_id!( @@ -1686,6 +1716,17 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.move_active_pane(client_id) ); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; + screen.unblock_input()?; + }, + ScreenInstruction::MovePaneBackwards(client_id) => { + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_backwards(client_id) + ); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins screen.render()?; screen.unblock_input()?; }, @@ -1695,6 +1736,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_down(client_id) ); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins screen.render()?; screen.unblock_input()?; }, @@ -1704,6 +1746,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_up(client_id) ); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins screen.render()?; screen.unblock_input()?; }, @@ -1713,6 +1756,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_right(client_id) ); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins screen.render()?; screen.unblock_input()?; }, @@ -1722,6 +1766,7 @@ pub(crate) fn screen_thread_main( client_id, |tab: &mut Tab, client_id: ClientId| tab.move_active_pane_left(client_id) ); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins screen.render()?; screen.unblock_input()?; }, @@ -1851,12 +1896,16 @@ pub(crate) fn screen_thread_main( ScreenInstruction::ClosePane(id, client_id) => { match client_id { Some(client_id) => { - active_tab!(screen, client_id, |tab: &mut Tab| tab.close_pane(id, false)); + active_tab!(screen, client_id, |tab: &mut Tab| tab.close_pane( + id, + false, + Some(client_id) + )); }, None => { for tab in screen.tabs.values_mut() { if tab.get_all_pane_ids().contains(&id) { - tab.close_pane(id, false); + tab.close_pane(id, false, None); break; } } @@ -1945,10 +1994,11 @@ pub(crate) fn screen_thread_main( layout, floating_panes_layout, tab_name, + swap_layouts, client_id, ) => { let tab_index = screen.get_new_tab_index(); - screen.new_tab(tab_index, client_id)?; + screen.new_tab(tab_index, swap_layouts, client_id)?; screen .bus .senders @@ -1999,7 +2049,7 @@ pub(crate) fn screen_thread_main( screen.render()?; } }, - ScreenInstruction::GoToTabName(tab_name, create, client_id) => { + ScreenInstruction::GoToTabName(tab_name, swap_layouts, create, client_id) => { let client_id = if client_id.is_none() { None } else if screen @@ -2016,7 +2066,7 @@ pub(crate) fn screen_thread_main( screen.render()?; if create && !tab_exists { let tab_index = screen.get_new_tab_index(); - screen.new_tab(tab_index, client_id)?; + screen.new_tab(tab_index, swap_layouts, client_id)?; screen .bus .senders @@ -2044,6 +2094,7 @@ pub(crate) fn screen_thread_main( }, ScreenInstruction::TerminalResize(new_size) => { screen.resize_to_screen(new_size)?; + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins screen.render()?; }, ScreenInstruction::TerminalPixelDimensions(pixel_dimensions) => { @@ -2256,6 +2307,28 @@ pub(crate) fn screen_thread_main( } screen.render()?; }, + ScreenInstruction::PreviousSwapLayout(client_id) => { + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.previous_swap_layout(Some(client_id)), + ? + ); + screen.render()?; + screen.update_tabs()?; + screen.unblock_input()?; + }, + ScreenInstruction::NextSwapLayout(client_id) => { + active_tab_and_connected_client_id!( + screen, + client_id, + |tab: &mut Tab, client_id: ClientId| tab.next_swap_layout(Some(client_id), true), + ? + ); + screen.render()?; + screen.update_tabs()?; + screen.unblock_input()?; + }, } } Ok(()) diff --git a/zellij-server/src/tab/layout_applier.rs b/zellij-server/src/tab/layout_applier.rs index f8245f08..eed7f8c8 100644 --- a/zellij-server/src/tab/layout_applier.rs +++ b/zellij-server/src/tab/layout_applier.rs @@ -14,11 +14,11 @@ use crate::{ ClientId, }; use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::rc::Rc; use zellij_utils::{ data::{Palette, Style}, - input::layout::{FloatingPanesLayout, PaneLayout, Run, RunPluginLocation}, + input::layout::{FloatingPaneLayout, Run, RunPluginLocation, TiledPaneLayout}, pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, }; @@ -85,8 +85,8 @@ impl<'a> LayoutApplier<'a> { } pub fn apply_layout( &mut self, - layout: PaneLayout, - floating_panes_layout: Vec, + layout: TiledPaneLayout, + floating_panes_layout: Vec, new_terminal_ids: Vec<(u32, HoldForCommand)>, new_floating_terminal_ids: Vec<(u32, HoldForCommand)>, mut new_plugin_ids: HashMap>, @@ -103,28 +103,73 @@ impl<'a> LayoutApplier<'a> { )?; return Ok(layout_has_floating_panes); } + pub fn apply_tiled_panes_layout_to_existing_panes( + &mut self, + layout: &TiledPaneLayout, + refocus_pane: bool, + client_id: Option, + ) -> Result<()> { + let err_context = || format!("failed to apply tiled panes layout"); + let free_space = self.total_space_for_tiled_panes(); + let tiled_panes_count = self.tiled_panes.visible_panes_count(); + match layout.position_panes_in_space(&free_space, Some(tiled_panes_count)) { + Ok(positions_in_layout) => { + let currently_focused_pane_id = + client_id.and_then(|client_id| self.tiled_panes.focused_pane_id(client_id)); + let mut existing_tab_state = + ExistingTabState::new(self.tiled_panes.drain(), currently_focused_pane_id); + let mut pane_focuser = PaneFocuser::new(refocus_pane); + for (layout, position_and_size) in positions_in_layout { + if let Some(mut pane) = existing_tab_state.find_and_extract_pane( + &layout.run, + &position_and_size, + layout.focus.unwrap_or(false), + true, + ) { + self.apply_layout_properties_to_pane( + &mut pane, + &layout, + Some(position_and_size), + ); + pane_focuser.set_pane_id_in_focused_location(layout.focus, &pane); + resize_pty!(pane, self.os_api, self.senders)?; + self.tiled_panes + .add_pane_with_existing_geom(pane.pid(), pane); + } + } + let remaining_pane_ids: Vec = existing_tab_state.pane_ids(); + for pane_id in remaining_pane_ids { + if let Some(mut pane) = existing_tab_state.remove_pane(&pane_id) { + self.apply_layout_properties_to_pane(&mut pane, &layout, None); + self.tiled_panes.insert_pane(pane.pid(), pane); + } + } + pane_focuser.focus_tiled_pane(&mut self.tiled_panes); + }, + Err(e) => { + Err::<(), _>(anyError::msg(e)) + .with_context(err_context) + .non_fatal(); // TODO: propagate this to the user + }, + }; + Ok(()) + } fn apply_tiled_panes_layout( &mut self, - layout: PaneLayout, + layout: TiledPaneLayout, new_terminal_ids: Vec<(u32, HoldForCommand)>, new_plugin_ids: &mut HashMap>, client_id: ClientId, ) -> Result<()> { let err_context = || format!("failed to apply tiled panes layout"); - let (viewport_cols, viewport_rows) = { - let viewport = self.viewport.borrow(); - (viewport.cols, viewport.rows) - }; - let mut free_space = PaneGeom::default(); - free_space.cols.set_inner(viewport_cols); - free_space.rows.set_inner(viewport_rows); - match layout.position_panes_in_space(&free_space) { + let free_space = self.total_space_for_tiled_panes(); + match layout.position_panes_in_space(&free_space, None) { Ok(positions_in_layout) => { let positions_and_size = positions_in_layout.iter(); let mut new_terminal_ids = new_terminal_ids.iter(); let mut focus_pane_id: Option = None; - let mut set_focus_pane_id = |layout: &PaneLayout, pane_id: PaneId| { + let mut set_focus_pane_id = |layout: &TiledPaneLayout, pane_id: PaneId| { if layout.focus.unwrap_or(false) && focus_pane_id.is_none() { focus_pane_id = Some(pane_id); } @@ -154,6 +199,7 @@ impl<'a> LayoutApplier<'a> { self.link_handler.clone(), self.character_cell_size.clone(), self.style, + layout.run.clone(), ); new_plugin.set_borderless(layout.borderless); self.tiled_panes @@ -180,6 +226,7 @@ impl<'a> LayoutApplier<'a> { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), initial_title, + layout.run.clone(), ); new_pane.set_borderless(layout.borderless); if let Some(held_command) = hold_for_command { @@ -216,7 +263,7 @@ impl<'a> LayoutApplier<'a> { } fn apply_floating_panes_layout( &mut self, - floating_panes_layout: Vec, + floating_panes_layout: Vec, new_floating_terminal_ids: Vec<(u32, HoldForCommand)>, new_plugin_ids: &mut HashMap>, layout_name: Option, @@ -254,6 +301,7 @@ impl<'a> LayoutApplier<'a> { self.link_handler.clone(), self.character_cell_size.clone(), self.style, + floating_pane_layout.run.clone(), ); new_pane.set_borderless(false); new_pane.set_content_offset(Offset::frame(1)); @@ -285,6 +333,7 @@ impl<'a> LayoutApplier<'a> { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), initial_title, + floating_pane_layout.run.clone(), ); new_pane.set_borderless(false); new_pane.set_content_offset(Offset::frame(1)); @@ -309,6 +358,80 @@ impl<'a> LayoutApplier<'a> { Ok(false) } } + pub fn apply_floating_panes_layout_to_existing_panes( + &mut self, + floating_panes_layout: &Vec, + refocus_pane: bool, + client_id: Option, + ) -> Result { + // true => has floating panes + let mut layout_has_floating_panes = false; + let layout_has_focused_pane = floating_panes_layout + .iter() + .find(|f| f.focus.map(|f| f).unwrap_or(false)) + .is_some(); + let floating_panes_layout = floating_panes_layout.iter(); + let currently_focused_pane_id = self + .floating_panes + .active_pane_id_or_focused_pane_id(client_id); + let mut existing_tab_state = + ExistingTabState::new(self.floating_panes.drain(), currently_focused_pane_id); + let mut pane_focuser = PaneFocuser::new(refocus_pane); + for floating_pane_layout in floating_panes_layout { + let position_and_size = self + .floating_panes + .position_floating_pane_layout(&floating_pane_layout); + let is_focused = floating_pane_layout.focus.unwrap_or(false); + if let Some(mut pane) = existing_tab_state.find_and_extract_pane( + &floating_pane_layout.run, + &position_and_size, + is_focused, + false, + ) { + layout_has_floating_panes = true; + self.apply_floating_pane_layout_properties_to_pane( + &mut pane, + Some(&floating_pane_layout), + position_and_size, + ); + let pane_is_focused = floating_pane_layout + .focus + .or(Some(!layout_has_focused_pane)); + pane_focuser.set_pane_id_in_focused_location(pane_is_focused, &pane); + resize_pty!(pane, self.os_api, self.senders)?; + self.floating_panes.add_pane(pane.pid(), pane); + } + } + let remaining_pane_ids: Vec = existing_tab_state.pane_ids(); + for pane_id in remaining_pane_ids { + match self.floating_panes.find_room_for_new_pane() { + Some(position_and_size) => { + if let Some(mut pane) = existing_tab_state.remove_pane(&pane_id) { + layout_has_floating_panes = true; + self.apply_floating_pane_layout_properties_to_pane( + &mut pane, + None, + position_and_size, + ); + pane_focuser + .set_pane_id_in_focused_location(Some(!layout_has_focused_pane), &pane); + resize_pty!(pane, self.os_api, self.senders)?; + self.floating_panes.add_pane(pane.pid(), pane); + } + }, + None => { + log::error!("could not find room for pane!") + }, + } + } + + if layout_has_floating_panes { + pane_focuser.focus_floating_pane(&mut self.floating_panes, &mut self.os_api); + Ok(true) + } else { + Ok(false) + } + } fn resize_whole_tab(&mut self, new_screen_size: Size) -> Result<()> { let err_context = || { format!( @@ -316,7 +439,6 @@ impl<'a> LayoutApplier<'a> { new_screen_size ) }; - self.floating_panes.resize(new_screen_size); // we need to do this explicitly because floating_panes.resize does not do this self.floating_panes @@ -385,4 +507,217 @@ impl<'a> LayoutApplier<'a> { } } } + fn apply_layout_properties_to_pane( + &self, + pane: &mut Box, + layout: &TiledPaneLayout, + position_and_size: Option, + ) { + if let Some(position_and_size) = position_and_size { + pane.set_geom(position_and_size); + } + pane.set_borderless(layout.borderless); + if let Some(pane_title) = layout.name.as_ref() { + pane.set_title(pane_title.into()); + } + } + fn apply_floating_pane_layout_properties_to_pane( + &self, + pane: &mut Box, + floating_pane_layout: Option<&FloatingPaneLayout>, + position_and_size: PaneGeom, + ) { + pane.set_geom(position_and_size); + pane.set_borderless(false); + if let Some(pane_title) = floating_pane_layout.and_then(|f| f.name.clone()) { + pane.set_title(pane_title); + } + pane.set_content_offset(Offset::frame(1)); + } + fn total_space_for_tiled_panes(&self) -> PaneGeom { + // for tiled panes we need to take the display area rather than the viewport because the + // viewport can potentially also be changed + let (display_area_cols, display_area_rows) = { + let display_area = self.display_area.borrow(); + (display_area.cols, display_area.rows) + }; + + let mut free_space = PaneGeom::default(); + free_space.cols.set_inner(display_area_cols); + free_space.rows.set_inner(display_area_rows); + free_space + } +} + +struct ExistingTabState { + existing_panes: BTreeMap>, + currently_focused_pane_id: Option, +} + +impl ExistingTabState { + pub fn new( + existing_panes: BTreeMap>, + currently_focused_pane_id: Option, + ) -> Self { + ExistingTabState { + existing_panes, + currently_focused_pane_id, + } + } + pub fn find_and_extract_pane( + &mut self, + run: &Option, + position_and_size: &PaneGeom, + is_focused: bool, + default_to_closest_position: bool, + ) -> Option> { + let candidates = self.pane_candidates(run, position_and_size, default_to_closest_position); + if let Some(current_pane_id_with_same_contents) = + self.find_pane_id_with_same_contents(&candidates, run) + { + return self + .existing_panes + .remove(¤t_pane_id_with_same_contents); + } else if let Some(currently_focused_pane_id) = + self.find_focused_pane_id(is_focused, &candidates) + { + return self.existing_panes.remove(¤tly_focused_pane_id); + } else if let Some(same_position_candidate_id) = candidates + .iter() + .find(|(_, p)| p.position_and_size() == *position_and_size) + .map(|(pid, _p)| *pid) + .copied() + { + return self.existing_panes.remove(&same_position_candidate_id); + } else if let Some(first_candidate) = + candidates.iter().next().map(|(pid, _p)| *pid).copied() + { + return self.existing_panes.remove(&first_candidate); + } + None + } + pub fn pane_ids(&self) -> Vec { + self.existing_panes.keys().copied().collect() + } + pub fn remove_pane(&mut self, pane_id: &PaneId) -> Option> { + self.existing_panes.remove(pane_id) + } + fn pane_candidates( + &self, + run: &Option, + position_and_size: &PaneGeom, + default_to_closest_position: bool, + ) -> Vec<(&PaneId, &Box)> { + let mut candidates: Vec<_> = self.existing_panes.iter().collect(); + candidates.sort_by(|(_a_id, a), (_b_id, b)| { + let a_invoked_with = a.invoked_with(); + let b_invoked_with = b.invoked_with(); + if Run::is_same_category(run, a_invoked_with) + && !Run::is_same_category(run, b_invoked_with) + { + std::cmp::Ordering::Less + } else if Run::is_same_category(run, b_invoked_with) + && !Run::is_same_category(run, a_invoked_with) + { + std::cmp::Ordering::Greater + } else if Run::is_terminal(a_invoked_with) && !Run::is_terminal(b_invoked_with) { + // we place terminals before everything else because when we can't find + // an exact match, we need to prefer terminals are more often than not + // we'd be doing the right thing here + std::cmp::Ordering::Less + } else if Run::is_terminal(b_invoked_with) && !Run::is_terminal(a_invoked_with) { + std::cmp::Ordering::Greater + } else { + // try to find the closest pane + if default_to_closest_position { + let abs = |a, b| (a as isize - b as isize).abs(); + let a_x_distance = abs(a.position_and_size().x, position_and_size.x); + let a_y_distance = abs(a.position_and_size().y, position_and_size.y); + let b_x_distance = abs(b.position_and_size().x, position_and_size.x); + let b_y_distance = abs(b.position_and_size().y, position_and_size.y); + (a_x_distance + a_y_distance).cmp(&(b_x_distance + b_y_distance)) + } else { + std::cmp::Ordering::Equal + } + } + }); + candidates + } + fn find_focused_pane_id( + &self, + is_focused: bool, + candidates: &Vec<(&PaneId, &Box)>, + ) -> Option { + if is_focused { + candidates + .iter() + .find(|(pid, _p)| Some(**pid) == self.currently_focused_pane_id) + .map(|(pid, _p)| *pid) + .copied() + } else { + None + } + } + fn find_pane_id_with_same_contents( + &self, + candidates: &Vec<(&PaneId, &Box)>, + run: &Option, + ) -> Option { + candidates + .iter() + .find(|(_pid, p)| p.invoked_with() == run) + .map(|(pid, _p)| *pid) + .copied() + } +} + +#[derive(Default, Debug)] +struct PaneFocuser { + refocus_pane: bool, + pane_id_in_focused_location: Option, +} + +impl PaneFocuser { + pub fn new(refocus_pane: bool) -> Self { + PaneFocuser { + refocus_pane, + ..Default::default() + } + } + pub fn set_pane_id_in_focused_location( + &mut self, + is_focused: Option, + pane: &Box, + ) { + if is_focused.unwrap_or(false) && pane.selectable() { + self.pane_id_in_focused_location = Some(pane.pid()); + } + } + pub fn focus_tiled_pane(&self, tiled_panes: &mut TiledPanes) { + match self.pane_id_in_focused_location { + Some(pane_id_in_focused_location) => { + if self.refocus_pane { + tiled_panes.reapply_pane_focus(); + tiled_panes.switch_active_pane_with(pane_id_in_focused_location); + } else { + tiled_panes.reapply_pane_focus(); + } + }, + None => { + tiled_panes.reapply_pane_focus(); + }, + } + } + pub fn focus_floating_pane( + &self, + floating_panes: &mut FloatingPanes, + os_api: &mut Box, + ) { + floating_panes.reapply_pane_focus(); + if let Some(pane_id_in_focused_location) = self.pane_id_in_focused_location { + if self.refocus_pane { + floating_panes.switch_active_pane_with(os_api, pane_id_in_focused_location); + } + } + } } diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 0dcd7ba5..e99794cf 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -4,6 +4,7 @@ mod clipboard; mod copy_command; mod layout_applier; +mod swap_layouts; use copy_command::CopyCommand; use std::env::temp_dir; @@ -19,6 +20,7 @@ use crate::pty_writer::PtyWriteInstruction; use crate::screen::CopyOptions; use crate::ui::pane_boundaries_frame::FrameParams; use layout_applier::LayoutApplier; +use swap_layouts::SwapLayouts; use self::clipboard::ClipboardProvider; use crate::{ @@ -44,7 +46,10 @@ use zellij_utils::{ data::{Event, InputMode, ModeInfo, Palette, PaletteColor, Style}, input::{ command::TerminalAction, - layout::{FloatingPanesLayout, PaneLayout, RunPluginLocation}, + layout::{ + FloatingPaneLayout, Run, RunPluginLocation, SwapFloatingLayout, SwapTiledLayout, + TiledPaneLayout, + }, parse_keys, }, pane_size::{Offset, PaneGeom, Size, SizeInPixels, Viewport}, @@ -108,6 +113,7 @@ pub(crate) struct Tab { pub style: Style, connected_clients: Rc>>, draw_pane_frames: bool, + auto_layout: bool, pending_vte_events: HashMap>, pub selecting_with_mouse: bool, // this is only pub for the tests TODO: remove this once we combine write_text_to_clipboard with render link_handler: Rc>, @@ -125,7 +131,8 @@ pub(crate) struct Tab { // cursor_shape_csi) is_pending: bool, // a pending tab is one that is still being loaded or otherwise waiting pending_instructions: Vec, // instructions that came while the tab was - // pending and need to be re-applied + // pending and need to be re-applied + swap_layouts: SwapLayouts, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -392,6 +399,8 @@ pub trait Pane { fn add_red_pane_frame_color_override(&mut self, _error_text: Option); fn clear_pane_frame_color_override(&mut self); fn frame_color_override(&self) -> Option; + fn invoked_with(&self) -> &Option; + fn set_title(&mut self, title: String); } #[derive(Clone, Debug)] @@ -437,12 +446,14 @@ impl Tab { style: Style, default_mode_info: ModeInfo, draw_pane_frames: bool, + auto_layout: bool, connected_clients_in_app: Rc>>, session_is_mirrored: bool, client_id: ClientId, copy_options: CopyOptions, terminal_emulator_colors: Rc>, terminal_emulator_color_codes: Rc>>, + swap_layouts: (Vec, Vec), ) -> Self { let name = if name.is_empty() { format!("Tab #{}", index + 1) @@ -489,6 +500,7 @@ impl Tab { Some(command) => ClipboardProvider::Command(CopyCommand::new(command)), None => ClipboardProvider::Osc52(copy_options.clipboard), }; + let swap_layouts = SwapLayouts::new(swap_layouts, display_area.clone()); Tab { index, @@ -511,6 +523,7 @@ impl Tab { mode_info, default_mode_info, draw_pane_frames, + auto_layout, pending_vte_events: HashMap::new(), connected_clients, selecting_with_mouse: false, @@ -525,18 +538,21 @@ impl Tab { cursor_positions_and_shape: HashMap::new(), is_pending: true, // will be switched to false once the layout is applied pending_instructions: vec![], + swap_layouts, } } pub fn apply_layout( &mut self, - layout: PaneLayout, - floating_panes_layout: Vec, + layout: TiledPaneLayout, + floating_panes_layout: Vec, new_terminal_ids: Vec<(u32, HoldForCommand)>, new_floating_terminal_ids: Vec<(u32, HoldForCommand)>, new_plugin_ids: HashMap>, client_id: ClientId, ) -> Result<()> { + self.swap_layouts + .set_base_layout((layout.clone(), floating_panes_layout.clone())); let layout_has_floating_panes = LayoutApplier::new( &self.viewport, &self.senders, @@ -563,14 +579,153 @@ impl Tab { )?; if layout_has_floating_panes { if !self.floating_panes.panes_are_visible() { - self.toggle_floating_panes(client_id, None)?; + self.toggle_floating_panes(Some(client_id), None)?; } } - self.tiled_panes.set_pane_frames(self.draw_pane_frames); + self.tiled_panes.reapply_pane_frames(); self.is_pending = false; self.apply_buffered_instructions()?; Ok(()) } + pub fn swap_layout_info(&self) -> (Option, bool) { + if self.floating_panes.panes_are_visible() { + self.swap_layouts.floating_layout_info() + } else { + let selectable_tiled_panes = + self.tiled_panes.get_panes().filter(|(_, p)| p.selectable()); + if selectable_tiled_panes.count() > 1 { + self.swap_layouts.tiled_layout_info() + } else { + // no layout for single pane + (None, false) + } + } + } + fn relayout_floating_panes( + &mut self, + client_id: Option, + search_backwards: bool, + refocus_pane: bool, + ) -> Result<()> { + if let Some(layout_candidate) = self + .swap_layouts + .swap_floating_panes(&self.floating_panes, search_backwards) + { + LayoutApplier::new( + &self.viewport, + &self.senders, + &self.sixel_image_store, + &self.link_handler, + &self.terminal_emulator_colors, + &self.terminal_emulator_color_codes, + &self.character_cell_size, + &self.style, + &self.display_area, + &mut self.tiled_panes, + &mut self.floating_panes, + self.draw_pane_frames, + &mut self.focus_pane_id, + &self.os_api, + ) + .apply_floating_panes_layout_to_existing_panes( + &layout_candidate, + refocus_pane, + client_id, + )?; + } + self.is_pending = false; + self.apply_buffered_instructions()?; + self.set_force_render(); + Ok(()) + } + fn relayout_tiled_panes( + &mut self, + client_id: Option, + search_backwards: bool, + refocus_pane: bool, + best_effort: bool, + ) -> Result<()> { + if self.tiled_panes.fullscreen_is_active() { + self.tiled_panes.unset_fullscreen(); + } + let refocus_pane = if self.swap_layouts.is_tiled_damaged() { + false + } else { + refocus_pane + }; + if let Some(layout_candidate) = self + .swap_layouts + .swap_tiled_panes(&self.tiled_panes, search_backwards) + .or_else(|| { + if best_effort { + self.swap_layouts + .best_effort_tiled_layout(&self.tiled_panes) + } else { + None + } + }) + { + LayoutApplier::new( + &self.viewport, + &self.senders, + &self.sixel_image_store, + &self.link_handler, + &self.terminal_emulator_colors, + &self.terminal_emulator_color_codes, + &self.character_cell_size, + &self.style, + &self.display_area, + &mut self.tiled_panes, + &mut self.floating_panes, + self.draw_pane_frames, + &mut self.focus_pane_id, + &self.os_api, + ) + .apply_tiled_panes_layout_to_existing_panes( + &layout_candidate, + refocus_pane, + client_id, + )?; + } + self.tiled_panes.reapply_pane_frames(); + self.is_pending = false; + self.apply_buffered_instructions()?; + let display_area = *self.display_area.borrow(); + // we do this so that the new swap layout has a chance to pass through the constraint system + self.tiled_panes.resize(display_area); + self.should_clear_display_before_rendering = true; + Ok(()) + } + pub fn previous_swap_layout(&mut self, client_id: Option) -> Result<()> { + // warning, here we cache resizes rather than sending them to the pty, we do that in + // apply_cached_resizes below - beware when bailing on this function early! + self.os_api.cache_resizes(); + let search_backwards = true; + if self.floating_panes.panes_are_visible() { + self.relayout_floating_panes(client_id, search_backwards, true)?; + } else { + self.relayout_tiled_panes(client_id, search_backwards, true, false)?; + } + self.os_api.apply_cached_resizes(); + Ok(()) + } + pub fn next_swap_layout( + &mut self, + client_id: Option, + refocus_pane: bool, + ) -> Result<()> { + // warning, here we cache resizes rather than sending them to the pty, we do that in + // apply_cached_resizes below - beware when bailing on this function early! + self.os_api.cache_resizes(); + let search_backwards = false; + if self.floating_panes.panes_are_visible() { + self.relayout_floating_panes(client_id, search_backwards, refocus_pane)?; + } else { + self.relayout_tiled_panes(client_id, search_backwards, refocus_pane, false)?; + } + self.os_api.apply_cached_resizes(); + Ok(()) + } pub fn apply_buffered_instructions(&mut self) -> Result<()> { let buffered_instructions: Vec = self.pending_instructions.drain(..).collect(); @@ -702,7 +857,7 @@ impl Tab { if let Some(focused_floating_pane_id) = self.floating_panes.active_pane_id(client_id) { if self.tiled_panes.has_room_for_new_pane() { let floating_pane_to_embed = self - .close_pane(focused_floating_pane_id, true) + .close_pane(focused_floating_pane_id, true, Some(client_id)) .with_context(|| format!( "failed to find floating pane (ID: {focused_floating_pane_id:?}) to embed for client {client_id}", )) @@ -713,6 +868,13 @@ impl Tab { self.tiled_panes .focus_pane(focused_floating_pane_id, client_id); self.hide_floating_panes(); + if self.auto_layout && !self.swap_layouts.is_tiled_damaged() { + // only do this if we're already in this layout, otherwise it might be + // confusing and not what the user intends + self.swap_layouts.set_is_tiled_damaged(); // we do this so that we won't skip to the + // next layout + self.next_swap_layout(Some(client_id), true)?; + } } } } else if let Some(focused_pane_id) = self.tiled_panes.focused_pane_id(client_id) { @@ -721,7 +883,9 @@ impl Tab { // don't close the only pane on screen... return Ok(()); } - if let Some(mut embedded_pane_to_float) = self.close_pane(focused_pane_id, true) { + if let Some(mut embedded_pane_to_float) = + self.close_pane(focused_pane_id, true, Some(client_id)) + { if !embedded_pane_to_float.borderless() { // floating panes always have a frame unless they're explicitly borderless embedded_pane_to_float.set_content_offset(Offset::frame(1)); @@ -734,6 +898,13 @@ impl Tab { .add_pane(focused_pane_id, embedded_pane_to_float); self.floating_panes.focus_pane(focused_pane_id, client_id); self.show_floating_panes(); + if self.auto_layout && !self.swap_layouts.is_floating_damaged() { + // only do this if we're already in this layout, otherwise it might be + // confusing and not what the user intends + self.swap_layouts.set_is_floating_damaged(); // we do this so that we won't skip to the + // next layout + self.next_swap_layout(Some(client_id), true)?; + } } } } @@ -741,7 +912,7 @@ impl Tab { } pub fn toggle_floating_panes( &mut self, - client_id: ClientId, + client_id: Option, default_shell: Option, ) -> Result<()> { if self.floating_panes.panes_are_visible() { @@ -750,24 +921,34 @@ impl Tab { } else { self.show_floating_panes(); match self.floating_panes.last_floating_pane_id() { - Some(first_floating_pane_id) => { - if !self.floating_panes.active_panes_contain(&client_id) { + Some(first_floating_pane_id) => match client_id { + Some(client_id) => { + if !self.floating_panes.active_panes_contain(&client_id) { + self.floating_panes + .focus_pane(first_floating_pane_id, client_id); + } + }, + None => { self.floating_panes - .focus_pane(first_floating_pane_id, client_id); - } + .focus_pane_for_all_clients(first_floating_pane_id); + }, }, None => { let name = None; let should_float = true; + let client_id_or_tab_index = match client_id { + Some(client_id) => ClientOrTabIndex::ClientId(client_id), + None => ClientOrTabIndex::TabIndex(self.index), + }; let instruction = PtyInstruction::SpawnTerminal( default_shell, Some(should_float), name, - ClientOrTabIndex::ClientId(client_id), + client_id_or_tab_index, ); - self.senders.send_to_pty(instruction).with_context(|| { - format!("failed to open a floating pane for client {client_id}") - })?; + self.senders + .send_to_pty(instruction) + .with_context(|| format!("failed to open a floating pane for client"))?; }, } self.floating_panes.set_force_render(); @@ -807,12 +988,21 @@ impl Tab { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), initial_pane_title, + None, ); + new_pane.set_active_at(Instant::now()); new_pane.set_content_offset(Offset::frame(1)); // floating panes always have a frame resize_pty!(new_pane, self.os_api, self.senders).with_context(err_context)?; self.floating_panes.add_pane(pid, Box::new(new_pane)); self.floating_panes.focus_pane_for_all_clients(pid); } + if self.auto_layout && !self.swap_layouts.is_floating_damaged() { + // only do this if we're already in this layout, otherwise it might be + // confusing and not what the user intends + self.swap_layouts.set_is_floating_damaged(); // we do this so that we won't skip to the + // next layout + self.next_swap_layout(client_id, true)?; + } } } else { if self.tiled_panes.fullscreen_is_active() { @@ -821,7 +1011,7 @@ impl Tab { if self.tiled_panes.has_room_for_new_pane() { if let PaneId::Terminal(term_pid) = pid { let next_terminal_position = self.get_next_terminal_position(); - let new_terminal = TerminalPane::new( + let mut new_terminal = TerminalPane::new( term_pid, PaneGeom::default(), // the initial size will be set later self.style, @@ -833,7 +1023,9 @@ impl Tab { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), initial_pane_title, + None, ); + new_terminal.set_active_at(Instant::now()); self.tiled_panes.insert_pane(pid, Box::new(new_terminal)); self.should_clear_display_before_rendering = true; if let Some(client_id) = client_id { @@ -841,6 +1033,13 @@ impl Tab { } } } + if self.auto_layout && !self.swap_layouts.is_tiled_damaged() { + // only do this if we're already in this layout, otherwise it might be + // confusing and not what the user intends + self.swap_layouts.set_is_tiled_damaged(); // we do this so that we won't skip to the + // next layout + self.next_swap_layout(client_id, true)?; + } } Ok(()) } @@ -865,6 +1064,7 @@ impl Tab { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), None, + None, ); new_pane.update_name("EDITING SCROLLBACK"); // we do this here and not in the // constructor so it won't be overrided @@ -934,11 +1134,13 @@ impl Tab { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), initial_pane_title, + None, ); self.tiled_panes .split_pane_horizontally(pid, Box::new(new_terminal), client_id); self.should_clear_display_before_rendering = true; self.tiled_panes.focus_pane(pid, client_id); + self.swap_layouts.set_is_tiled_damaged(); } } else { log::error!("No room to split pane horizontally"); @@ -946,7 +1148,7 @@ impl Tab { self.senders .send_to_background_jobs(BackgroundJob::DisplayPaneError( vec![active_pane_id], - "TOO SMALL!".into(), + "CAN'T SPLIT!".into(), )) .with_context(err_context)?; } @@ -988,11 +1190,13 @@ impl Tab { self.terminal_emulator_colors.clone(), self.terminal_emulator_color_codes.clone(), initial_pane_title, + None, ); self.tiled_panes .split_pane_vertically(pid, Box::new(new_terminal), client_id); self.should_clear_display_before_rendering = true; self.tiled_panes.focus_pane(pid, client_id); + self.swap_layouts.set_is_tiled_damaged(); } } else { log::error!("No room to split pane vertically"); @@ -1000,7 +1204,7 @@ impl Tab { self.senders .send_to_background_jobs(BackgroundJob::DisplayPaneError( vec![active_pane_id], - "TOO SMALL!".into(), + "CAN'T SPLIT!".into(), )) .with_context(err_context)?; } @@ -1285,7 +1489,7 @@ impl Tab { .with_context(err_context)?; }, Some(AdjustedInput::CloseThisPane) => { - self.close_pane(PaneId::Terminal(active_terminal_id), false); + self.close_pane(PaneId::Terminal(active_terminal_id), false, None); should_update_ui = true; }, None => {}, @@ -1522,19 +1726,35 @@ impl Tab { selectable_tiled_panes.count() > 0 } pub fn resize_whole_tab(&mut self, new_screen_size: Size) -> Result<()> { + // warning, here we cache resizes rather than sending them to the pty, we do that in + // apply_cached_resizes below - beware when bailing on this function early! + self.os_api.cache_resizes(); let err_context = || format!("failed to resize whole tab (index {})", self.index); - self.floating_panes.resize(new_screen_size); // we need to do this explicitly because floating_panes.resize does not do this self.floating_panes .resize_pty_all_panes(&mut self.os_api) .with_context(err_context)?; self.tiled_panes.resize(new_screen_size); + if self.auto_layout && !self.swap_layouts.is_floating_damaged() { + // we do this only for floating panes, because the constraint system takes care of the + // tiled panes + self.swap_layouts.set_is_floating_damaged(); + let _ = self.relayout_floating_panes(None, false, false); + } + if self.auto_layout && !self.swap_layouts.is_tiled_damaged() && !self.is_fullscreen_active() + { + self.swap_layouts.set_is_tiled_damaged(); + let _ = self.relayout_tiled_panes(None, false, false, true); + } self.should_clear_display_before_rendering = true; + let _ = self.os_api.apply_cached_resizes(); Ok(()) } pub fn resize(&mut self, client_id: ClientId, strategy: ResizeStrategy) -> Result<()> { let err_context = || format!("unable to resize pane"); + self.swap_layouts.set_is_floating_damaged(); + self.swap_layouts.set_is_tiled_damaged(); if self.floating_panes.panes_are_visible() { let successfully_resized = self .floating_panes @@ -1691,11 +1911,35 @@ impl Tab { if self.tiled_panes.fullscreen_is_active() { return; } - self.tiled_panes.move_active_pane(client_id); + let search_backwards = false; + if self.floating_panes.panes_are_visible() { + self.floating_panes + .move_active_pane(search_backwards, &mut self.os_api, client_id); + } else { + self.tiled_panes + .move_active_pane(search_backwards, client_id); + } + } + pub fn move_active_pane_backwards(&mut self, client_id: ClientId) { + if !self.has_selectable_panes() { + return; + } + if self.tiled_panes.fullscreen_is_active() { + return; + } + let search_backwards = true; + if self.floating_panes.panes_are_visible() { + self.floating_panes + .move_active_pane(search_backwards, &mut self.os_api, client_id); + } else { + self.tiled_panes + .move_active_pane(search_backwards, client_id); + } } pub fn move_active_pane_down(&mut self, client_id: ClientId) { if self.floating_panes.panes_are_visible() { self.floating_panes.move_active_pane_down(client_id); + self.swap_layouts.set_is_floating_damaged(); self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind } else { if !self.has_selectable_panes() { @@ -1710,6 +1954,7 @@ impl Tab { pub fn move_active_pane_up(&mut self, client_id: ClientId) { if self.floating_panes.panes_are_visible() { self.floating_panes.move_active_pane_up(client_id); + self.swap_layouts.set_is_floating_damaged(); self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind } else { if !self.has_selectable_panes() { @@ -1724,6 +1969,7 @@ impl Tab { pub fn move_active_pane_right(&mut self, client_id: ClientId) { if self.floating_panes.panes_are_visible() { self.floating_panes.move_active_pane_right(client_id); + self.swap_layouts.set_is_floating_damaged(); self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind } else { if !self.has_selectable_panes() { @@ -1738,6 +1984,7 @@ impl Tab { pub fn move_active_pane_left(&mut self, client_id: ClientId) { if self.floating_panes.panes_are_visible() { self.floating_panes.move_active_pane_left(client_id); + self.swap_layouts.set_is_floating_damaged(); self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind } else { if !self.has_selectable_panes() { @@ -1756,7 +2003,7 @@ impl Tab { self.senders .send_to_pty(PtyInstruction::ClosePane(pid)) .context("failed to close down to max terminals")?; - self.close_pane(pid, false); + self.close_pane(pid, false, None); } } Ok(()) @@ -1803,6 +2050,7 @@ impl Tab { &mut self, id: PaneId, ignore_suppressed_panes: bool, + client_id: Option, ) -> Option> { // we need to ignore suppressed panes when we toggle a pane to be floating/embedded(tiled) // this is because in that case, while we do use this logic, we're not actually closing the @@ -1829,6 +2077,15 @@ impl Tab { } self.set_force_render(); self.floating_panes.set_force_render(); + if self.auto_layout + && !self.swap_layouts.is_floating_damaged() + && self.floating_panes.visible_panes_count() > 0 + { + self.swap_layouts.set_is_floating_damaged(); + // only relayout if the user is already "in" a layout, otherwise this might be + // confusing + let _ = self.next_swap_layout(client_id, false); + } closed_pane } else { if self.tiled_panes.fullscreen_is_active() { @@ -1837,6 +2094,17 @@ impl Tab { let closed_pane = self.tiled_panes.remove_pane(id); self.set_force_render(); self.tiled_panes.set_force_render(); + let closed_pane_is_stacked = closed_pane + .as_ref() + .map(|p| p.position_and_size().is_stacked) + .unwrap_or(false); + if self.auto_layout && !self.swap_layouts.is_tiled_damaged() && !closed_pane_is_stacked + { + self.swap_layouts.set_is_tiled_damaged(); + // only relayout if the user is already "in" a layout, otherwise this might be + // confusing + let _ = self.next_swap_layout(client_id, false); + } closed_pane } } @@ -1905,7 +2173,7 @@ impl Tab { if self.floating_panes.panes_are_visible() { if let Some(active_floating_pane_id) = self.floating_panes.active_pane_id(client_id) { - self.close_pane(active_floating_pane_id, false); + self.close_pane(active_floating_pane_id, false, Some(client_id)); self.senders .send_to_pty(PtyInstruction::ClosePane(active_floating_pane_id)) .with_context(|| err_context(active_floating_pane_id))?; @@ -1913,7 +2181,7 @@ impl Tab { } } if let Some(active_pane_id) = self.tiled_panes.get_active_pane_id(client_id) { - self.close_pane(active_pane_id, false); + self.close_pane(active_pane_id, false, Some(client_id)); self.senders .send_to_pty(PtyInstruction::ClosePane(active_pane_id)) .with_context(|| err_context(active_pane_id))?; @@ -2212,6 +2480,7 @@ impl Tab { .floating_panes .move_pane_with_mouse(*position, search_selectable) { + self.swap_layouts.set_is_floating_damaged(); self.set_force_render(); return Ok(()); } @@ -2472,6 +2741,7 @@ impl Tab { .floating_panes .move_pane_with_mouse(*position_on_screen, search_selectable) { + self.swap_layouts.set_is_floating_damaged(); self.set_force_render(); return Ok(!is_repeated); // we don't need to re-render in this case if the pane did not move // return; diff --git a/zellij-server/src/tab/swap_layouts.rs b/zellij-server/src/tab/swap_layouts.rs new file mode 100644 index 00000000..57085779 --- /dev/null +++ b/zellij-server/src/tab/swap_layouts.rs @@ -0,0 +1,284 @@ +use crate::panes::{FloatingPanes, TiledPanes}; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::rc::Rc; +use zellij_utils::{ + input::layout::{ + FloatingPaneLayout, LayoutConstraint, SwapFloatingLayout, SwapTiledLayout, TiledPaneLayout, + }, + pane_size::{PaneGeom, Size}, +}; + +#[derive(Clone, Debug, Default)] +pub struct SwapLayouts { + swap_tiled_layouts: Vec, + swap_floating_layouts: Vec, + current_floating_layout_position: usize, + current_tiled_layout_position: usize, + is_floating_damaged: bool, + is_tiled_damaged: bool, + display_area: Rc>, // includes all panes (including eg. the status bar and tab bar in the default layout) +} + +impl SwapLayouts { + pub fn new( + swap_layouts: (Vec, Vec), + display_area: Rc>, + ) -> Self { + let display_area = display_area.clone(); + SwapLayouts { + swap_tiled_layouts: swap_layouts.0, + swap_floating_layouts: swap_layouts.1, + is_floating_damaged: false, + is_tiled_damaged: false, + display_area, + ..Default::default() + } + } + pub fn set_base_layout(&mut self, layout: (TiledPaneLayout, Vec)) { + let mut base_swap_tiled_layout = BTreeMap::new(); + let mut base_swap_floating_layout = BTreeMap::new(); + let tiled_panes_count = layout.0.pane_count(); + let floating_panes_count = layout.1.len(); + // we set MaxPanes to the current panes in the layout, because the base layout is not + // intended to be progressive - i.e. to have additional panes added to it + // we still want to keep it around in case we'd like to swap layouts without adding panes + base_swap_tiled_layout.insert(LayoutConstraint::MaxPanes(tiled_panes_count), layout.0); + base_swap_floating_layout + .insert(LayoutConstraint::MaxPanes(floating_panes_count), layout.1); + self.swap_tiled_layouts + .insert(0, (base_swap_tiled_layout, Some("BASE".into()))); + self.swap_floating_layouts + .insert(0, (base_swap_floating_layout, Some("BASE".into()))); + self.current_tiled_layout_position = 0; + self.current_floating_layout_position = 0; + } + pub fn set_is_floating_damaged(&mut self) { + self.is_floating_damaged = true; + } + pub fn set_is_tiled_damaged(&mut self) { + self.is_tiled_damaged = true; + } + pub fn is_floating_damaged(&self) -> bool { + self.is_floating_damaged + } + pub fn is_tiled_damaged(&self) -> bool { + self.is_tiled_damaged + } + pub fn tiled_layout_info(&self) -> (Option, bool) { + // (swap_layout_name, is_swap_layout_dirty) + match self + .swap_tiled_layouts + .iter() + .nth(self.current_tiled_layout_position) + { + Some(current_tiled_layout) => ( + current_tiled_layout.1.clone().or_else(|| { + Some(format!( + "Layout #{}", + self.current_tiled_layout_position + 1 + )) + }), + self.is_tiled_damaged, + ), + None => (None, self.is_tiled_damaged), + } + } + pub fn floating_layout_info(&self) -> (Option, bool) { + // (swap_layout_name, is_swap_layout_dirty) + match self + .swap_floating_layouts + .iter() + .nth(self.current_floating_layout_position) + { + Some(current_floating_layout) => ( + current_floating_layout.1.clone().or_else(|| { + Some(format!( + "Layout #{}", + self.current_floating_layout_position + 1 + )) + }), + self.is_floating_damaged, + ), + None => (None, self.is_floating_damaged), + } + } + pub fn swap_floating_panes( + &mut self, + floating_panes: &FloatingPanes, + search_backwards: bool, + ) -> Option> { + if self.swap_floating_layouts.is_empty() { + return None; + } + let initial_position = self.current_floating_layout_position; + + macro_rules! progress_layout { + () => {{ + if search_backwards { + if self.current_floating_layout_position == 0 { + self.current_floating_layout_position = + self.swap_floating_layouts.len().saturating_sub(1); + } else { + self.current_floating_layout_position -= 1; + } + } else { + self.current_floating_layout_position += 1; + } + }}; + } + + if !self.is_floating_damaged + && self + .swap_floating_layouts + .iter() + .nth(self.current_floating_layout_position) + .is_some() + { + progress_layout!(); + } + self.is_floating_damaged = false; + loop { + match self + .swap_floating_layouts + .iter() + .nth(self.current_floating_layout_position) + { + Some(swap_layout) => { + for (constraint, layout) in swap_layout.0.iter() { + if self.state_fits_floating_panes_constraint(constraint, floating_panes) { + return Some(layout.clone()); + }; + } + progress_layout!(); + }, + None => { + self.current_floating_layout_position = 0; + }, + }; + if self.current_floating_layout_position == initial_position { + break; + } + } + None + } + fn state_fits_tiled_panes_constraint( + &self, + constraint: &LayoutConstraint, + tiled_panes: &TiledPanes, + ) -> bool { + match constraint { + LayoutConstraint::MaxPanes(max_panes) => { + tiled_panes.visible_panes_count() <= *max_panes + }, + LayoutConstraint::MinPanes(min_panes) => { + tiled_panes.visible_panes_count() >= *min_panes + }, + LayoutConstraint::NoConstraint => true, + } + } + fn state_fits_floating_panes_constraint( + &self, + constraint: &LayoutConstraint, + floating_panes: &FloatingPanes, + ) -> bool { + match constraint { + LayoutConstraint::MaxPanes(max_panes) => { + floating_panes.visible_panes_count() <= *max_panes + }, + LayoutConstraint::MinPanes(min_panes) => { + floating_panes.visible_panes_count() >= *min_panes + }, + LayoutConstraint::NoConstraint => true, + } + } + pub fn swap_tiled_panes( + &mut self, + tiled_panes: &TiledPanes, + search_backwards: bool, + ) -> Option { + if self.swap_tiled_layouts.is_empty() { + return None; + } + + macro_rules! progress_layout { + () => {{ + if search_backwards { + if self.current_tiled_layout_position == 0 { + self.current_tiled_layout_position = + self.swap_tiled_layouts.len().saturating_sub(1); + } else { + self.current_tiled_layout_position -= 1; + } + } else { + self.current_tiled_layout_position += 1; + } + }}; + } + + let initial_position = self.current_tiled_layout_position; + if !self.is_tiled_damaged + && self + .swap_tiled_layouts + .iter() + .nth(self.current_tiled_layout_position) + .is_some() + { + progress_layout!(); + } + self.is_tiled_damaged = false; + loop { + match self + .swap_tiled_layouts + .iter() + .nth(self.current_tiled_layout_position) + { + Some(swap_layout) => { + for (constraint, layout) in swap_layout.0.iter() { + if self.state_fits_tiled_panes_constraint(constraint, tiled_panes) { + let display_area = self.display_area.borrow(); + // TODO: reuse the assets from position_panes_in_space here? + let pane_count = tiled_panes.visible_panes_count(); + let display_area = PaneGeom::from(&*display_area); + if layout + .position_panes_in_space(&display_area, Some(pane_count)) + .is_ok() + { + return Some(layout.clone()); + } + }; + } + progress_layout!(); + }, + None => { + self.current_tiled_layout_position = 0; + }, + }; + if self.current_tiled_layout_position == initial_position { + break; + } + } + None + } + pub fn best_effort_tiled_layout( + &mut self, + tiled_panes: &TiledPanes, + ) -> Option { + for swap_layout in self.swap_tiled_layouts.iter() { + for (_constraint, layout) in swap_layout.0.iter() { + let display_area = self.display_area.borrow(); + // TODO: reuse the assets from position_panes_in_space here? + let pane_count = tiled_panes.visible_panes_count(); + let display_area = PaneGeom::from(&*display_area); + if layout + .position_panes_in_space(&display_area, Some(pane_count)) + .is_ok() + { + return Some(layout.clone()); + } + } + } + log::error!("Could not find layout that would fit on screen!"); + None + } +} diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_floating_layout_is_included_in_swap_layouts.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_floating_layout_is_included_in_swap_layouts.snap new file mode 100644 index 00000000..f6e33291 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_floating_layout_is_included_in_swap_layouts.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5472 +expression: snapshot +--- +00 (C): ┌ tab-bar ────────────────────────────────────────┌ status-bar ──────────────────────────────────────────────┐──────────┐ +01 (C): │I am a tab bar │I am a │ │ +02 (C): │ │status bar │ │ +03 (C): │ │ │ │ +04 (C): │ │ │ │ +05 (C): │ ┌ Pane #3 ──────────│ │ │ +06 (C): │ │ │ │ │ +07 (C): │ │ │ │ │ +08 (C): │ │ │ │ │ +09 (C): └─────────────────────────────│ └──────────────────────────────────────────────────────────┘ │ +10 (C): ┌ command1 ───────────────────│ ┌ command2 ────────────────────────────────────────────────┐ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ │ │ +13 (C): │ │ │ │ │ +14 (C): │ Waiting to ru└───────────────────│ Waiting to run: command2 │ │ +15 (C): │ │ │ │ +16 (C): │ to run, to exit │ to run, to exit │ │ +17 (C): │ │ │ │ +18 (C): │ │ │ │ +19 (C): └─────────────────────────────────────────────────└──────────────────────────────────────────────────────────┘──────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_layout_is_included_in_swap_layouts.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_layout_is_included_in_swap_layouts.snap new file mode 100644 index 00000000..5fbf2847 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_layout_is_included_in_swap_layouts.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4762 +expression: snapshot +--- +00 (C): I am a tab bar +01 (C): ┌ command2 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐┌ command1 ────────────────────────────┐ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ Waiting to run: command2 ││ ││ Waiting to run: command1 │ +09 (C): │ ││ ││ │ +10 (C): │ to run, to exit ││ ││ to run, to exit │ +11 (C): │ ││ ││ │ +12 (C): │ ││ ││ │ +13 (C): │ ││ ││ │ +14 (C): │ ││ ││ │ +15 (C): │ ││ ││ │ +16 (C): │ ││ ││ │ +17 (C): └───────────────────────────────────────┘└──────────────────────────────────────┘└──────────────────────────────────────┘ +18 (C): I am a +19 (C): status bar + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_horizontally.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_horizontally.snap new file mode 100644 index 00000000..f4322702 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_horizontally.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3955 +expression: snapshot +--- +00 (C): ┌ Pane #1 ────────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ───────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ───────────────────────────────────────────┐ +03 (C): │ │┌ Pane #5 ───────────────────────────────────────────┐ +04 (C): │ │┌ Pane #6 ───────────────────────────────────────────┐ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └─────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_non_directionally.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_non_directionally.snap new file mode 100644 index 00000000..997af6c2 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_non_directionally.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4062 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +13 (C): ┌ Pane #2 ────────────────────────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────────┐ +14 (C): │ │┌ Pane #5 ───────────────────────────────────────────┐ +15 (C): │ │┌ Pane #6 ───────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): └─────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────┘ +28 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_vertically.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_vertically.snap new file mode 100644 index 00000000..b800c634 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_into_pane_stack_vertically.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4047 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +13 (C): ┌ Pane #2 ──────────────────────────────────────────────────┐┌ Pane #4 ─────────────────────────────────────────────────┐ +14 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +15 (C): │ │┌ Pane #6 ─────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +25 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +26 (C): │ │ +27 (C): │ │ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_horizontally.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_horizontally.snap new file mode 100644 index 00000000..ecee6a70 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_horizontally.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3716 +expression: snapshot +--- +00 (C): ┌ Pane #1 ───────────────────────────────────────────┐┌ Pane #2 ────────────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ────────────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ────────────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #5 ────────────────────────────────────────────────────────┐ +04 (C): │ │┌ Pane #6 ────────────────────────────────────────────────────────┐ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └────────────────────────────────────────────────────┘└─────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_non_directionally.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_non_directionally.snap new file mode 100644 index 00000000..bcd3b4ee --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_non_directionally.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3823 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +13 (C): ┌ Pane #2 ───────────────────────────────────────────┐┌ Pane #4 ────────────────────────────────────────────────────────┐ +14 (C): │ │┌ Pane #5 ────────────────────────────────────────────────────────┐ +15 (C): │ │┌ Pane #6 ────────────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): └────────────────────────────────────────────────────┘└─────────────────────────────────────────────────────────────────┘ +28 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_vertically.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_vertically.snap new file mode 100644 index 00000000..b5dea0aa --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_increase_size_of_main_pane_in_stack_vertically.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3807 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +13 (C): ┌ Pane #2 ──────────────────────────────────────────────────┐┌ Pane #4 ─────────────────────────────────────────────────┐ +14 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +15 (C): │ │┌ Pane #6 ─────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): │ ││ │ +20 (C): │ ││ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): │ ││ │ +27 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +28 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_between_multiple_layouts.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_between_multiple_layouts.snap new file mode 100644 index 00000000..b7f08c8a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_between_multiple_layouts.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3064 +expression: snapshot +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ──────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └──────────────────────────────────────────────────────────┘└───────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_floating_layout_at_runtime.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_floating_layout_at_runtime.snap new file mode 100644 index 00000000..04fa9400 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_floating_layout_at_runtime.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3022 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): ┌ Pane #2 ─────────────────────────────────────────────────┐ ┌ Pane #3 ─────────────────────────────────────────────────┐ +06 (C): │ │ │ │ +07 (C): │ │ │ │ +08 (C): │ │ │ │ +09 (C): │ │ │ │ +10 (C): │ │ │ │ +11 (C): │ │ │ │ +12 (C): │ │ │ │ +13 (C): │ │ │ │ +14 (C): └──────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────┘ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_tiled_layout_at_runtime.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_tiled_layout_at_runtime.snap new file mode 100644 index 00000000..e14c889a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__can_swap_tiled_layout_at_runtime.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 2977 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__cannot_decrease_stack_size_beyond_minimum_height.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__cannot_decrease_stack_size_beyond_minimum_height.snap new file mode 100644 index 00000000..8416d6e0 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__cannot_decrease_stack_size_beyond_minimum_height.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4346 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #6 ─────────────────────────────────────────────────┐ +04 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_main_stacked_pane.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_main_stacked_pane.snap new file mode 100644 index 00000000..1260736f --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_main_stacked_pane.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3536 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_main_stacked_pane_in_mid_stack.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_main_stacked_pane_in_mid_stack.snap new file mode 100644 index 00000000..071b2413 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_main_stacked_pane_in_mid_stack.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3590 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ │└──────────────────────────────────────────────────────────┘ +19 (C): └───────────────────────────────────────────────────────────┘└ Pane #6 ─────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_one_liner_stacked_pane_above_main_pane.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_one_liner_stacked_pane_above_main_pane.snap new file mode 100644 index 00000000..17d9500e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_one_liner_stacked_pane_above_main_pane.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3699 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ │└──────────────────────────────────────────────────────────┘ +18 (C): │ │└ Pane #5 ─────────────────────────────────────────────────┘ +19 (C): └───────────────────────────────────────────────────────────┘└ Pane #6 ─────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_one_liner_stacked_pane_below_main_pane.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_one_liner_stacked_pane_below_main_pane.snap new file mode 100644 index 00000000..fffc3a7d --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_one_liner_stacked_pane_below_main_pane.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3645 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ │└──────────────────────────────────────────────────────────┘ +18 (C): │ │└ Pane #5 ─────────────────────────────────────────────────┘ +19 (C): └───────────────────────────────────────────────────────────┘└ Pane #6 ─────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_pane_near_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_pane_near_stacked_panes.snap new file mode 100644 index 00000000..f9d93989 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_pane_near_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4574 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_stacked_pane_with_previously_focused_other_pane.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_stacked_pane_with_previously_focused_other_pane.snap new file mode 100644 index 00000000..b065d0bc --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_stacked_pane_with_previously_focused_other_pane.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4517 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ │└──────────────────────────────────────────────────────────┘ +09 (C): └───────────────────────────────────────────────────────────┘└ Pane #6 ─────────────────────────────────────────────────┘ +10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__decreasing_size_of_whole_tab_treats_stacked_panes_properly.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__decreasing_size_of_whole_tab_treats_stacked_panes_properly.snap new file mode 100644 index 00000000..913206c0 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__decreasing_size_of_whole_tab_treats_stacked_panes_properly.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4195 +expression: snapshot +--- +00 (C): ┌ Pane #1 ───────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ───────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ───────────────────────────────────────┐ +03 (C): │ │┌ Pane #5 ───────────────────────────────────────┐ +04 (C): │ │┌ Pane #6 ───────────────────────────────────────┐ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └────────────────────────────────────────────────┘└────────────────────────────────────────────────┘ +10 (C): +11 (C): +12 (C): +13 (C): +14 (C): +15 (C): +16 (C): +17 (C): +18 (C): +19 (C): + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__floating_layout_with_plugins_and_commands_swaped_properly.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__floating_layout_with_plugins_and_commands_swaped_properly.snap new file mode 100644 index 00000000..11b9b669 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__floating_layout_with_plugins_and_commands_swaped_properly.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5402 +expression: snapshot +--- +00 (C): ┌ status-bar ──────────────────────────────────────────────┐─────────────────────────────────────────────────┐──────────┐ +01 (C): │I am a │b bar │ │ +02 (C): │status bar │ │ │ +03 (C): │ │ │ │ +04 (C): │ │ │ │ +05 (C): │ │ │ │ +06 (C): │ │ │ │ +07 (C): │ │ │ │ +08 (C): │ │ │ │ +09 (C): └──────────────────────────────────────────────────────────┘─────────────────────────────────────────────────┘ │ +10 (C): ┌ command2 ───────────────────│ ┌ command1 ────────────────────────────────────────────────┐ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ │ │ +13 (C): │ │ │ │ │ +14 (C): │ Waiting to ru└───────────────────│ Waiting to run: command1 │ │ +15 (C): │ │ │ │ +16 (C): │ to run, to exit │ to run, to exit │ │ +17 (C): │ │ │ │ +18 (C): │ │ │ │ +19 (C): └─────────────────────────────────────────────────└──────────────────────────────────────────────────────────┘──────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_next_pane_expands_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_next_pane_expands_stacked_panes.snap new file mode 100644 index 00000000..fec3d753 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_next_pane_expands_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4628 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ │└──────────────────────────────────────────────────────────┘ +07 (C): │ │└ Pane #4 ─────────────────────────────────────────────────┘ +08 (C): │ │└ Pane #5 ─────────────────────────────────────────────────┘ +09 (C): └───────────────────────────────────────────────────────────┘└ Pane #6 ─────────────────────────────────────────────────┘ +10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_stacked_pane_over_flexible_pane_with_the_mouse.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_stacked_pane_over_flexible_pane_with_the_mouse.snap new file mode 100644 index 00000000..77530c7c --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_stacked_pane_over_flexible_pane_with_the_mouse.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4399 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ │└──────────────────────────────────────────────────────────┘ +08 (C): │ │└ Pane #5 ─────────────────────────────────────────────────┘ +09 (C): └───────────────────────────────────────────────────────────┘└ Pane #6 ─────────────────────────────────────────────────┘ +10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_stacked_pane_under_flexible_pane_with_the_mouse.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_stacked_pane_under_flexible_pane_with_the_mouse.snap new file mode 100644 index 00000000..41100f1e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__focus_stacked_pane_under_flexible_pane_with_the_mouse.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4454 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #6 ─────────────────────────────────────────────────┐ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_into_main_pane_in_stack_horizontally_does_not_break_stack.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_into_main_pane_in_stack_horizontally_does_not_break_stack.snap new file mode 100644 index 00000000..417fe7cd --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_into_main_pane_in_stack_horizontally_does_not_break_stack.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4178 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +13 (C): ┌ Pane #2 ────────────────────────────────────────────────────────┐┌ Pane #4 ───────────────────────────────────────────┐ +14 (C): │ │┌ Pane #6 ───────────────────────────────────────────┐ +15 (C): │ │┌ Pane #7 ───────────────────────────────────────────┐ +16 (C): │ │┌ Pane #8 ───────────────────────────────────────────┐ +17 (C): │ │┌ Pane #9 ───────────────────────────────────────────┐ +18 (C): │ │┌ Pane #10 ──────────────────────────────────────────┐ +19 (C): └─────────────────────────────────────────────────────────────────┘┌ Pane #11 ──────────────────────────────────────────┐ +20 (C): ┌ Pane #3 ────────────────────────────────────────────────────────┐│ │ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): └─────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────┘ +27 (C): ┌ Pane #5 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_of_main_pane_in_stack_horizontally_does_not_break_stack.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_of_main_pane_in_stack_horizontally_does_not_break_stack.snap new file mode 100644 index 00000000..530bc567 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_of_main_pane_in_stack_horizontally_does_not_break_stack.snap @@ -0,0 +1,46 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3942 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +13 (C): ┌ Pane #2 ───────────────────────────────────────────┐┌ Pane #4 ────────────────────────────────────────────────────────┐ +14 (C): │ │┌ Pane #6 ────────────────────────────────────────────────────────┐ +15 (C): │ │┌ Pane #7 ────────────────────────────────────────────────────────┐ +16 (C): │ │┌ Pane #8 ────────────────────────────────────────────────────────┐ +17 (C): │ │┌ Pane #9 ────────────────────────────────────────────────────────┐ +18 (C): │ │┌ Pane #10 ───────────────────────────────────────────────────────┐ +19 (C): └────────────────────────────────────────────────────┘┌ Pane #11 ───────────────────────────────────────────────────────┐ +20 (C): ┌ Pane #3 ───────────────────────────────────────────┐┌ Pane #12 ───────────────────────────────────────────────────────┐ +21 (C): │ ││ │ +22 (C): │ ││ │ +23 (C): │ ││ │ +24 (C): │ ││ │ +25 (C): │ ││ │ +26 (C): └────────────────────────────────────────────────────┘└─────────────────────────────────────────────────────────────────┘ +27 (C): ┌ Pane #5 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +28 (C): │ │ +29 (C): │ │ +30 (C): │ │ +31 (C): │ │ +32 (C): │ │ +33 (C): │ │ +34 (C): │ │ +35 (C): │ │ +36 (C): │ │ +37 (C): │ │ +38 (C): │ │ +39 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_of_whole_tab_treats_stacked_panes_properly.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_of_whole_tab_treats_stacked_panes_properly.snap new file mode 100644 index 00000000..df202642 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__increasing_size_of_whole_tab_treats_stacked_panes_properly.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4253 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +04 (C): │ │┌ Pane #6 ─────────────────────────────────────────────────┐ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__layout_with_plugins_and_commands_swaped_properly.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__layout_with_plugins_and_commands_swaped_properly.snap new file mode 100644 index 00000000..6be12af6 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__layout_with_plugins_and_commands_swaped_properly.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4689 +expression: snapshot +--- +00 (C): I am a +01 (C): status bar +02 (C): ┌ command2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ │ +04 (C): │ Waiting to run: command2 │ +05 (C): │ │ +06 (C): │ to run, to exit │ +07 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +08 (C): ┌ command1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +09 (C): │ │ +10 (C): │ Waiting to run: command1 │ +11 (C): │ │ +12 (C): │ to run, to exit │ +13 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +14 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +19 (C): I am a tab bar + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_down_into_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_down_into_stacked_panes.snap new file mode 100644 index 00000000..9fee9b1e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_down_into_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3492 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #6 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +07 (C): ┌ Pane #2 ──────────────────────────────────────────────────┐┌ Pane #4 ─────────────────────────────────────────────────┐ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ │└──────────────────────────────────────────────────────────┘ +13 (C): └───────────────────────────────────────────────────────────┘└ Pane #5 ─────────────────────────────────────────────────┘ +14 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_down_with_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_down_with_stacked_panes.snap new file mode 100644 index 00000000..1dfa52c7 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_down_with_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3269 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_left_into_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_left_into_stacked_panes.snap new file mode 100644 index 00000000..6a0d0d7e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_left_into_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3378 +expression: snapshot +--- +00 (C): ┌ Pane #3 ──────────────────────────────────────────────────┐┌ Pane #7 ─────────────────────────────────────────────────┐ +01 (C): ┌ Pane #2 ──────────────────────────────────────────────────┐│ │ +02 (C): ┌ Pane #4 ──────────────────────────────────────────────────┐│ │ +03 (C): ┌ Pane #5 ──────────────────────────────────────────────────┐│ │ +04 (C): ┌ Pane #6 ──────────────────────────────────────────────────┐│ │ +05 (C): ┌ Pane #8 ──────────────────────────────────────────────────┐│ │ +06 (C): ┌ Pane #9 ──────────────────────────────────────────────────┐│ │ +07 (C): ┌ Pane #10 ─────────────────────────────────────────────────┐│ │ +08 (C): ┌ Pane #11 ─────────────────────────────────────────────────┐│ │ +09 (C): ┌ Pane #12 ─────────────────────────────────────────────────┐└──────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #13 ─────────────────────────────────────────────────┐┌ Pane #15 ────────────────────────────────────────────────┐ +11 (C): ┌ Pane #14 ─────────────────────────────────────────────────┐│ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_right_into_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_right_into_stacked_panes.snap new file mode 100644 index 00000000..3acd26d8 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_right_into_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3324 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +03 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +04 (C): │ │┌ Pane #6 ─────────────────────────────────────────────────┐ +05 (C): │ │┌ Pane #7 ─────────────────────────────────────────────────┐ +06 (C): │ │┌ Pane #8 ─────────────────────────────────────────────────┐ +07 (C): │ │┌ Pane #9 ─────────────────────────────────────────────────┐ +08 (C): │ │┌ Pane #10 ────────────────────────────────────────────────┐ +09 (C): └───────────────────────────────────────────────────────────┘┌ Pane #11 ────────────────────────────────────────────────┐ +10 (C): ┌ Pane #14 ─────────────────────────────────────────────────┐┌ Pane #12 ────────────────────────────────────────────────┐ +11 (C): │ │┌ Pane #13 ────────────────────────────────────────────────┐ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_up_into_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_up_into_stacked_panes.snap new file mode 100644 index 00000000..e8e3dd7c --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_up_into_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3434 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +07 (C): ┌ Pane #2 ──────────────────────────────────────────────────┐┌ Pane #4 ─────────────────────────────────────────────────┐ +08 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +14 (C): ┌ Pane #3 ──────────────────────────────────────────────────┐┌ Pane #6 ─────────────────────────────────────────────────┐ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_up_with_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_up_with_stacked_panes.snap new file mode 100644 index 00000000..c5d5de95 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_up_with_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3222 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ │└──────────────────────────────────────────────────────────┘ +19 (C): └───────────────────────────────────────────────────────────┘└ Pane #4 ─────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_with_stacked_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_with_stacked_panes.snap new file mode 100644 index 00000000..254e671a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__move_focus_with_stacked_panes.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3185 +expression: snapshot +--- +00 (C): ┌ Pane #4 ─────────────────────────────────────────────────┐┌ Pane #1 ──────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #2 ──────────────────────────────────────────────────┐ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ │└───────────────────────────────────────────────────────────┘ +19 (C): └──────────────────────────────────────────────────────────┘┌ Pane #3 ──────────────────────────────────────────────────┐ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout-2.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout-2.snap new file mode 100644 index 00000000..cdef4812 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout-2.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5799 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ ┌ Pane #2 ────────────────────────────────────────────┐ ┌ Pane #3 ────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ │ │ +07 (C): │ │ │ │ │ │ +08 (C): │ │ │ │ │ │ +09 (C): │ │ │ │ │ │ +10 (C): │ │ │ │ │ │ +11 (C): │ │ │ │ │ │ +12 (C): │ │ │ │ │ │ +13 (C): │ │ │ │ │ │ +14 (C): │ └─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout-3.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout-3.snap new file mode 100644 index 00000000..b5c19c92 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout-3.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5799 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ ┌ Pane #3 ────────────────────────────────────────────┐ ┌ Pane #2 ────────────────────────────────────────────┐ │ +02 (C): │ │ │ │ │ │ +03 (C): │ │ │ │ │ │ +04 (C): │ │ │ │ │ │ +05 (C): │ │ │ │ │ │ +06 (C): │ │ │ │ │ │ +07 (C): │ │ │ │ │ │ +08 (C): │ │ │ │ │ │ +09 (C): │ │ │ │ │ │ +10 (C): │ └─────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────┘ │ +11 (C): │ ┌ Pane #4 ────────────────────────────────────────────┐ │ +12 (C): │ │ │ │ +13 (C): │ │ │ │ +14 (C): │ │ │ │ +15 (C): │ │ │ │ +16 (C): │ │ │ │ +17 (C): │ │ │ │ +18 (C): │ │ │ │ +19 (C): └─────────────────────────────└─────────────────────────────────────────────────────┘───────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout.snap new file mode 100644 index 00000000..d6c84185 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_floating_pane_in_auto_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5799 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ ┌ Pane #2 ─────────────────────────────────────────────────┐ +11 (C): │ │ │ +12 (C): │ │ │ +13 (C): │ │ │ +14 (C): │ │ │ +15 (C): │ │ │ +16 (C): │ │ │ +17 (C): │ │ │ +18 (C): │ │ │ +19 (C): └────────────────────────────────────────────────────────────└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-2.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-2.snap new file mode 100644 index 00000000..241910dc --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-2.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5099 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ │└──────────────────────────────────────────────────────────┘ +10 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-3.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-3.snap new file mode 100644 index 00000000..764303eb --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-3.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5099 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ │└──────────────────────────────────────────────────────────┘ +07 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ │└──────────────────────────────────────────────────────────┘ +14 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-4.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-4.snap new file mode 100644 index 00000000..2e426650 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-4.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5099 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ │└──────────────────────────────────────────────────────────┘ +05 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ │└──────────────────────────────────────────────────────────┘ +10 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ │└──────────────────────────────────────────────────────────┘ +15 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-5.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-5.snap new file mode 100644 index 00000000..469f2b26 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-5.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5099 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ │└──────────────────────────────────────────────────────────┘ +05 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #6 ──────────────────────────────────────────────────┐┌ Pane #4 ─────────────────────────────────────────────────┐ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ │└──────────────────────────────────────────────────────────┘ +15 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-6.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-6.snap new file mode 100644 index 00000000..caf0b98a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-6.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5099 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ │└──────────────────────────────────────────────────────────┘ +05 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +06 (C): └───────────────────────────────────────────────────────────┘│ │ +07 (C): ┌ Pane #6 ──────────────────────────────────────────────────┐│ │ +08 (C): │ ││ │ +09 (C): │ │└──────────────────────────────────────────────────────────┘ +10 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): └───────────────────────────────────────────────────────────┘│ │ +14 (C): ┌ Pane #7 ──────────────────────────────────────────────────┐└──────────────────────────────────────────────────────────┘ +15 (C): │ │┌ Pane #5 ─────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-7.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-7.snap new file mode 100644 index 00000000..8956fb0f --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout-7.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5099 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +05 (C): ┌ Pane #6 ──────────────────────────────────────────────────┐┌ Pane #3 ─────────────────────────────────────────────────┐ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #7 ──────────────────────────────────────────────────┐┌ Pane #4 ─────────────────────────────────────────────────┐ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ +15 (C): ┌ Pane #8 ──────────────────────────────────────────────────┐┌ Pane #5 ─────────────────────────────────────────────────┐ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout.snap new file mode 100644 index 00000000..71021120 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__new_pane_in_auto_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5097 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_tab_with_floating_panes.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_tab_with_floating_panes.snap index e90a13dc..da14ad29 100644 --- a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_tab_with_floating_panes.snap +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_tab_with_floating_panes.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/tab/./unit/tab_integration_tests.rs -assertion_line: 1371 +assertion_line: 1544 expression: snapshot --- 00 (C): ┌ Pane #1 ────────────────────┌ ┌ ┌ Pane #4 ─────────────────────────────────── SCROLL: 0/1 ┐─────┐ diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_whole_tab_while_floting_pane_is_suppressed.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_whole_tab_while_floting_pane_is_suppressed.snap index d4e106aa..2a886db7 100644 --- a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_whole_tab_while_floting_pane_is_suppressed.snap +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_whole_tab_while_floting_pane_is_suppressed.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/tab/./unit/tab_integration_tests.rs -assertion_line: 1801 +assertion_line: 2220 expression: snapshot --- 00 (C): ┌ Pane #1 ────────────────────┌ EDITING SCROLLBACK ──────────────────────────────────────┐─────────┐ diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically.snap index e6f1786c..cfa37b74 100644 --- a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically.snap +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/tab/./unit/tab_integration_tests.rs -assertion_line: 1421 +assertion_line: 1594 expression: snapshot --- 00 (C): ┌ Pane #1 ────────────────────┌ ┌ ┌ Pane #4 ── 0 ┐ diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_back.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_back.snap index a005424f..a7c0f582 100644 --- a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_back.snap +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_back.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/tab/./unit/tab_integration_tests.rs -assertion_line: 1475 +assertion_line: 1648 expression: snapshot --- 00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__stacked_panes_can_become_fullscreen.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__stacked_panes_can_become_fullscreen.snap new file mode 100644 index 00000000..746d110e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__stacked_panes_can_become_fullscreen.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4667 +expression: snapshot +--- +00 (C): ┌ Pane #5 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_including_command_panes_absent_from_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_including_command_panes_absent_from_existing_layout.snap new file mode 100644 index 00000000..6bdd963e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_including_command_panes_absent_from_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5541 +expression: snapshot +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────┐────────────────────────────────────────────────────────────┐ +01 (C): │ │ │ +02 (C): │ │ │ +03 (C): │ │ │ +04 (C): │ │ │ +05 (C): │ ┌ status-bar ──────────────────────────────────────────────┐ │ +06 (C): │ │I am a │ │ +07 (C): │ │status bar │─┐ │ +08 (C): │ │ │ │ │ +09 (C): └─────────────────────────────│ │───┐ │ +10 (C): │ ┌ Pane #3 ──────────│ │ │ │ +11 (C): │ │ │ │ │ │ +12 (C): │ │ │ │ │ │ +13 (C): │ │ │ │ │ │ +14 (C): │ │ └──────────────────────────────────────────────────────────┘ │ │ +15 (C): │ │ │ │ │ │ +16 (C): │ │ └─│ │ │ +17 (C): │ │ │ │ │ +18 (C): │ │ └──────────────────────────────────────────────────────────┘ │ +19 (C): └─────────└──────────────────────────────────────────────────────────┘──────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_including_plugin_panes_absent_from_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_including_plugin_panes_absent_from_existing_layout.snap new file mode 100644 index 00000000..2f06d931 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_including_plugin_panes_absent_from_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5672 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ ┌ Pane #2 ─────────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ +07 (C): │ │ ┌ Pane #3 ─────────────────────────────────────────────────┐ │ +08 (C): │ │ │ │ │ +09 (C): │ │ │ ┌ Pane #4 ─────────────────────────────────────────────────┐ │ +10 (C): │ │ │ │ │ │ +11 (C): │ │ │ │ │ │ +12 (C): │ │ │ │ │ │ +13 (C): │ │ │ │ │ │ +14 (C): │ └─│ │ │ │ +15 (C): │ │ │ │ │ +16 (C): │ └─│ │ │ +17 (C): │ │ │ │ +18 (C): │ └──────────────────────────────────────────────────────────┘ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_command_panes_present_in_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_command_panes_present_in_existing_layout.snap new file mode 100644 index 00000000..29553797 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_command_panes_present_in_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5614 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ ┌ command2 ────────────────────────────┐ │ +03 (C): │ │ │ │ +04 (C): │ │ ┌ tab-bar ─────────────────────────────┐ │ +05 (C): │ │ │I am a tab bar ┌ status-bar ──────────────────────────────────────────────┐ │ +06 (C): │ │ │ │I am a │ │ +07 (C): │ └─│ │status bar │─┐ │ +08 (C): │ │ │ │ │ │ +09 (C): │ └─────────────────────────│ │───┐ │ +10 (C): │ │ │ │ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ │ │ +13 (C): │ │ │ │ │ +14 (C): │ └──────────────────────────────────────────────────────────┘ │ │ +15 (C): │ │ │ to run, to exit │ │ +16 (C): │ └─│ │ │ +17 (C): │ │ │ │ +18 (C): │ └──────────────────────────────────────────────────────────┘ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_plugin_panes_present_in_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_plugin_panes_present_in_existing_layout.snap new file mode 100644 index 00000000..e6baccbe --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_plugin_panes_present_in_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5734 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ ┌ Pane #2 ─────────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ +07 (C): │ │ ┌ tab-bar ─────────────────────────────────────────────────┐ │ +08 (C): │ │ │I am a tab bar │ │ +09 (C): │ │ │ ┌ status-bar ──────────────────────────────────────────────┐ │ +10 (C): │ │ │ │I am a │ │ +11 (C): │ │ │ │status bar │ │ +12 (C): │ │ │ │ │ │ +13 (C): │ │ │ │ │ │ +14 (C): │ └─│ │ │ │ +15 (C): │ │ │ │ │ +16 (C): │ └─│ │ │ +17 (C): │ │ │ │ +18 (C): │ └──────────────────────────────────────────────────────────┘ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_including_command_panes_absent_from_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_including_command_panes_absent_from_existing_layout.snap new file mode 100644 index 00000000..5b7bd24e --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_including_command_panes_absent_from_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4831 +expression: snapshot +--- +00 (C): I am a +01 (C): status bar +02 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +08 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +14 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +19 (C): I am a tab bar + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_including_plugin_panes_absent_from_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_including_plugin_panes_absent_from_existing_layout.snap new file mode 100644 index 00000000..df1a8e09 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_including_plugin_panes_absent_from_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4966 +expression: snapshot +--- +00 (C): +01 (C): +02 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): │ │ +10 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +11 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_command_panes_present_in_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_command_panes_present_in_existing_layout.snap new file mode 100644 index 00000000..dd0cbbd7 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_command_panes_present_in_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 4904 +expression: snapshot +--- +00 (C): I am a +01 (C): status bar +02 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +08 (C): ┌ command1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +09 (C): │ │ +10 (C): │ Waiting to run: command1 │ +11 (C): │ │ +12 (C): │ to run, to exit │ +13 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +14 (C): ┌ command2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +15 (C): │ Waiting to run: command2 │ +16 (C): │ │ +17 (C): │ to run, to exit │ +18 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +19 (C): I am a tab bar + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_plugin_panes_present_in_existing_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_plugin_panes_present_in_existing_layout.snap new file mode 100644 index 00000000..258ea58b --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_plugin_panes_present_in_existing_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5035 +expression: snapshot +--- +00 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +02 (C): ┌ command1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +03 (C): │ │ +04 (C): │ Waiting to run: command1 │ +05 (C): │ │ +06 (C): │ to run, to exit │ +07 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +08 (C): ┌ command2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +09 (C): │ │ +10 (C): │ Waiting to run: command2 │ +11 (C): │ │ +12 (C): │ to run, to exit │ +13 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +14 (C): ┌ status-bar ───────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +15 (C): │I am a │ +16 (C): │status bar │ +17 (C): │ │ +18 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +19 (C): ┌ tab-bar ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children.snap new file mode 100644 index 00000000..4a51d490 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3176 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ │┌ Pane #3 ─────────────────────────────────────────────────┐ +02 (C): │ │┌ Pane #4 ─────────────────────────────────────────────────┐ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap new file mode 100644 index 00000000..7174f10a --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3204 +expression: snapshot +--- +00 (C): ├─ Pane #2 ───────────────────────────────────────────────── +01 (C): ├─ Pane #3 ───────────────────────────────────────────────── +02 (C): ├─ Pane #4 ───────────────────────────────────────────────── +03 (C): │ +04 (C): │ +05 (C): │ +06 (C): │ +07 (C): │ +08 (C): │ +09 (C): │ +10 (C): │ +11 (C): │ +12 (C): │ +13 (C): │ +14 (C): │ +15 (C): │ +16 (C): │ +17 (C): │ +18 (C): │ +19 (C): │ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swapping_layouts_after_resize_snaps_to_current_layout.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swapping_layouts_after_resize_snaps_to_current_layout.snap new file mode 100644 index 00000000..4322a975 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swapping_layouts_after_resize_snaps_to_current_layout.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 3132 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_closing_a_floating_pane_in_auto_layout_the_focus_goes_to_last_focused_pane.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_closing_a_floating_pane_in_auto_layout_the_focus_goes_to_last_focused_pane.snap new file mode 100644 index 00000000..331e678b --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_closing_a_floating_pane_in_auto_layout_the_focus_goes_to_last_focused_pane.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 6028 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ ┌ Pane #3 ─────────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ +07 (C): │ │ │─┐ │ +08 (C): │ │ │ │ │ +09 (C): │ │ │ │ │ +10 (C): │ │ │ │ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ │ │ +13 (C): │ │ │ │ │ +14 (C): │ └──────────────────────────────────────────────────────────┘ │ │ +15 (C): │ │ │ │ +16 (C): │ └──────────────────────────────────────────────────────────┘ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane.snap new file mode 100644 index 00000000..8f548b1d --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5333 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +10 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +11 (C): │ │ +12 (C): │ │ +13 (C): │ │ +14 (C): │ │ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_resizing_whole_tab_with_auto_layout_and_floating_panes_the_layout_is_maintained.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_resizing_whole_tab_with_auto_layout_and_floating_panes_the_layout_is_maintained.snap new file mode 100644 index 00000000..26bcb978 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_resizing_whole_tab_with_auto_layout_and_floating_panes_the_layout_is_maintained.snap @@ -0,0 +1,36 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 6079 +expression: snapshot +--- +00 (C): ┌ Pane #1 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ ┌ Pane #2 ────────────────────────────────────────────────────────────────┐ │ +09 (C): │ │ │ │ +10 (C): │ │ ┌ Pane #3 ────────────────────────────────────────────────────────────────┐ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ ┌ Pane #4 ────────────────────────────────────────────────────────────────┐ │ +13 (C): │ │ │ │ │ │ +14 (C): │ │ │ │ │ │ +15 (C): │ │ │ │ │ │ +16 (C): │ │ │ │ │ │ +17 (C): │ │ │ │ │ │ +18 (C): │ │ │ │ │ │ +19 (C): │ │ │ │ │ │ +20 (C): │ │ │ │ │ │ +21 (C): │ │ │ │ │ │ +22 (C): │ └─│ │ │ │ +23 (C): │ │ │ │ │ +24 (C): │ └─│ │ │ +25 (C): │ │ │ │ +26 (C): │ └─────────────────────────────────────────────────────────────────────────┘ │ +27 (C): │ │ +28 (C): │ │ +29 (C): └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged.snap new file mode 100644 index 00000000..985cb317 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5859 +expression: snapshot +--- +00 (C): ┌ Pane #2 ─────────────────────────────────────────────────┐────────────────────────────────────────────────────────────┐ +01 (C): │ │ │ +02 (C): │ │ │ +03 (C): │ │ │ +04 (C): │ │ │ +05 (C): │ ┌ Pane #3 ─────────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ +07 (C): │ │ ┌ Pane #4 ─────────────────────────────────────────────────┐ │ +08 (C): │ │ │ │ │ +09 (C): └─────────────────────────────│ │ │ │ +10 (C): │ │ │ │ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ │ │ +13 (C): │ │ │ │ │ +14 (C): │ └─│ │ │ +15 (C): │ │ │ │ +16 (C): │ └──────────────────────────────────────────────────────────┘ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_an_undamaged_state_pane_focuses_on_focused_node.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_an_undamaged_state_pane_focuses_on_focused_node.snap new file mode 100644 index 00000000..c42856cd --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_an_undamaged_state_pane_focuses_on_focused_node.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5915 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ ┌ Pane #4 ─────────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ +07 (C): │ │ │─┐ │ +08 (C): │ │ │ │ │ +09 (C): │ │ │ │─┐ │ +10 (C): │ │ │ │ │ │ +11 (C): │ │ │ │ │ │ +12 (C): │ │ │ │ │ │ +13 (C): │ │ │ │ │ │ +14 (C): │ └──────────────────────────────────────────────────────────┘ │ │ │ +15 (C): │ │ │ │ │ +16 (C): │ └──────────────────────────────────────────────────────────┘ │ │ +17 (C): │ │ │ │ +18 (C): │ └──────────────────────────────────────────────────────────┘ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node.snap new file mode 100644 index 00000000..36c93e27 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_floating_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5970 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ ┌ Pane #4 ─────────────────────────────────────────────────┐ │ +06 (C): │ │ │ │ +07 (C): │ │ │─┐ │ +08 (C): │ │ │ │ │ +09 (C): │ │ ┌ Pane #2 ─────────────────────────────────────────────────┐ │ +10 (C): │ │ │ │ │ +11 (C): │ │ │ │ │ +12 (C): │ │ │ │ │ +13 (C): │ │ │ │ │ +14 (C): │ └───│ │ │ +15 (C): │ │ │ │ │ +16 (C): │ └─│ │ │ +17 (C): │ │ │ │ +18 (C): │ └──────────────────────────────────────────────────────────┘ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged.snap new file mode 100644 index 00000000..7124c336 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5160 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +07 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +08 (C): │ │ +09 (C): │ │ +10 (C): │ │ +11 (C): │ │ +12 (C): │ │ +13 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +14 (C): ┌ Pane #3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +15 (C): │ │ +16 (C): │ │ +17 (C): │ │ +18 (C): │ │ +19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_an_undamaged_state_pane_focuses_on_focused_node.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_an_undamaged_state_pane_focuses_on_focused_node.snap new file mode 100644 index 00000000..ba1a4e3f --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_an_undamaged_state_pane_focuses_on_focused_node.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5216 +expression: snapshot +--- +00 (C): ┌ Pane #2 ──────────────────────────────┐┌ Pane #1 ─────────────────────────────┐┌ Pane #3 ─────────────────────────────┐ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ ││ ││ │ +10 (C): │ ││ ││ │ +11 (C): │ ││ ││ │ +12 (C): │ ││ ││ │ +13 (C): │ ││ ││ │ +14 (C): │ ││ ││ │ +15 (C): │ ││ ││ │ +16 (C): │ ││ ││ │ +17 (C): │ ││ ││ │ +18 (C): │ ││ ││ │ +19 (C): └───────────────────────────────────────┘└──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node.snap new file mode 100644 index 00000000..d5f33377 --- /dev/null +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__when_swapping_tiled_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/tab/./unit/tab_integration_tests.rs +assertion_line: 5274 +expression: snapshot +--- +00 (C): ┌ Pane #1 ──────────────────────────────┐┌ Pane #3 ─────────────────────────────┐┌ Pane #2 ─────────────────────────────┐ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ +03 (C): │ ││ ││ │ +04 (C): │ ││ ││ │ +05 (C): │ ││ ││ │ +06 (C): │ ││ ││ │ +07 (C): │ ││ ││ │ +08 (C): │ ││ ││ │ +09 (C): │ ││ ││ │ +10 (C): │ ││ ││ │ +11 (C): │ ││ ││ │ +12 (C): │ ││ ││ │ +13 (C): │ ││ ││ │ +14 (C): │ ││ ││ │ +15 (C): │ ││ ││ │ +16 (C): │ ││ ││ │ +17 (C): │ ││ ││ │ +18 (C): │ ││ ││ │ +19 (C): └───────────────────────────────────────┘└──────────────────────────────────────┘└──────────────────────────────────────┘ + diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 63bfa3f3..3defa1e6 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -6,6 +6,7 @@ use crate::Mutex; use crate::{ os_input_output::{AsyncReader, Pid, ServerOsApi}, panes::PaneId, + plugins::PluginInstruction, thread_bus::ThreadSenders, ClientId, }; @@ -16,7 +17,11 @@ use zellij_utils::data::Resize; use zellij_utils::data::ResizeStrategy; use zellij_utils::envs::set_session_name; use zellij_utils::errors::{prelude::*, ErrorContext}; -use zellij_utils::input::layout::{Layout, PaneLayout}; +use zellij_utils::input::layout::{ + FloatingPaneLayout, Layout, RunPluginLocation, SwapFloatingLayout, SwapTiledLayout, + TiledPaneLayout, +}; +use zellij_utils::input::plugins::PluginTag; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::position::Position; @@ -197,6 +202,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { let mode_info = default_mode; let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -220,15 +226,17 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), ); tab.apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], vec![(1, None)], vec![], @@ -239,6 +247,88 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { tab } +fn create_new_tab_with_swap_layouts( + size: Size, + default_mode: ModeInfo, + swap_layouts: (Vec, Vec), + base_layout_and_ids: Option<( + TiledPaneLayout, + Vec, + Vec<(u32, Option)>, + Vec<(u32, Option)>, + HashMap>, + )>, + draw_pane_frames: bool, +) -> Tab { + set_session_name("test".into()); + let index = 0; + let position = 0; + let name = String::new(); + let os_api = Box::new(FakeInputOutput::default()); + let mut senders = ThreadSenders::default().silently_fail_on_send(); + let (mock_plugin_sender, _mock_plugin_receiver): ChannelWithContext = + channels::unbounded(); + senders.replace_to_plugin(SenderWithContext::new(mock_plugin_sender)); + let max_panes = None; + let mode_info = default_mode; + let style = Style::default(); + let auto_layout = true; + let client_id = 1; + let session_is_mirrored = true; + let mut connected_clients = HashSet::new(); + connected_clients.insert(client_id); + let connected_clients = Rc::new(RefCell::new(connected_clients)); + let character_cell_info = Rc::new(RefCell::new(None)); + let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); + let copy_options = CopyOptions::default(); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let mut tab = Tab::new( + index, + position, + name, + size, + character_cell_info, + sixel_image_store, + os_api, + senders, + max_panes, + style, + mode_info, + draw_pane_frames, + auto_layout, + connected_clients, + session_is_mirrored, + client_id, + copy_options, + terminal_emulator_colors, + terminal_emulator_color_codes, + swap_layouts, + ); + let ( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + ) = base_layout_and_ids.unwrap_or_default(); + let new_terminal_ids = if new_terminal_ids.is_empty() { + vec![(1, None)] + } else { + new_terminal_ids + }; + tab.apply_layout( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + client_id, + ) + .unwrap(); + tab +} + fn create_new_tab_with_os_api( size: Size, default_mode: ModeInfo, @@ -254,6 +344,7 @@ fn create_new_tab_with_os_api( let mode_info = default_mode; let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -277,15 +368,17 @@ fn create_new_tab_with_os_api( style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); tab.apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], vec![(1, None)], vec![], @@ -307,6 +400,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) let mode_info = default_mode; let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -317,7 +411,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); - let layout = Layout::from_str(layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_str(layout, "layout_file_name".into(), None, None).unwrap(); let (tab_layout, floating_panes_layout) = layout.new_tab(); let mut tab = Tab::new( index, @@ -332,12 +426,14 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); let pane_ids = tab_layout .extract_run_instructions() @@ -378,6 +474,7 @@ fn create_new_tab_with_mock_pty_writer( let mode_info = default_mode; let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -401,15 +498,17 @@ fn create_new_tab_with_mock_pty_writer( style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); tab.apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], vec![(1, None)], vec![], @@ -436,6 +535,7 @@ fn create_new_tab_with_sixel_support( let mode_info = ModeInfo::default(); let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -461,15 +561,17 @@ fn create_new_tab_with_sixel_support( style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); tab.apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], vec![(1, None)], vec![], @@ -609,7 +711,7 @@ fn new_floating_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -637,10 +739,10 @@ fn floating_panes_persist_across_toggles() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); // here we send bytes to the pane when it's not visible to make sure they're still handled and // we see them once we toggle the panes back tab.handle_pty_bytes( @@ -648,7 +750,7 @@ fn floating_panes_persist_across_toggles() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -669,7 +771,7 @@ fn toggle_floating_panes_off() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -677,7 +779,7 @@ fn toggle_floating_panes_off() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -698,7 +800,7 @@ fn toggle_floating_panes_on() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -706,8 +808,8 @@ fn toggle_floating_panes_on() { Vec::from("\n\n\n I am scratch terminal".as_bytes()), ) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -732,7 +834,7 @@ fn five_new_floating_panes() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -776,7 +878,7 @@ fn increase_floating_pane_size() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -806,7 +908,7 @@ fn decrease_floating_pane_size() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -836,7 +938,7 @@ fn resize_floating_pane_left() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -869,7 +971,7 @@ fn resize_floating_pane_right() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -902,7 +1004,7 @@ fn resize_floating_pane_up() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -935,7 +1037,7 @@ fn resize_floating_pane_down() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -972,7 +1074,7 @@ fn move_floating_pane_focus_left() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1027,7 +1129,7 @@ fn move_floating_pane_focus_right() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1083,7 +1185,7 @@ fn move_floating_pane_focus_up() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1138,7 +1240,7 @@ fn move_floating_pane_focus_down() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1194,7 +1296,7 @@ fn move_floating_pane_focus_with_mouse() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1252,7 +1354,7 @@ fn move_pane_focus_with_mouse_to_non_floating_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1310,7 +1412,7 @@ fn drag_pane_with_mouse() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1368,7 +1470,7 @@ fn mark_text_inside_floating_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1434,7 +1536,7 @@ fn resize_tab_with_floating_panes() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1488,7 +1590,7 @@ fn shrink_whole_tab_with_floating_panes_horizontally_and_vertically() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1538,7 +1640,7 @@ fn shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_b let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -1589,7 +1691,7 @@ fn embed_floating_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -1647,7 +1749,7 @@ fn embed_floating_pane_without_pane_frames() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.set_pane_frames(false); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.handle_pty_bytes( @@ -1865,7 +1967,7 @@ fn move_floating_pane_with_sixel_image() { }))); let mut output = Output::new(sixel_image_store.clone(), character_cell_size); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); let fixture = read_fixture("sixel-image-500px.six"); @@ -1903,7 +2005,7 @@ fn floating_pane_above_sixel_image() { }))); let mut output = Output::new(sixel_image_store.clone(), character_cell_size); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); let fixture = read_fixture("sixel-image-500px.six"); @@ -1960,7 +2062,7 @@ fn suppress_floating_pane() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); @@ -1991,7 +2093,7 @@ fn close_suppressing_tiled_pane() { .unwrap(); tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes())) .unwrap(); - tab.close_pane(new_pane_id, false); + tab.close_pane(new_pane_id, false, None); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -2014,7 +2116,7 @@ fn close_suppressing_floating_pane() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); @@ -2022,7 +2124,7 @@ fn close_suppressing_floating_pane() { .unwrap(); tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes())) .unwrap(); - tab.close_pane(editor_pane_id, false); + tab.close_pane(editor_pane_id, false, None); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -2049,7 +2151,7 @@ fn suppress_tiled_pane_float_it_and_close() { tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes())) .unwrap(); tab.toggle_pane_embed_or_floating(client_id).unwrap(); - tab.close_pane(new_pane_id, false); + tab.close_pane(new_pane_id, false, None); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -2072,7 +2174,7 @@ fn suppress_floating_pane_embed_it_and_close_it() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); @@ -2081,7 +2183,7 @@ fn suppress_floating_pane_embed_it_and_close_it() { tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes())) .unwrap(); tab.toggle_pane_embed_or_floating(client_id).unwrap(); - tab.close_pane(editor_pane_id, false); + tab.close_pane(editor_pane_id, false, None); tab.render(&mut output, None).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -2132,7 +2234,7 @@ fn resize_whole_tab_while_floting_pane_is_suppressed() { let editor_pane_id = PaneId::Terminal(3); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); tab.suppress_active_pane(editor_pane_id, client_id).unwrap(); @@ -2233,7 +2335,7 @@ fn enter_search_floating_pane() { let mut tab = create_new_tab(size, mode_info); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id, None, None, Some(client_id)) .unwrap(); @@ -2773,7 +2875,7 @@ fn move_floating_pane_focus_sends_tty_csi_event() { let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -2816,12 +2918,12 @@ fn toggle_floating_panes_on_sends_tty_csi_event() { let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.handle_pty_bytes( 1, // subscribe to focus events @@ -2840,7 +2942,7 @@ fn toggle_floating_panes_on_sends_tty_csi_event() { Vec::from("\u{1b}[?1004h".as_bytes()), ) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); assert_snapshot!(format!("{:?}", *tty_stdin_bytes.lock().unwrap())); } @@ -2860,7 +2962,7 @@ fn toggle_floating_panes_off_sends_tty_csi_event() { let new_pane_id_1 = PaneId::Terminal(2); let new_pane_id_2 = PaneId::Terminal(3); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.new_pane(new_pane_id_1, None, None, Some(client_id)) .unwrap(); tab.new_pane(new_pane_id_2, None, None, Some(client_id)) @@ -2883,6 +2985,3867 @@ fn toggle_floating_panes_off_sends_tty_csi_event() { Vec::from("\u{1b}[?1004h".as_bytes()), ) .unwrap(); - tab.toggle_floating_panes(client_id, None).unwrap(); + tab.toggle_floating_panes(Some(client_id), None).unwrap(); assert_snapshot!(format!("{:?}", *tty_stdin_bytes.lock().unwrap())); } + +#[test] +fn can_swap_tiled_layout_at_runtime() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab max_panes=2 split_direction="vertical" { + pane + pane + } + } + swap_tiled_layout { + tab max_panes=2 { + pane + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_swap_floating_layout_at_runtime() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes max_panes=2 { + pane + pane + } + } + swap_floating_layout { + floating_panes max_panes=2 { + pane { + x "0%" + } + pane { + x "100%" + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + + tab.toggle_floating_panes(Some(client_id), None).unwrap(); + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn swapping_layouts_after_resize_snaps_to_current_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane + } + } + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane + pane + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn swap_tiled_layout_with_stacked_children() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn swap_tiled_layout_with_stacked_children_and_no_pane_frames() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + false, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn move_focus_up_with_stacked_panes() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.move_focus_up(client_id); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn move_focus_down_with_stacked_panes() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.move_focus_up(client_id); + tab.move_focus_down(client_id); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn move_focus_right_into_stacked_panes() { + // here we make sure that when we focus right into a stack, + // we will always focus on the "main" pane of the stack + // and not on one of its folds + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + for i in 0..12 { + let new_pane_id = i + 2; + tab.new_pane(PaneId::Terminal(new_pane_id), None, None, Some(client_id)) + .unwrap(); + } + tab.move_focus_left(client_id); + tab.horizontal_split(PaneId::Terminal(16), None, client_id) + .unwrap(); + + tab.move_focus_up(client_id); + tab.move_focus_right(client_id); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((62, 12)), + "cursor coordinates moved to the main pane of the stack", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn move_focus_left_into_stacked_panes() { + // here we make sure that when we focus left into a stack, + // we will always focus on the "main" pane of the stack + // and not on one of its folds + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane { children stacked=true; } + pane focus=true + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + for i in 0..13 { + let new_pane_id = i + 2; + tab.new_pane(PaneId::Terminal(new_pane_id), None, None, Some(client_id)) + .unwrap(); + } + tab.move_focus_right(client_id); + tab.horizontal_split(PaneId::Terminal(1), None, client_id) + .unwrap(); + + tab.move_focus_up(client_id); + tab.move_focus_left(client_id); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((1, 12)), + "cursor coordinates moved to the main pane of the stack", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn move_focus_up_into_stacked_panes() { + // here we make sure that when we focus up into a stack, + // the main pane will become the lowest pane and the sizes + // in the stack will be adjusted accordingly + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + for i in 0..4 { + let new_pane_id = i + 3; + tab.new_pane(PaneId::Terminal(new_pane_id), None, None, Some(client_id)) + .unwrap(); + } + tab.move_focus_right(client_id); + tab.move_focus_up(client_id); + tab.move_focus_left(client_id); + tab.move_focus_down(client_id); + tab.vertical_split(PaneId::Terminal(7), None, client_id) + .unwrap(); + + tab.move_focus_up(client_id); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((62, 9)), + "cursor coordinates moved to the main pane of the stack", + ); + assert_snapshot!(snapshot); +} + +#[test] +fn move_focus_down_into_stacked_panes() { + // here we make sure that when we focus down into a stack, + // the main pane will become the highest pane and the sizes + // in the stack will be adjusted accordingly + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + for i in 0..4 { + let new_pane_id = i + 3; + tab.new_pane(PaneId::Terminal(new_pane_id), None, None, Some(client_id)) + .unwrap(); + } + tab.move_focus_left(client_id); + tab.move_focus_up(client_id); + tab.vertical_split(PaneId::Terminal(7), None, client_id) + .unwrap(); + + tab.move_focus_down(client_id); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((62, 8)), + "cursor coordinates moved to the main pane of the stack", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn close_main_stacked_pane() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.close_pane(new_pane_id_2, false, None); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn close_main_stacked_pane_in_mid_stack() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.move_focus_up(client_id); + tab.move_focus_up(client_id); + tab.close_pane(new_pane_id_3, false, None); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn close_one_liner_stacked_pane_below_main_pane() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_left(client_id); + tab.move_focus_right(client_id); + tab.move_focus_up(client_id); + tab.move_focus_up(client_id); + tab.close_pane(new_pane_id_2, false, None); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn close_one_liner_stacked_pane_above_main_pane() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.move_focus_up(client_id); + tab.move_focus_up(client_id); + tab.close_pane(new_pane_id_1, false, None); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_increase_size_of_main_pane_in_stack_horizontally() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Left)), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_increase_size_of_main_pane_in_stack_vertically() { + let size = Size { + cols: 121, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Down)), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_increase_size_of_main_pane_in_stack_non_directionally() { + let size = Size { + cols: 121, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn increasing_size_of_main_pane_in_stack_horizontally_does_not_break_stack() { + // here we test a situation where we're increasing the size of the main pane in a stack + // while adjacent to this main pane there's another pane perfectly aligned to it + // if the pane weren't a member of the stack, we would increase into that adjacent pane + // now, we increase all of the stack also into the panes above said pane + let size = Size { + cols: 121, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane { + pane focus=true + pane + } + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + let new_pane_id_6 = PaneId::Terminal(7); + let new_pane_id_7 = PaneId::Terminal(8); + let new_pane_id_8 = PaneId::Terminal(9); + let new_pane_id_9 = PaneId::Terminal(10); + let new_pane_id_10 = PaneId::Terminal(11); + let new_pane_id_11 = PaneId::Terminal(12); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_6, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_7, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_8, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_9, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_10, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_11, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Left)), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_increase_size_into_pane_stack_horizontally() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Right)), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_increase_size_into_pane_stack_vertically() { + let size = Size { + cols: 121, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_right(client_id); + tab.move_focus_down(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Up)), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn can_increase_size_into_pane_stack_non_directionally() { + let size = Size { + cols: 121, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_left(client_id); + tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn increasing_size_into_main_pane_in_stack_horizontally_does_not_break_stack() { + // here we test a situation where we're increasing the size of the main pane in a stack + // while adjacent to this main pane there's another pane perfectly aligned to it + // if the pane weren't a member of the stack, we would increase into that adjacent pane + // now, we increase all of the stack also into the panes above said pane + let size = Size { + cols: 121, + rows: 40, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane + pane split_direction="vertical" { + pane { + pane focus=true + pane + } + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + let new_pane_id_6 = PaneId::Terminal(7); + let new_pane_id_7 = PaneId::Terminal(8); + let new_pane_id_8 = PaneId::Terminal(9); + let new_pane_id_9 = PaneId::Terminal(10); + let new_pane_id_10 = PaneId::Terminal(11); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_6, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_7, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_8, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_9, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_10, None, None, Some(client_id)) + .unwrap(); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Right)), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn decreasing_size_of_whole_tab_treats_stacked_panes_properly() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.resize_whole_tab(Size { + cols: 100, + rows: 10, + }); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn increasing_size_of_whole_tab_treats_stacked_panes_properly() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.resize_whole_tab(Size { + cols: 100, + rows: 10, + }); + tab.resize_whole_tab(Size { + cols: 121, + rows: 20, + }); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn cannot_decrease_stack_size_beyond_minimum_height() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_down(client_id); + for _ in 0..6 { + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Up)), + ) + .unwrap(); + } + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn focus_stacked_pane_over_flexible_pane_with_the_mouse() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.handle_left_click(&Position::new(1, 71), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn focus_stacked_pane_under_flexible_pane_with_the_mouse() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.handle_left_click(&Position::new(1, 71), client_id) + .unwrap(); + tab.handle_left_click(&Position::new(9, 71), client_id) + .unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_snapshot!(snapshot); +} + +#[test] +fn close_stacked_pane_with_previously_focused_other_pane() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.handle_left_click(&Position::new(2, 71), client_id) + .unwrap(); + tab.handle_left_click(&Position::new(1, 71), client_id) + .unwrap(); + tab.close_pane(PaneId::Terminal(4), false, None); + tab.render(&mut output, None).unwrap(); + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((62, 2)), + "cursor coordinates moved to the main pane of the stack", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn close_pane_near_stacked_panes() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane + pane { children stacked=true; } + } + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.close_pane(PaneId::Terminal(6), false, None); + tab.render(&mut output, None).unwrap(); + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((62, 4)), + "cursor coordinates moved to the main pane of the stack", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn focus_next_pane_expands_stacked_panes() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_left(client_id); + tab.focus_next_pane(client_id); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn stacked_panes_can_become_fullscreen() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + pane + } + } + } + "#; + let layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + None, + true, + ); + let new_pane_id_1 = PaneId::Terminal(2); + let new_pane_id_2 = PaneId::Terminal(3); + let new_pane_id_3 = PaneId::Terminal(4); + let new_pane_id_4 = PaneId::Terminal(5); + let new_pane_id_5 = PaneId::Terminal(6); + + tab.new_pane(new_pane_id_1, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_2, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_3, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_4, None, None, Some(client_id)) + .unwrap(); + tab.new_pane(new_pane_id_5, None, None, Some(client_id)) + .unwrap(); + tab.move_focus_up(client_id); + tab.toggle_active_pane_fullscreen(client_id); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn layout_with_plugins_and_commands_swaped_properly() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="vertical" { + pane command="command1" + pane + pane command="command2" + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + pane command="command2" + pane command="command1" + pane + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_floating_terminal_ids = vec![]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn base_layout_is_included_in_swap_layouts() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="vertical" { + pane command="command1" + pane + pane command="command2" + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + pane command="command2" + pane command="command1" + pane + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_floating_terminal_ids = vec![]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.previous_swap_layout(Some(client_id)).unwrap(); // move back to the base layout + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_layouts_including_command_panes_absent_from_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="vertical" { + pane + pane + pane + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + pane command="command2" + pane command="command1" + pane + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_layouts_not_including_command_panes_present_in_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="vertical" { + pane command="command1" + pane + pane command="command2" + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + pane + pane + pane + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_floating_terminal_ids = vec![]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_layouts_including_plugin_panes_absent_from_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane size=2 borderless=true + pane split_direction="vertical" { + pane + pane + pane + } + pane size=1 borderless=true + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + pane + pane + pane + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_layouts_not_including_plugin_panes_present_in_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="vertical" { + pane command="command1" + pane + pane command="command2" + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane size=2 + pane + pane + pane + pane size=1 + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_floating_terminal_ids = vec![]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn new_pane_in_auto_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout + "#; + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab max_panes=5 { + pane split_direction="vertical" { + pane + pane { children; } + } + } + tab max_panes=8 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + + let mut expected_cursor_coordinates = vec![ + (62, 1), + (62, 11), + (62, 15), + (62, 16), + (1, 11), + (1, 15), + (1, 16), + ]; + for i in 0..7 { + let new_pane_id = i + 2; + tab.new_pane(PaneId::Terminal(new_pane_id), None, None, Some(client_id)) + .unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + let (expected_x, expected_y) = expected_cursor_coordinates.remove(0); + assert_eq!( + cursor_coordinates, + Some((expected_x, expected_y)), + "cursor coordinates moved to the new pane", + ); + assert_snapshot!(snapshot); + } +} + +#[test] +fn when_swapping_tiled_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane + pane + pane + } + "#; + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane + pane + pane + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.move_focus_down(client_id); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Down)), + ) + .unwrap(); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((1, 8)), + "cursor coordinates moved to the new pane", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn when_swapping_tiled_layouts_in_an_undamaged_state_pane_focuses_on_focused_node() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane + pane + pane + } + "#; + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane focus=true + pane + pane + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.move_focus_down(client_id); + tab.next_swap_layout(Some(client_id), true).unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((1, 1)), + "cursor coordinates moved to the new pane", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn when_swapping_tiled_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node( +) { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane + pane + pane + } + "#; + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane + pane + pane + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.move_focus_down(client_id); + tab.next_swap_layout(Some(client_id), true).unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((82, 1)), + "cursor coordinates moved to the new pane", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn when_closing_a_pane_in_auto_layout_the_focus_goes_to_last_focused_pane() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + pane + pane + pane + } + "#; + let swap_layouts = r#" + layout { + swap_tiled_layout { + tab { + pane split_direction="vertical" { + pane + pane + pane + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.move_focus_down(client_id); + tab.move_focus_down(client_id); + tab.close_pane(PaneId::Terminal(3), false, Some(client_id)); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((1, 11)), + "cursor coordinates moved to the new pane", + ); + assert_snapshot!(snapshot); +} + +#[test] +fn floating_layout_with_plugins_and_commands_swaped_properly() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane x=0 y=0 { + plugin location="zellij:tab-bar" + } + pane x=0 y=10 command="command1" + pane + pane x=50 y=10 command="command2" + pane x=50 y=0 { + plugin location="zellij:status-bar" + } + } + } + "#; + // this swap layout swaps between the location of the plugins and the commands + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane x=0 y=0 { + plugin location="zellij:status-bar" + } + pane x=0 y=10 command="command2" + pane + pane x=50 y=10 command="command1" + pane x=50 y=0 { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_floating_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_terminal_ids = vec![(4, None)]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn base_floating_layout_is_included_in_swap_layouts() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane x=0 y=0 { + plugin location="zellij:tab-bar" + } + pane x=0 y=10 command="command1" + pane + pane x=50 y=10 command="command2" + pane x=50 y=0 { + plugin location="zellij:status-bar" + } + } + } + "#; + // this swap layout swaps between the location of the plugins and the commands + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane x=0 y=0 { + plugin location="zellij:status-bar" + } + pane x=0 y=10 command="command2" + pane + pane x=50 y=10 command="command1" + pane x=50 y=0 { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_floating_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_terminal_ids = vec![(4, None)]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.previous_swap_layout(Some(client_id)).unwrap(); // move back to the base layout + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_floating_layouts_including_command_panes_absent_from_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane { + plugin location="zellij:tab-bar" + } + pane + pane + pane + pane { + plugin location="zellij:status-bar" + } + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane { + plugin location="zellij:status-bar" + } + pane x=0 y=0 command="command1" + pane x=10 y=10 command="command2" + pane + pane { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_floating_layouts_not_including_command_panes_present_in_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane { + plugin location="zellij:tab-bar" + } + pane command="command1" + pane + pane command="command2" + pane { + plugin location="zellij:status-bar" + } + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane { + plugin location="zellij:status-bar" + } + pane + pane + pane + pane { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_floating_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_terminal_ids = vec![(4, None)]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_floating_layouts_including_plugin_panes_absent_from_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane + pane + pane + } + } + "#; + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane { + plugin location="zellij:status-bar" + } + pane + pane { + plugin location="zellij:tab-bar" + } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn swap_floating_layouts_not_including_plugin_panes_present_in_existing_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane { + plugin location="zellij:tab-bar" + } + pane + pane { + plugin location="zellij:status-bar" + } + } + } + "#; + // this swap layout changes both the split direction of the two command panes and the location + // of the plugins - we want to make sure that they are all placed properly and not switched + // around + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane + pane + pane + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let mut command_1 = RunCommand::default(); + command_1.command = PathBuf::from("command1"); + let mut command_2 = RunCommand::default(); + command_2.command = PathBuf::from("command2"); + let new_floating_terminal_ids = vec![(1, Some(command_1)), (2, None), (3, Some(command_2))]; + let new_terminal_ids = vec![(4, None)]; + let mut new_plugin_ids = HashMap::new(); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + vec![1], + ); + new_plugin_ids.insert( + RunPluginLocation::Zellij(PluginTag::new("status-bar")), + vec![2], + ); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let _ = tab.handle_plugin_bytes(1, 1, "I am a tab bar".as_bytes().to_vec()); + let _ = tab.handle_plugin_bytes(2, 1, "I am a\n\rstatus bar".as_bytes().to_vec()); + tab.next_swap_layout(Some(client_id), false).unwrap(); + tab.render(&mut output, None).unwrap(); + let snapshot = take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn new_floating_pane_in_auto_layout() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout + "#; + let swap_layouts = r#" + layout { + swap_floating_layout name="spread" { + floating_panes max_panes=1 { + pane {y "50%"; x "50%"; } + } + floating_panes max_panes=2 { + pane { x "1%"; y "25%"; width "45%"; } + pane { x "50%"; y "25%"; width "45%"; } + } + floating_panes max_panes=3 { + pane focus=true { y "55%"; width "45%"; height "45%"; } + pane { x "1%"; y "1%"; width "45%"; } + pane { x "50%"; y "1%"; width "45%"; } + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_terminal_ids = vec![(1, None)]; + let new_floating_terminal_ids = vec![]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + + let mut expected_cursor_coordinates = vec![(62, 11), (62, 6), (31, 12)]; + for i in 0..3 { + let new_pane_id = i + 2; + let should_float = true; + tab.new_pane( + PaneId::Terminal(new_pane_id), + None, + Some(should_float), + Some(client_id), + ) + .unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + let (expected_x, expected_y) = expected_cursor_coordinates.remove(0); + assert_eq!( + cursor_coordinates, + Some((expected_x, expected_y)), + "cursor coordinates moved to the new pane", + ); + assert_snapshot!(snapshot); + } +} + +#[test] +fn when_swapping_floating_layouts_in_a_damaged_state_layout_and_pane_focus_are_unchanged() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane x=0 y=0 + pane + pane + } + } + "#; + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane + pane + pane + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.resize( + client_id, + ResizeStrategy::new(Resize::Increase, Some(Direction::Down)), + ) + .unwrap(); + tab.next_swap_layout(Some(client_id), true).unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((33, 8)), + "cursor coordinates moved to the new pane", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn when_swapping_floating_layouts_in_an_undamaged_state_pane_focuses_on_focused_node() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane x=0 y=0 + pane + pane + } + } + "#; + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane focus=true + pane + pane + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.next_swap_layout(Some(client_id), true).unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((31, 6)), + "cursor coordinates moved to the new pane", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn when_swapping_floating_layouts_in_an_undamaged_state_with_no_focus_node_pane_focuses_on_deepest_node( +) { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane focus=true + pane + pane + } + } + "#; + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane + pane + pane + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.next_swap_layout(Some(client_id), true).unwrap(); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((35, 10)), + "cursor coordinates moved to the new pane", + ); + + assert_snapshot!(snapshot); +} + +#[test] +fn when_closing_a_floating_pane_in_auto_layout_the_focus_goes_to_last_focused_pane() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane + pane + pane + } + } + "#; + let swap_layouts = r#" + layout { + swap_floating_layout { + floating_panes { + pane + pane + pane + } + } + } + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + tab.move_focus_up(client_id); + tab.move_focus_up(client_id); + tab.close_pane(PaneId::Terminal(1), false, Some(client_id)); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((31, 6)), + "cursor coordinates moved to the new pane", + ); + assert_snapshot!(snapshot); +} + +#[test] +fn when_resizing_whole_tab_with_auto_layout_and_floating_panes_the_layout_is_maintained() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut output = Output::default(); + let base_layout = r#" + layout { + floating_panes { + pane + pane + pane + } + } + "#; + let swap_layouts = r#" + layout + "#; + let (base_layout, base_floating_layout) = + Layout::from_kdl(base_layout, "file_name.kdl".into(), None, None) + .unwrap() + .template + .unwrap(); + + let new_floating_terminal_ids = vec![(1, None), (2, None), (3, None)]; + let new_terminal_ids = vec![(4, None)]; + let new_plugin_ids = HashMap::new(); + + let swap_layout = Layout::from_kdl(swap_layouts, "file_name.kdl".into(), None, None).unwrap(); + let swap_tiled_layouts = swap_layout.swap_tiled_layouts.clone(); + let swap_floating_layouts = swap_layout.swap_floating_layouts.clone(); + let mut tab = create_new_tab_with_swap_layouts( + size, + ModeInfo::default(), + (swap_tiled_layouts, swap_floating_layouts), + Some(( + base_layout, + base_floating_layout, + new_terminal_ids, + new_floating_terminal_ids, + new_plugin_ids, + )), + true, + ); + let new_size = Size { + cols: 150, + rows: 30, + }; + tab.resize_whole_tab(new_size); + tab.render(&mut output, None).unwrap(); + + let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( + output.serialize().unwrap().get(&client_id).unwrap(), + new_size.rows, + new_size.cols, + Palette::default(), + ); + assert_eq!( + cursor_coordinates, + Some((43, 13)), + "cursor coordinates moved to the new pane", + ); + assert_snapshot!(snapshot); +} diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 1a87e2e1..8fbf6649 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -10,7 +10,7 @@ use crate::{ use std::path::PathBuf; use zellij_utils::data::{Direction, Resize, ResizeStrategy}; use zellij_utils::errors::prelude::*; -use zellij_utils::input::layout::{PaneLayout, SplitDirection, SplitSize}; +use zellij_utils::input::layout::{SplitDirection, SplitSize, TiledPaneLayout}; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -146,6 +146,7 @@ fn create_new_tab(size: Size) -> Tab { let mode_info = ModeInfo::default(); let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -169,15 +170,17 @@ fn create_new_tab(size: Size) -> Tab { style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); tab.apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], vec![(1, None)], vec![], @@ -188,7 +191,7 @@ fn create_new_tab(size: Size) -> Tab { tab } -fn create_new_tab_with_layout(size: Size, layout: PaneLayout) -> Tab { +fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab { let index = 0; let position = 0; let name = String::new(); @@ -198,6 +201,7 @@ fn create_new_tab_with_layout(size: Size, layout: PaneLayout) -> Tab { let mode_info = ModeInfo::default(); let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -221,12 +225,14 @@ fn create_new_tab_with_layout(size: Size, layout: PaneLayout) -> Tab { style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); let mut new_terminal_ids = vec![]; for i in 0..layout.extract_run_instructions().len() { @@ -257,6 +263,7 @@ fn create_new_tab_with_cell_size( let mode_info = ModeInfo::default(); let style = Style::default(); let draw_pane_frames = true; + let auto_layout = true; let client_id = 1; let session_is_mirrored = true; let mut connected_clients = HashSet::new(); @@ -279,15 +286,17 @@ fn create_new_tab_with_cell_size( style, mode_info, draw_pane_frames, + auto_layout, connected_clients, session_is_mirrored, client_id, copy_options, terminal_emulator_colors, terminal_emulator_color_codes, + (vec![], vec![]), // swap layouts ); tab.apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], vec![(1, None)], vec![], @@ -738,11 +747,11 @@ pub fn cannot_split_largest_pane_when_there_is_no_room() { #[test] pub fn cannot_split_panes_vertically_when_active_pane_has_fixed_columns() { let size = Size { cols: 50, rows: 20 }; - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - let mut fixed_child = PaneLayout::default(); + let mut fixed_child = TiledPaneLayout::default(); fixed_child.split_size = Some(SplitSize::Fixed(30)); - initial_layout.children = vec![fixed_child, PaneLayout::default()]; + initial_layout.children = vec![fixed_child, TiledPaneLayout::default()]; let mut tab = create_new_tab_with_layout(size, initial_layout); tab.vertical_split(PaneId::Terminal(3), None, 1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "Tab still has two panes"); @@ -751,11 +760,11 @@ pub fn cannot_split_panes_vertically_when_active_pane_has_fixed_columns() { #[test] pub fn cannot_split_panes_horizontally_when_active_pane_has_fixed_rows() { let size = Size { cols: 50, rows: 20 }; - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Horizontal; - let mut fixed_child = PaneLayout::default(); + let mut fixed_child = TiledPaneLayout::default(); fixed_child.split_size = Some(SplitSize::Fixed(12)); - initial_layout.children = vec![fixed_child, PaneLayout::default()]; + initial_layout.children = vec![fixed_child, TiledPaneLayout::default()]; let mut tab = create_new_tab_with_layout(size, initial_layout); tab.horizontal_split(PaneId::Terminal(3), None, 1).unwrap(); assert_eq!(tab.tiled_panes.panes.len(), 2, "Tab still has two panes"); @@ -5948,11 +5957,11 @@ pub fn cannot_resize_down_when_pane_has_fixed_rows() { rows: 20, }; - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Horizontal; - let mut fixed_child = PaneLayout::default(); + let mut fixed_child = TiledPaneLayout::default(); fixed_child.split_size = Some(SplitSize::Fixed(10)); - initial_layout.children = vec![fixed_child, PaneLayout::default()]; + initial_layout.children = vec![fixed_child, TiledPaneLayout::default()]; let mut tab = create_new_tab_with_layout(size, initial_layout); tab_resize_down(&mut tab, 1); @@ -5994,11 +6003,11 @@ pub fn cannot_resize_down_when_pane_below_has_fixed_rows() { rows: 20, }; - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Horizontal; - let mut fixed_child = PaneLayout::default(); + let mut fixed_child = TiledPaneLayout::default(); fixed_child.split_size = Some(SplitSize::Fixed(10)); - initial_layout.children = vec![PaneLayout::default(), fixed_child]; + initial_layout.children = vec![TiledPaneLayout::default(), fixed_child]; let mut tab = create_new_tab_with_layout(size, initial_layout); tab_resize_down(&mut tab, 1); @@ -6040,11 +6049,11 @@ pub fn cannot_resize_up_when_pane_below_has_fixed_rows() { rows: 20, }; - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Horizontal; - let mut fixed_child = PaneLayout::default(); + let mut fixed_child = TiledPaneLayout::default(); fixed_child.split_size = Some(SplitSize::Fixed(10)); - initial_layout.children = vec![PaneLayout::default(), fixed_child]; + initial_layout.children = vec![TiledPaneLayout::default(), fixed_child]; let mut tab = create_new_tab_with_layout(size, initial_layout); tab_resize_up(&mut tab, 1); @@ -11371,11 +11380,11 @@ pub fn cannot_resize_right_when_pane_has_fixed_columns() { rows: 20, }; - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - let mut fixed_child = PaneLayout::default(); + let mut fixed_child = TiledPaneLayout::default(); fixed_child.split_size = Some(SplitSize::Fixed(60)); - initial_layout.children = vec![fixed_child, PaneLayout::default()]; + initial_layout.children = vec![fixed_child, TiledPaneLayout::default()]; let mut tab = create_new_tab_with_layout(size, initial_layout); tab_resize_down(&mut tab, 1); @@ -14377,7 +14386,7 @@ fn correctly_resize_frameless_panes_on_pane_close() { tab.new_pane(PaneId::Terminal(2), None, None, Some(1)) .unwrap(); - tab.close_pane(PaneId::Terminal(2), true); + tab.close_pane(PaneId::Terminal(2), true, None); // the size should be the same after adding and then removing a pane let pane = tab.tiled_panes.panes.get(&PaneId::Terminal(1)).unwrap(); diff --git a/zellij-server/src/thread_bus.rs b/zellij-server/src/thread_bus.rs index 7f831d6b..3247ecf4 100644 --- a/zellij-server/src/thread_bus.rs +++ b/zellij-server/src/thread_bus.rs @@ -143,6 +143,12 @@ impl ThreadSenders { // this is mostly used for the tests, see struct self.to_pty_writer.replace(new_pty_writer); } + + #[allow(unused)] + pub fn replace_to_plugin(&mut self, new_to_plugin: SenderWithContext) { + // this is mostly used for the tests, see struct + self.to_plugin.replace(new_to_plugin); + } } /// A container for a receiver, OS input and the senders to a given thread diff --git a/zellij-server/src/ui/boundaries.rs b/zellij-server/src/ui/boundaries.rs index 2ba3e67e..29b614c1 100644 --- a/zellij-server/src/ui/boundaries.rs +++ b/zellij-server/src/ui/boundaries.rs @@ -436,6 +436,7 @@ pub struct Boundaries { pub boundary_characters: HashMap, } +#[allow(clippy::if_same_then_else)] impl Boundaries { pub fn new(viewport: Viewport) -> Self { Boundaries { @@ -444,6 +445,7 @@ impl Boundaries { } } pub fn add_rect(&mut self, rect: &dyn Pane, color: Option) { + let pane_is_stacked = rect.current_geom().is_stacked; if !self.is_fully_inside_screen(rect) { return; } @@ -456,8 +458,11 @@ impl Boundaries { let coordinates = Coordinates::new(boundary_x_coords, row); let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y { BoundarySymbol::new(boundary_type::TOP_LEFT).color(color) + } else if row == first_row_coordinates && pane_is_stacked { + BoundarySymbol::new(boundary_type::TOP_LEFT).color(color) } else if row == last_row_coordinates - 1 && row != self.viewport.y + self.viewport.rows - 1 + && !pane_is_stacked { BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color) } else { @@ -471,7 +476,7 @@ impl Boundaries { self.boundary_characters.insert(coordinates, next_symbol); } } - if rect.y() > self.viewport.y { + if rect.y() > self.viewport.y && !pane_is_stacked { // top boundary let boundary_y_coords = rect.y() - 1; let first_col_coordinates = self.rect_bottom_boundary_col_start(rect); @@ -504,6 +509,7 @@ impl Boundaries { BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color) } else if row == last_row_coordinates - 1 && row != self.viewport.y + self.viewport.rows - 1 + && !pane_is_stacked { BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color) } else { @@ -517,7 +523,7 @@ impl Boundaries { self.boundary_characters.insert(coordinates, next_symbol); } } - if self.rect_bottom_boundary_is_before_screen_edge(rect) { + if self.rect_bottom_boundary_is_before_screen_edge(rect) && !pane_is_stacked { // bottom boundary let boundary_y_coords = rect.bottom_boundary_y_coords() - 1; let first_col_coordinates = self.rect_bottom_boundary_col_start(rect); @@ -570,8 +576,10 @@ impl Boundaries { rect.y() + rect.rows() < self.viewport.y + self.viewport.rows } fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize { + let pane_is_stacked = rect.current_geom().is_stacked; + let horizontal_frame_offset = if pane_is_stacked { 0 } else { 1 }; if rect.y() > self.viewport.y { - rect.y() - 1 + rect.y() - horizontal_frame_offset } else { self.viewport.y } diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs index 537a80c4..61ec7a4b 100644 --- a/zellij-server/src/ui/pane_boundaries_frame.rs +++ b/zellij-server/src/ui/pane_boundaries_frame.rs @@ -75,6 +75,9 @@ pub struct FrameParams { pub style: Style, pub color: Option, pub other_cursors_exist_in_session: bool, + pub pane_is_stacked_under: bool, + pub pane_is_stacked_over: bool, + pub should_draw_pane_frames: bool, } #[derive(Default, PartialEq)] @@ -90,6 +93,9 @@ pub struct PaneFrame { pub other_focused_clients: Vec, exit_status: Option, is_first_run: bool, + pane_is_stacked_over: bool, + pane_is_stacked_under: bool, + should_draw_pane_frames: bool, } impl PaneFrame { @@ -111,6 +117,9 @@ impl PaneFrame { other_cursors_exist_in_session: frame_params.other_cursors_exist_in_session, exit_status: None, is_first_run: false, + pane_is_stacked_over: frame_params.pane_is_stacked_over, + pane_is_stacked_under: frame_params.pane_is_stacked_under, + should_draw_pane_frames: frame_params.should_draw_pane_frames, } } pub fn add_exit_status(&mut self, exit_status: Option) { @@ -130,6 +139,17 @@ impl PaneFrame { background_color(" ", color.map(|c| c.0)) } fn get_corner(&self, corner: &'static str) -> &'static str { + let corner = if !self.should_draw_pane_frames + && (corner == boundary_type::TOP_LEFT || corner == boundary_type::TOP_RIGHT) + { + boundary_type::HORIZONTAL + } else if self.pane_is_stacked_under && corner == boundary_type::TOP_RIGHT { + boundary_type::BOTTOM_RIGHT + } else if self.pane_is_stacked_under && corner == boundary_type::TOP_LEFT { + boundary_type::BOTTOM_LEFT + } else { + corner + }; if self.style.rounded_corners { match corner { boundary_type::TOP_RIGHT => boundary_type::TOP_RIGHT_ROUND, @@ -323,6 +343,13 @@ impl PaneFrame { self.render_my_and_others_focus(max_length) } else if !self.other_focused_clients.is_empty() { self.render_other_focused_users(max_length) + } else if self.pane_is_stacked_under || self.pane_is_stacked_over { + let (first_part, first_part_len) = self.first_exited_held_title_part_full(); + if first_part_len <= max_length { + Some((first_part, first_part_len)) + } else { + None + } } else { None } @@ -617,6 +644,14 @@ impl PaneFrame { .or_else(|| Some(self.title_line_without_middle())) .with_context(|| format!("failed to render title '{}'", self.title)) } + fn render_one_line_title(&self) -> Result> { + let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners + + self.render_title_middle(total_title_length) + .map(|(middle, middle_length)| self.title_line_with_middle(middle, &middle_length)) + .or_else(|| Some(self.title_line_without_middle())) + .with_context(|| format!("failed to render title '{}'", self.title)) + } fn render_held_undertitle(&self) -> Result> { let max_undertitle_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners let (mut first_part, first_part_len) = self.first_exited_held_title_part_full(); @@ -678,55 +713,69 @@ impl PaneFrame { pub fn render(&self) -> Result<(Vec, Option)> { let err_context = || "failed to render pane frame"; let mut character_chunks = vec![]; - for row in 0..self.geom.rows { - if row == 0 { - // top row - let title = self.render_title().with_context(err_context)?; - let x = self.geom.x; - let y = self.geom.y + row; - character_chunks.push(CharacterChunk::new(title, x, y)); - } else if row == self.geom.rows - 1 { - // bottom row - if self.exit_status.is_some() || self.is_first_run { + if self.geom.rows == 1 || !self.should_draw_pane_frames { + // we do this explicitly when not drawing pane frames because this should only happen + // if this is a stacked pane with pane frames off (and it doesn't necessarily have only + // 1 row because it could also be a flexible stacked pane) + // in this case we should always draw the pane title line, and only the title line + let one_line_title = self.render_one_line_title().with_context(err_context)?; + character_chunks.push(CharacterChunk::new( + one_line_title, + self.geom.x, + self.geom.y, + )); + } else { + for row in 0..self.geom.rows { + if row == 0 { + // top row + let title = self.render_title().with_context(err_context)?; let x = self.geom.x; let y = self.geom.y + row; - character_chunks.push(CharacterChunk::new( - self.render_held_undertitle().with_context(err_context)?, - x, - y, - )); - } else { - let mut bottom_row = vec![]; - for col in 0..self.geom.cols { - let boundary = if col == 0 { - // bottom left corner - self.get_corner(boundary_type::BOTTOM_LEFT) - } else if col == self.geom.cols - 1 { - // bottom right corner - self.get_corner(boundary_type::BOTTOM_RIGHT) - } else { - boundary_type::HORIZONTAL - }; + character_chunks.push(CharacterChunk::new(title, x, y)); + } else if row == self.geom.rows - 1 { + // bottom row + if self.exit_status.is_some() || self.is_first_run { + let x = self.geom.x; + let y = self.geom.y + row; + character_chunks.push(CharacterChunk::new( + self.render_held_undertitle().with_context(err_context)?, + x, + y, + )); + } else { + let mut bottom_row = vec![]; + for col in 0..self.geom.cols { + let boundary = if col == 0 { + // bottom left corner + self.get_corner(boundary_type::BOTTOM_LEFT) + } else if col == self.geom.cols - 1 { + // bottom right corner + self.get_corner(boundary_type::BOTTOM_RIGHT) + } else { + boundary_type::HORIZONTAL + }; - let mut boundary_character = foreground_color(boundary, self.color); - bottom_row.append(&mut boundary_character); + let mut boundary_character = foreground_color(boundary, self.color); + bottom_row.append(&mut boundary_character); + } + let x = self.geom.x; + let y = self.geom.y + row; + character_chunks.push(CharacterChunk::new(bottom_row, x, y)); } + } else { + let boundary_character_left = + foreground_color(boundary_type::VERTICAL, self.color); + let boundary_character_right = + foreground_color(boundary_type::VERTICAL, self.color); + let x = self.geom.x; let y = self.geom.y + row; - character_chunks.push(CharacterChunk::new(bottom_row, x, y)); + character_chunks.push(CharacterChunk::new(boundary_character_left, x, y)); + + let x = (self.geom.x + self.geom.cols).saturating_sub(1); + let y = self.geom.y + row; + character_chunks.push(CharacterChunk::new(boundary_character_right, x, y)); } - } else { - let boundary_character_left = foreground_color(boundary_type::VERTICAL, self.color); - let boundary_character_right = - foreground_color(boundary_type::VERTICAL, self.color); - - let x = self.geom.x; - let y = self.geom.y + row; - character_chunks.push(CharacterChunk::new(boundary_character_left, x, y)); - - let x = (self.geom.x + self.geom.cols).saturating_sub(1); - let y = self.geom.y + row; - character_chunks.push(CharacterChunk::new(boundary_character_right, x, y)); } } Ok((character_chunks, None)) diff --git a/zellij-server/src/ui/pane_contents_and_ui.rs b/zellij-server/src/ui/pane_contents_and_ui.rs index 7f718d0b..72f0b804 100644 --- a/zellij-server/src/ui/pane_contents_and_ui.rs +++ b/zellij-server/src/ui/pane_contents_and_ui.rs @@ -16,6 +16,9 @@ pub struct PaneContentsAndUi<'a> { focused_clients: Vec, multiple_users_exist_in_session: bool, z_index: Option, + pane_is_stacked_under: bool, + pane_is_stacked_over: bool, + should_draw_pane_frames: bool, } impl<'a> PaneContentsAndUi<'a> { @@ -26,6 +29,9 @@ impl<'a> PaneContentsAndUi<'a> { active_panes: &HashMap, multiple_users_exist_in_session: bool, z_index: Option, + pane_is_stacked_under: bool, + pane_is_stacked_over: bool, + should_draw_pane_frames: bool, ) -> Self { let mut focused_clients: Vec = active_panes .iter() @@ -40,6 +46,9 @@ impl<'a> PaneContentsAndUi<'a> { focused_clients, multiple_users_exist_in_session, z_index, + pane_is_stacked_under, + pane_is_stacked_over, + should_draw_pane_frames, } } pub fn render_pane_contents_to_multiple_clients( @@ -194,6 +203,9 @@ impl<'a> PaneContentsAndUi<'a> { style: self.style, color: frame_color, other_cursors_exist_in_session: false, + pane_is_stacked_over: self.pane_is_stacked_over, + pane_is_stacked_under: self.pane_is_stacked_under, + should_draw_pane_frames: self.should_draw_pane_frames, } } else { FrameParams { @@ -203,6 +215,9 @@ impl<'a> PaneContentsAndUi<'a> { style: self.style, color: frame_color, other_cursors_exist_in_session: self.multiple_users_exist_in_session, + pane_is_stacked_over: self.pane_is_stacked_over, + pane_is_stacked_under: self.pane_is_stacked_under, + should_draw_pane_frames: self.should_draw_pane_frames, } }; diff --git a/zellij-server/src/unit/os_input_output_tests.rs b/zellij-server/src/unit/os_input_output_tests.rs index 58dbf777..7e651083 100644 --- a/zellij-server/src/unit/os_input_output_tests.rs +++ b/zellij-server/src/unit/os_input_output_tests.rs @@ -39,6 +39,7 @@ fn get_cwd() { orig_termios: Arc::new(Mutex::new(test_termios)), client_senders: Arc::default(), terminal_id_to_raw_fd: Arc::default(), + cached_resizes: Arc::default(), }; let pid = nix::unistd::getpid(); diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 7b1be55e..616989d1 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -14,7 +14,7 @@ use zellij_utils::data::Resize; use zellij_utils::errors::{prelude::*, ErrorContext}; use zellij_utils::input::actions::Action; use zellij_utils::input::command::{RunCommand, TerminalAction}; -use zellij_utils::input::layout::{PaneLayout, SplitDirection}; +use zellij_utils::input::layout::{Layout, SplitDirection, TiledPaneLayout}; use zellij_utils::input::options::Options; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -220,6 +220,7 @@ fn create_new_screen(size: Size) -> Screen { let mut mode_info = ModeInfo::default(); mode_info.session_name = Some("zellij-test".into()); let draw_pane_frames = false; + let auto_layout = true; let session_is_mirrored = true; let copy_options = CopyOptions::default(); @@ -229,6 +230,7 @@ fn create_new_screen(size: Size) -> Screen { max_panes, mode_info, draw_pane_frames, + auto_layout, session_is_mirrored, copy_options, ); @@ -257,7 +259,7 @@ struct MockScreen { } impl MockScreen { - pub fn run(&mut self, initial_layout: Option) -> std::thread::JoinHandle<()> { + pub fn run(&mut self, initial_layout: Option) -> std::thread::JoinHandle<()> { let config_options = self.config_options.clone(); let client_attributes = self.client_attributes.clone(); let screen_bus = Bus::new( @@ -299,6 +301,7 @@ impl MockScreen { Some(pane_layout.clone()), vec![], // floating_panes_layout tab_name, + (vec![], vec![]), // swap layouts self.main_client_id, )); let _ = self.to_screen.send(ScreenInstruction::ApplyLayout( @@ -313,7 +316,7 @@ impl MockScreen { self.last_opened_tab_index = Some(tab_index); screen_thread } - pub fn new_tab(&mut self, tab_layout: PaneLayout) { + pub fn new_tab(&mut self, tab_layout: TiledPaneLayout) { let pane_count = tab_layout.extract_run_instructions().len(); let mut pane_ids = vec![]; let plugin_ids = HashMap::new(); @@ -328,6 +331,7 @@ impl MockScreen { Some(tab_layout.clone()), vec![], // floating_panes_layout tab_name, + (vec![], vec![]), // swap layouts self.main_client_id, )); let _ = self.to_screen.send(ScreenInstruction::ApplyLayout( @@ -353,6 +357,7 @@ impl MockScreen { } pub fn clone_session_metadata(&self) -> SessionMetaData { // hack that only clones the clonable parts of SessionMetaData + let layout = Box::new(Layout::default()); // this is not actually correct!! SessionMetaData { senders: self.session_metadata.senders.clone(), capabilities: self.session_metadata.capabilities.clone(), @@ -363,6 +368,7 @@ impl MockScreen { plugin_thread: None, pty_writer_thread: None, background_jobs_thread: None, + layout, } } } @@ -399,6 +405,7 @@ impl MockScreen { arrow_fonts: Default::default(), }; + let layout = Box::new(Layout::default()); // this is not actually correct!! let session_metadata = SessionMetaData { senders: ThreadSenders { to_screen: Some(to_screen.clone()), @@ -417,6 +424,7 @@ impl MockScreen { plugin_thread: None, pty_writer_thread: None, background_jobs_thread: None, + layout, }; let os_input = FakeInputOutput::default(); @@ -474,10 +482,12 @@ fn new_tab(screen: &mut Screen, pid: u32, tab_index: usize) { let client_id = 1; let new_terminal_ids = vec![(pid, None)]; let new_plugin_ids = HashMap::new(); - screen.new_tab(tab_index, client_id).expect("TEST"); + screen + .new_tab(tab_index, (vec![], vec![]), client_id) + .expect("TEST"); screen .apply_layout( - PaneLayout::default(), + TiledPaneLayout::default(), vec![], // floating panes layout new_terminal_ids, vec![], // new floating terminal ids @@ -1039,9 +1049,9 @@ pub fn send_cli_write_action_to_screen() { pub fn send_cli_resize_action_to_screen() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1078,9 +1088,9 @@ pub fn send_cli_resize_action_to_screen() { pub fn send_cli_focus_next_pane_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1116,9 +1126,9 @@ pub fn send_cli_focus_next_pane_action() { pub fn send_cli_focus_previous_pane_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1154,9 +1164,9 @@ pub fn send_cli_focus_previous_pane_action() { pub fn send_cli_move_focus_pane_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1194,9 +1204,9 @@ pub fn send_cli_move_focus_pane_action() { pub fn send_cli_move_focus_or_tab_pane_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1234,9 +1244,9 @@ pub fn send_cli_move_focus_or_tab_pane_action() { pub fn send_cli_move_pane_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1248,7 +1258,7 @@ pub fn send_cli_move_pane_action() { server_receiver ); let cli_action = CliAction::MovePane { - direction: Direction::Right, + direction: Some(Direction::Right), }; send_cli_action_to_server(&session_metadata, cli_action, &mut mock_screen, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -1268,9 +1278,9 @@ pub fn send_cli_move_pane_action() { pub fn send_cli_dump_screen_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1302,9 +1312,9 @@ pub fn send_cli_dump_screen_action() { pub fn send_cli_edit_scrollback_action() { let size = Size { cols: 80, rows: 20 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1350,9 +1360,9 @@ pub fn send_cli_edit_scrollback_action() { pub fn send_cli_scroll_up_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1404,9 +1414,9 @@ pub fn send_cli_scroll_up_action() { pub fn send_cli_scroll_down_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1484,9 +1494,9 @@ pub fn send_cli_scroll_down_action() { pub fn send_cli_scroll_to_bottom_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1558,9 +1568,9 @@ pub fn send_cli_scroll_to_bottom_action() { pub fn send_cli_scroll_to_top_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1605,9 +1615,9 @@ pub fn send_cli_scroll_to_top_action() { pub fn send_cli_page_scroll_up_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1651,9 +1661,9 @@ pub fn send_cli_page_scroll_up_action() { pub fn send_cli_page_scroll_down_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1714,9 +1724,9 @@ pub fn send_cli_page_scroll_down_action() { pub fn send_cli_half_page_scroll_up_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1760,9 +1770,9 @@ pub fn send_cli_half_page_scroll_up_action() { pub fn send_cli_half_page_scroll_down_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1823,9 +1833,9 @@ pub fn send_cli_half_page_scroll_down_action() { pub fn send_cli_toggle_full_screen_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1860,9 +1870,9 @@ pub fn send_cli_toggle_full_screen_action() { pub fn send_cli_toggle_pane_frames_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -1903,9 +1913,9 @@ pub fn send_cli_toggle_active_tab_sync_action() { let mut mock_screen = MockScreen::new(size); let pty_writer_receiver = mock_screen.pty_writer_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_writer_thread = log_actions_in_thread!( @@ -1944,9 +1954,9 @@ pub fn send_cli_new_pane_action_with_default_parameters() { let mut mock_screen = MockScreen::new(size); let pty_receiver = mock_screen.pty_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( @@ -1984,9 +1994,9 @@ pub fn send_cli_new_pane_action_with_split_direction() { let mut mock_screen = MockScreen::new(size); let pty_receiver = mock_screen.pty_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( @@ -2024,9 +2034,9 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() { let mut mock_screen = MockScreen::new(size); let pty_receiver = mock_screen.pty_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( @@ -2064,9 +2074,9 @@ pub fn send_cli_edit_action_with_default_parameters() { let mut mock_screen = MockScreen::new(size); let pty_receiver = mock_screen.pty_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( @@ -2102,9 +2112,9 @@ pub fn send_cli_edit_action_with_line_number() { let mut mock_screen = MockScreen::new(size); let pty_receiver = mock_screen.pty_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( @@ -2140,9 +2150,9 @@ pub fn send_cli_edit_action_with_split_direction() { let mut mock_screen = MockScreen::new(size); let pty_receiver = mock_screen.pty_receiver.take().unwrap(); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( @@ -2177,9 +2187,9 @@ pub fn send_cli_switch_mode_action() { let client_id = 10; // fake client id should not appear in the screen's state let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let screen_thread = mock_screen.run(Some(initial_layout)); let cli_switch_mode = CliAction::SwitchMode { input_mode: InputMode::Locked, @@ -2206,9 +2216,9 @@ pub fn send_cli_switch_mode_action() { pub fn send_cli_toggle_pane_embed_or_float() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -2252,9 +2262,9 @@ pub fn send_cli_toggle_pane_embed_or_float() { pub fn send_cli_toggle_floating_panes() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -2307,9 +2317,9 @@ pub fn send_cli_toggle_floating_panes() { pub fn send_cli_close_pane_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -2344,9 +2354,9 @@ pub fn send_cli_close_pane_action() { pub fn send_cli_new_tab_action_default_params() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -2385,9 +2395,9 @@ pub fn send_cli_new_tab_action_default_params() { pub fn send_cli_new_tab_action_with_name_and_layout() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); let session_metadata = mock_screen.clone_session_metadata(); let screen_thread = mock_screen.run(Some(initial_layout)); @@ -2435,12 +2445,12 @@ pub fn send_cli_new_tab_action_with_name_and_layout() { pub fn send_cli_next_tab_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut second_tab_layout = TiledPaneLayout::default(); second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + second_tab_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); mock_screen.new_tab(second_tab_layout); let session_metadata = mock_screen.clone_session_metadata(); @@ -2476,12 +2486,12 @@ pub fn send_cli_next_tab_action() { pub fn send_cli_previous_tab_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut second_tab_layout = TiledPaneLayout::default(); second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + second_tab_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); mock_screen.new_tab(second_tab_layout); let session_metadata = mock_screen.clone_session_metadata(); @@ -2517,12 +2527,12 @@ pub fn send_cli_previous_tab_action() { pub fn send_cli_goto_tab_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut second_tab_layout = TiledPaneLayout::default(); second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + second_tab_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); mock_screen.new_tab(second_tab_layout); let session_metadata = mock_screen.clone_session_metadata(); @@ -2553,12 +2563,12 @@ pub fn send_cli_goto_tab_action() { pub fn send_cli_close_tab_action() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut second_tab_layout = TiledPaneLayout::default(); second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + second_tab_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); mock_screen.new_tab(second_tab_layout); let session_metadata = mock_screen.clone_session_metadata(); @@ -2589,12 +2599,12 @@ pub fn send_cli_close_tab_action() { pub fn send_cli_rename_tab() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut second_tab_layout = TiledPaneLayout::default(); second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + second_tab_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); mock_screen.new_tab(second_tab_layout); let session_metadata = mock_screen.clone_session_metadata(); @@ -2622,12 +2632,12 @@ pub fn send_cli_rename_tab() { pub fn send_cli_undo_rename_tab() { let size = Size { cols: 80, rows: 10 }; let client_id = 10; // fake client id should not appear in the screen's state - let mut initial_layout = PaneLayout::default(); + let mut initial_layout = TiledPaneLayout::default(); initial_layout.children_split_direction = SplitDirection::Vertical; - initial_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; - let mut second_tab_layout = PaneLayout::default(); + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut second_tab_layout = TiledPaneLayout::default(); second_tab_layout.children_split_direction = SplitDirection::Horizontal; - second_tab_layout.children = vec![PaneLayout::default(), PaneLayout::default()]; + second_tab_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; let mut mock_screen = MockScreen::new(size); mock_screen.new_tab(second_tab_layout); let session_metadata = mock_screen.clone_session_metadata(); diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap index 1ad6b84e..12dc9dd3 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap @@ -1,17 +1,17 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2300 +assertion_line: 2304 expression: "format!(\"{:#?}\", new_tab_action)" --- Some( NewTab( None, Some( - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -20,8 +20,9 @@ Some( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -30,6 +31,7 @@ Some( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -37,6 +39,7 @@ Some( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ), [], diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap index 7190eb8d..c4d4f40a 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap @@ -1,16 +1,16 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2350 +assertion_line: 2354 expression: "format!(\"{:#?}\", new_tab_instruction)" --- NewTab( None, Some( - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -23,8 +23,9 @@ NewTab( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -37,8 +38,9 @@ NewTab( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -51,6 +53,7 @@ NewTab( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -58,6 +61,7 @@ NewTab( borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ), [], diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap index fb7239db..957af3cc 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_rename_tab.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2534 +assertion_line: 2538 expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" --- [ @@ -29,11 +29,11 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" NewTab( None, Some( - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -42,8 +42,9 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -52,6 +53,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -59,6 +61,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ), [], @@ -167,6 +170,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -176,11 +183,11 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" NewTab( None, Some( - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -189,8 +196,9 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -199,6 +207,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -206,6 +215,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ), [], @@ -317,6 +327,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -327,6 +341,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -351,6 +369,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -361,6 +383,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -385,6 +411,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -395,6 +425,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap index bda17001..a229be90 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_undo_rename_tab.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2577 +assertion_line: 2581 expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" --- [ @@ -29,11 +29,11 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" NewTab( None, Some( - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -42,8 +42,9 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -52,6 +53,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -59,6 +61,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ), [], @@ -167,6 +170,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -176,11 +183,11 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" NewTab( None, Some( - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -189,8 +196,9 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -199,6 +207,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -206,6 +215,7 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ), [], @@ -317,6 +327,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -327,6 +341,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -351,6 +369,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -361,6 +383,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -385,6 +411,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -395,6 +425,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), @@ -430,6 +464,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, TabInfo { position: 1, @@ -440,6 +478,10 @@ expression: "format!(\"{:#?}\", * received_plugin_instructions.lock().unwrap())" is_sync_panes_active: false, are_floating_panes_visible: false, other_focused_clients: [], + active_swap_layout_name: Some( + "BASE", + ), + is_swap_layout_dirty: false, }, ], ), diff --git a/zellij-utils/assets/compact-bar.wasm b/zellij-utils/assets/compact-bar.wasm new file mode 100644 index 00000000..46267f5e Binary files /dev/null and b/zellij-utils/assets/compact-bar.wasm differ diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 248b4026..1e8ddd7c 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -40,6 +40,7 @@ keybinds { move { bind "Ctrl h" { SwitchToMode "Normal"; } bind "n" "Tab" { MovePane; } + bind "p" { MovePaneBackwards; } bind "h" "Left" { MovePane "Left"; } bind "j" "Down" { MovePane "Down"; } bind "k" "Up" { MovePane "Up"; } @@ -130,6 +131,7 @@ keybinds { bind "k" { MoveFocus "Up"; SwitchToMode "Normal"; } bind "o" { FocusNextPane; } bind "d" { Detach; } + bind "Space" { NextSwapLayout; } bind "x" { CloseFocus; SwitchToMode "Normal"; } } shared_except "locked" { @@ -142,6 +144,8 @@ keybinds { bind "Alt k" "Alt Up" { MoveFocus "Up"; } bind "Alt =" "Alt +" { Resize "Increase"; } bind "Alt -" { Resize "Decrease"; } + bind "Alt [" { PreviousSwapLayout; } + bind "Alt ]" { NextSwapLayout; } } shared_except "normal" "locked" { bind "Enter" "Esc" { SwitchToMode "Normal"; } @@ -203,6 +207,13 @@ plugins { // // pane_frames true +// Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible +// Options: +// - true (default) +// - false +// +// auto_layout true + // Define color themes for Zellij // For more examples, see: https://github.com/zellij-org/zellij/tree/main/example/themes // Once these themes are defined, one of them should to be selected in the "theme" section of this file diff --git a/zellij-utils/assets/layouts/compact.swap.kdl b/zellij-utils/assets/layouts/compact.swap.kdl new file mode 100644 index 00000000..e38fa210 --- /dev/null +++ b/zellij-utils/assets/layouts/compact.swap.kdl @@ -0,0 +1,91 @@ +tab_template name="ui" { + children + pane size=1 borderless=true { + plugin location="zellij:compact-bar" + } +} + +swap_tiled_layout name="vertical" { + ui max_panes=4 { + pane split_direction="vertical" { + pane + pane { children; } + } + } + ui max_panes=7 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + } + } + ui max_panes=11 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + pane { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="horizontal" { + ui max_panes=3 { + pane + pane + } + ui max_panes=7 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } + ui max_panes=11 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="stacked" { + ui min_panes=4 { + pane split_direction="vertical" { + pane + pane { children stacked=true; } + } + } +} + +swap_floating_layout name="staggered" { + floating_panes +} + +swap_floating_layout name="enlarged" { + floating_panes max_panes=10 { + pane { x 1; y 1; width "90%"; height "90%"; } + pane { x 2; y 2; width "90%"; height "90%"; } + pane { x 3; y 3; width "90%"; height "90%"; } + pane { x 4; y 4; width "90%"; height "90%"; } + pane { x 5; y 5; width "90%"; height "90%"; } + pane { x 6; y 6; width "90%"; height "90%"; } + pane { x 7; y 7; width "90%"; height "90%"; } + pane { x 8; y 8; width "90%"; height "90%"; } + pane { x 9; y 9; width "90%"; height "90%"; } + pane focus=true { x 10; y 10; width "90%"; height "90%"; } + } +} + +swap_floating_layout name="spread" { + floating_panes max_panes=1 { + pane {y "50%"; x "50%"; } + } + floating_panes max_panes=2 { + pane { x "1%"; y "25%"; width "45%"; } + pane { x "50%"; y "25%"; width "45%"; } + } + floating_panes max_panes=3 { + pane focus=true { y "55%"; width "45%"; height "45%"; } + pane { x "1%"; y "1%"; width "45%"; } + pane { x "50%"; y "1%"; width "45%"; } + } +} diff --git a/zellij-utils/assets/layouts/default.swap.kdl b/zellij-utils/assets/layouts/default.swap.kdl new file mode 100644 index 00000000..51e8afed --- /dev/null +++ b/zellij-utils/assets/layouts/default.swap.kdl @@ -0,0 +1,94 @@ +tab_template name="ui" { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + children + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} + +swap_tiled_layout name="vertical" { + ui max_panes=5 { + pane split_direction="vertical" { + pane + pane { children; } + } + } + ui max_panes=8 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + } + } + ui max_panes=12 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + pane { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="horizontal" { + ui max_panes=5 { + pane + pane + } + ui max_panes=8 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } + ui max_panes=12 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="stacked" { + ui min_panes=5 { + pane split_direction="vertical" { + pane + pane { children stacked=true; } + } + } +} + +swap_floating_layout name="staggered" { + floating_panes +} + +swap_floating_layout name="enlarged" { + floating_panes max_panes=10 { + pane { x 1; y 1; width "90%"; height "90%"; } + pane { x 2; y 2; width "90%"; height "90%"; } + pane { x 3; y 3; width "90%"; height "90%"; } + pane { x 4; y 4; width "90%"; height "90%"; } + pane { x 5; y 5; width "90%"; height "90%"; } + pane { x 6; y 6; width "90%"; height "90%"; } + pane { x 7; y 7; width "90%"; height "90%"; } + pane { x 8; y 8; width "90%"; height "90%"; } + pane { x 9; y 9; width "90%"; height "90%"; } + pane focus=true { x 10; y 10; width "90%"; height "90%"; } + } +} + +swap_floating_layout name="spread" { + floating_panes max_panes=1 { + pane {y "50%"; x "50%"; } + } + floating_panes max_panes=2 { + pane { x "1%"; y "25%"; width "45%"; } + pane { x "50%"; y "25%"; width "45%"; } + } + floating_panes max_panes=3 { + pane focus=true { y "55%"; width "45%"; height "45%"; } + pane { x "1%"; y "1%"; width "45%"; } + pane { x "50%"; y "1%"; width "45%"; } + } +} diff --git a/zellij-utils/assets/layouts/strider.kdl b/zellij-utils/assets/layouts/strider.kdl index dbe8d077..57ec01a7 100644 --- a/zellij-utils/assets/layouts/strider.kdl +++ b/zellij-utils/assets/layouts/strider.kdl @@ -1,19 +1,14 @@ layout { - default_tab_template { - pane size=1 borderless=true { - plugin location="zellij:tab-bar" - } - children - pane size=2 borderless=true { - plugin location="zellij:status-bar" - } + pane size=1 borderless=true { + plugin location="zellij:tab-bar" } - tab { - pane split_direction="Vertical" { - pane size="20%" { - plugin location="zellij:strider" - } - pane + pane split_direction="Vertical" { + pane size="20%" { + plugin location="zellij:strider" } + pane + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" } } diff --git a/zellij-utils/assets/layouts/strider.swap.kdl b/zellij-utils/assets/layouts/strider.swap.kdl new file mode 100644 index 00000000..c6df1575 --- /dev/null +++ b/zellij-utils/assets/layouts/strider.swap.kdl @@ -0,0 +1,102 @@ +tab_template name="ui" { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + pane split_direction="Vertical" { + pane size="20%" { + plugin location="zellij:strider" + } + pane { + children + } + + } + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } +} + +swap_tiled_layout name="vertical" { + ui max_panes=6 { + pane split_direction="vertical" { + pane + pane { children; } + } + } + ui max_panes=9 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + } + } + ui max_panes=13 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + pane { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="horizontal" { + ui max_panes=6 { + pane + pane + } + ui max_panes=9 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } + ui max_panes=13 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="stacked" { + ui min_panes=6 { + pane split_direction="vertical" { + pane focus=true + pane { children stacked=true; } + } + } +} + +swap_floating_layout name="staggered" { + floating_panes +} + +swap_floating_layout name="enlarged" { + floating_panes max_panes=10 { + pane { x 1; y 1; width "90%"; height "90%"; } + pane { x 2; y 2; width "90%"; height "90%"; } + pane { x 3; y 3; width "90%"; height "90%"; } + pane { x 4; y 4; width "90%"; height "90%"; } + pane { x 5; y 5; width "90%"; height "90%"; } + pane { x 6; y 6; width "90%"; height "90%"; } + pane { x 7; y 7; width "90%"; height "90%"; } + pane { x 8; y 8; width "90%"; height "90%"; } + pane { x 9; y 9; width "90%"; height "90%"; } + pane focus=true { x 10; y 10; width "90%"; height "90%"; } + } +} + +swap_floating_layout name="spread" { + floating_panes max_panes=1 { + pane {y "50%"; x "50%"; } + } + floating_panes max_panes=2 { + pane { x "1%"; y "25%"; width "45%"; } + pane { x "50%"; y "25%"; width "45%"; } + } + floating_panes max_panes=3 { + pane focus=true { y "55%"; width "45%"; height "45%"; } + pane { x "1%"; y "1%"; width "45%"; } + pane { x "50%"; y "1%"; width "45%"; } + } +} diff --git a/zellij-utils/assets/plugins/compact-bar.wasm b/zellij-utils/assets/plugins/compact-bar.wasm index 46267f5e..3a18eab8 100644 Binary files a/zellij-utils/assets/plugins/compact-bar.wasm and b/zellij-utils/assets/plugins/compact-bar.wasm differ diff --git a/zellij-utils/assets/plugins/status-bar.wasm b/zellij-utils/assets/plugins/status-bar.wasm index 5b4f6eb0..3ea5b48a 100644 Binary files a/zellij-utils/assets/plugins/status-bar.wasm and b/zellij-utils/assets/plugins/status-bar.wasm differ diff --git a/zellij-utils/assets/plugins/strider.wasm b/zellij-utils/assets/plugins/strider.wasm index 0af68b2f..589bab10 100644 Binary files a/zellij-utils/assets/plugins/strider.wasm and b/zellij-utils/assets/plugins/strider.wasm differ diff --git a/zellij-utils/assets/plugins/tab-bar.wasm b/zellij-utils/assets/plugins/tab-bar.wasm index 1166de83..1ebeacd8 100644 Binary files a/zellij-utils/assets/plugins/tab-bar.wasm and b/zellij-utils/assets/plugins/tab-bar.wasm differ diff --git a/zellij-utils/assets/status-bar.wasm b/zellij-utils/assets/status-bar.wasm new file mode 100644 index 00000000..5b4f6eb0 Binary files /dev/null and b/zellij-utils/assets/status-bar.wasm differ diff --git a/zellij-utils/assets/strider.wasm b/zellij-utils/assets/strider.wasm new file mode 100644 index 00000000..0af68b2f Binary files /dev/null and b/zellij-utils/assets/strider.wasm differ diff --git a/zellij-utils/assets/tab-bar.wasm b/zellij-utils/assets/tab-bar.wasm new file mode 100644 index 00000000..1166de83 Binary files /dev/null and b/zellij-utils/assets/tab-bar.wasm differ diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 1ef43e27..ea0b8d72 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -180,9 +180,13 @@ pub enum Sessions { #[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] pub enum CliAction { /// Write bytes to the terminal. - Write { bytes: Vec }, + Write { + bytes: Vec, + }, /// Write characters to the terminal. - WriteChars { chars: String }, + WriteChars { + chars: String, + }, /// [increase|decrease] the focused panes area at the [left|down|up|right] border. Resize { resize: Resize, @@ -193,13 +197,21 @@ pub enum CliAction { /// Change focus to the previous pane FocusPreviousPane, /// Move the focused pane in the specified direction. [right|left|up|down] - MoveFocus { direction: Direction }, + MoveFocus { + direction: Direction, + }, /// Move focus to the pane or tab (if on screen edge) in the specified direction /// [right|left|up|down] - MoveFocusOrTab { direction: Direction }, - /// Change the location of the focused pane in the specified direction + MoveFocusOrTab { + direction: Direction, + }, + /// Change the location of the focused pane in the specified direction or rotate forwrads /// [right|left|up|down] - MovePane { direction: Direction }, + MovePane { + direction: Option, + }, + /// Rotate the location of the previous pane backwards + MovePaneBackwards, /// Dump the focused pane to a file DumpScreen { path: PathBuf, @@ -296,7 +308,9 @@ pub enum CliAction { cwd: Option, }, /// Switch input mode of all connected clients [locked|pane|tab|resize|move|search|session] - SwitchMode { input_mode: InputMode }, + SwitchMode { + input_mode: InputMode, + }, /// Embed focused pane if floating or float focused pane if embedded TogglePaneEmbedOrFloating, /// Toggle the visibility of all fdirectionloating panes in the current Tab, open one if none exist @@ -304,7 +318,9 @@ pub enum CliAction { /// Close the focused pane. ClosePane, /// Renames the focused pane - RenamePane { name: String }, + RenamePane { + name: String, + }, /// Remove a previously set pane name UndoRenamePane, /// Go to the next tab. @@ -314,7 +330,9 @@ pub enum CliAction { /// Close the current tab. CloseTab, /// Go to tab with index [index] - GoToTab { index: u32 }, + GoToTab { + index: u32, + }, /// Go to tab with name [name] GoToTabName { name: String, @@ -323,7 +341,9 @@ pub enum CliAction { create: bool, }, /// Renames the focused pane - RenameTab { name: String }, + RenameTab { + name: String, + }, /// Remove a previously set tab name UndoRenameTab, /// Create a new tab, optionally with a specified tab layout and name @@ -340,4 +360,6 @@ pub enum CliAction { #[clap(short, long, value_parser, requires("layout"))] cwd: Option, }, + PreviousSwapLayout, + NextSwapLayout, } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index a0f255da..f5c4828e 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -686,6 +686,8 @@ pub struct TabInfo { pub is_sync_panes_active: bool, pub are_floating_panes_visible: bool, pub other_focused_clients: Vec, + pub active_swap_layout_name: Option, + pub is_swap_layout_dirty: bool, } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index a7986de8..3493f84b 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -249,6 +249,7 @@ pub enum ScreenContext { MoveFocusRight, MoveFocusRightOrNextTab, MovePane, + MovePaneBackwards, MovePaneDown, MovePaneUp, MovePaneRight, @@ -320,6 +321,8 @@ pub enum ScreenContext { SearchToggleWrap, AddRedPaneFrameColorOverride, ClearPaneFrameColorOverride, + PreviousSwapLayout, + NextSwapLayout, } /// Stack call representations corresponding to the different types of [`PtyInstruction`]s. diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 7db0e4f3..a4f4b74f 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,7 +1,9 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::{FloatingPanesLayout, Layout, PaneLayout}; +use super::layout::{ + FloatingPaneLayout, Layout, SwapFloatingLayout, SwapTiledLayout, TiledPaneLayout, +}; use crate::cli::CliAction; use crate::data::InputMode; use crate::data::{Direction, Resize}; @@ -115,6 +117,7 @@ pub enum Action { /// If there is no pane in the direction, move to previous/next Tab. MoveFocusOrTab(Direction), MovePane(Option), + MovePaneBackwards, /// Dumps the screen to a file DumpScreen(String, bool), /// Scroll up in focus pane. @@ -164,7 +167,13 @@ pub enum Action { PaneNameInput(Vec), UndoRenamePane, /// Create a new tab, optionally with a specified tab layout. - NewTab(Option, Vec, Option), // the String is the tab name + NewTab( + Option, + Vec, + Option>, + Option>, + Option, + ), // the String is the tab name /// Do nothing. NoOp, /// Go to the next tab. @@ -205,13 +214,15 @@ pub enum Action { /// Toggle case sensitivity of search SearchToggleOption(SearchOption), ToggleMouseMode, + PreviousSwapLayout, + NextSwapLayout, } impl Action { /// Checks that two Action are match except their mutable attributes. pub fn shallow_eq(&self, other_action: &Action) -> bool { match (self, other_action) { - (Action::NewTab(_, _, _), Action::NewTab(_, _, _)) => true, + (Action::NewTab(..), Action::NewTab(..)) => true, _ => self == other_action, } } @@ -228,7 +239,8 @@ impl Action { CliAction::FocusPreviousPane => Ok(vec![Action::FocusPreviousPane]), CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus(direction)]), CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]), - CliAction::MovePane { direction } => Ok(vec![Action::MovePane(Some(direction))]), + CliAction::MovePane { direction } => Ok(vec![Action::MovePane(direction)]), + CliAction::MovePaneBackwards => Ok(vec![Action::MovePaneBackwards]), CliAction::DumpScreen { path, full } => Ok(vec![Action::DumpScreen( path.as_os_str().to_string_lossy().into(), full, @@ -342,10 +354,10 @@ impl Action { .map(|cwd| current_dir.join(cwd)) .or_else(|| Some(current_dir)); if let Some(layout_path) = layout { - let (path_to_raw_layout, raw_layout) = + let (path_to_raw_layout, raw_layout, swap_layouts) = Layout::stringified_from_path_or_default(Some(&layout_path), None) .map_err(|e| format!("Failed to load layout: {}", e))?; - let layout = Layout::from_str(&raw_layout, path_to_raw_layout, cwd).map_err(|e| { + let layout = Layout::from_str(&raw_layout, path_to_raw_layout, swap_layouts.as_ref().map(|(f, p)| (f.as_str(), p.as_str())), cwd).map_err(|e| { let stringified_error = match e { ConfigError::KdlError(kdl_error) => { let error = kdl_error.add_src(layout_path.as_path().as_os_str().to_string_lossy().to_string(), String::from(raw_layout)); @@ -381,26 +393,52 @@ impl Action { if tabs.len() > 1 { return Err(format!("Tab layout cannot itself have tabs")); } else if !tabs.is_empty() { + let swap_tiled_layouts = if layout.swap_tiled_layouts.is_empty() { + None + } else { + Some(layout.swap_tiled_layouts.clone()) + }; + let swap_floating_layouts = if layout.swap_floating_layouts.is_empty() { + None + } else { + Some(layout.swap_floating_layouts.clone()) + }; let (tab_name, layout, floating_panes_layout) = tabs.drain(..).next().unwrap(); let name = tab_name.or(name); Ok(vec![Action::NewTab( Some(layout), floating_panes_layout, + swap_tiled_layouts, + swap_floating_layouts, name, )]) } else { + let swap_tiled_layouts = if layout.swap_tiled_layouts.is_empty() { + None + } else { + Some(layout.swap_tiled_layouts.clone()) + }; + let swap_floating_layouts = if layout.swap_floating_layouts.is_empty() { + None + } else { + Some(layout.swap_floating_layouts.clone()) + }; let (layout, floating_panes_layout) = layout.new_tab(); Ok(vec![Action::NewTab( Some(layout), floating_panes_layout, + swap_tiled_layouts, + swap_floating_layouts, name, )]) } } else { - Ok(vec![Action::NewTab(None, vec![], name)]) + Ok(vec![Action::NewTab(None, vec![], None, None, name)]) } }, + CliAction::PreviousSwapLayout => Ok(vec![Action::PreviousSwapLayout]), + CliAction::NextSwapLayout => Ok(vec![Action::NextSwapLayout]), } } } diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index eaa2e395..a0d61b24 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -22,6 +22,7 @@ use std::str::FromStr; use super::plugins::{PluginTag, PluginsConfigError}; use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; use std::convert::TryFrom; use std::vec::Vec; use std::{ @@ -169,6 +170,21 @@ impl Run { } } } + pub fn is_same_category(first: &Option, second: &Option) -> bool { + match (first, second) { + (Some(Run::Plugin(..)), Some(Run::Plugin(..))) => true, + (Some(Run::Command(..)), Some(Run::Command(..))) => true, + (Some(Run::EditFile(..)), Some(Run::EditFile(..))) => true, + (Some(Run::Cwd(..)), Some(Run::Cwd(..))) => true, + _ => false, + } + } + pub fn is_terminal(run: &Option) -> bool { + match run { + Some(Run::Command(..)) | Some(Run::EditFile(..)) | Some(Run::Cwd(..)) | None => true, + _ => false, + } + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -211,12 +227,27 @@ impl fmt::Display for RunPluginLocation { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)] +pub enum LayoutConstraint { + MaxPanes(usize), + MinPanes(usize), + NoConstraint, +} + +pub type SwapTiledLayout = (BTreeMap, Option); // Option is the swap layout name +pub type SwapFloatingLayout = ( + BTreeMap>, + Option, +); // Option is the swap layout name + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] pub struct Layout { - pub tabs: Vec<(Option, PaneLayout, Vec)>, + pub tabs: Vec<(Option, TiledPaneLayout, Vec)>, pub focused_tab_index: Option, - pub template: Option, - pub floating_panes_template: Vec, + pub template: Option<(TiledPaneLayout, Vec)>, + pub swap_layouts: Vec<(TiledPaneLayout, Vec)>, + pub swap_tiled_layouts: Vec, + pub swap_floating_layouts: Vec, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -270,8 +301,7 @@ impl FromStr for PercentOrFixed { } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] -pub struct FloatingPanesLayout { - // TODO: change name to singular +pub struct FloatingPaneLayout { pub name: Option, pub height: Option, pub width: Option, @@ -281,7 +311,7 @@ pub struct FloatingPanesLayout { pub focus: Option, } -impl FloatingPanesLayout { +impl FloatingPaneLayout { pub fn add_cwd_to_layout(&mut self, cwd: &PathBuf) { match self.run.as_mut() { Some(run) => run.add_cwd(cwd), @@ -292,9 +322,9 @@ impl FloatingPanesLayout { } } -impl From<&PaneLayout> for FloatingPanesLayout { - fn from(pane_layout: &PaneLayout) -> Self { - FloatingPanesLayout { +impl From<&TiledPaneLayout> for FloatingPaneLayout { + fn from(pane_layout: &TiledPaneLayout) -> Self { + FloatingPaneLayout { name: pane_layout.name.clone(), run: pane_layout.run.clone(), focus: pane_layout.focus, @@ -304,21 +334,22 @@ impl From<&PaneLayout> for FloatingPanesLayout { } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] -pub struct PaneLayout { +pub struct TiledPaneLayout { pub children_split_direction: SplitDirection, pub name: Option, - pub children: Vec, + pub children: Vec, pub split_size: Option, pub run: Option, pub borderless: bool, pub focus: Option, pub external_children_index: Option, + pub children_are_stacked: bool, } -impl PaneLayout { +impl TiledPaneLayout { pub fn insert_children_layout( &mut self, - children_layout: &mut PaneLayout, + children_layout: &mut TiledPaneLayout, ) -> Result { // returns true if successfully inserted and false otherwise match self.external_children_index { @@ -338,6 +369,30 @@ impl PaneLayout { }, } } + pub fn insert_children_nodes( + &mut self, + children_nodes: &mut Vec, + ) -> Result { + // returns true if successfully inserted and false otherwise + match self.external_children_index { + Some(external_children_index) => { + children_nodes.reverse(); + for child_node in children_nodes.drain(..) { + self.children.insert(external_children_index, child_node); + } + self.external_children_index = None; + Ok(true) + }, + None => { + for pane in self.children.iter_mut() { + if pane.insert_children_nodes(children_nodes)? { + return Ok(true); + } + } + Ok(false) + }, + } + } pub fn children_block_count(&self) -> usize { let mut count = 0; if self.external_children_index.is_some() { @@ -348,11 +403,50 @@ impl PaneLayout { } count } + pub fn pane_count(&self) -> usize { + if self.children.is_empty() { + 1 // self + } else { + let mut pane_count = 0; + for child in &self.children { + pane_count += child.pane_count(); + } + pane_count + } + } pub fn position_panes_in_space( &self, space: &PaneGeom, - ) -> Result, &'static str> { - let layouts = split_space(space, self, space); + max_panes: Option, + ) -> Result, &'static str> { + let layouts = match max_panes { + Some(max_panes) => { + let mut layout_to_split = self.clone(); + let pane_count_in_layout = layout_to_split.pane_count(); + if max_panes > pane_count_in_layout { + // the + 1 here is because this was previously an "actual" pane and will now + // become just a container, so we need to account for it too + // TODO: make sure this works when the `children` node has sibling nodes, + // because we really should support that + let children_count = (max_panes - pane_count_in_layout) + 1; + let mut extra_children = vec![TiledPaneLayout::default(); children_count]; + if !layout_to_split.has_focused_node() { + if let Some(last_child) = extra_children.last_mut() { + last_child.focus = Some(true); + } + } + let _ = layout_to_split.insert_children_nodes(&mut extra_children); + } else { + layout_to_split.truncate(max_panes); + } + if !layout_to_split.has_focused_node() { + layout_to_split.focus_deepest_pane(); + } + + split_space(space, &layout_to_split, space)? + }, + None => split_space(space, self, space)?, + }; for (_pane_layout, pane_geom) in layouts.iter() { if !pane_geom.is_at_least_minimum_size() { return Err("No room on screen for this layout!"); @@ -374,8 +468,8 @@ impl PaneLayout { run_instructions } pub fn with_one_pane() -> Self { - let mut default_layout = PaneLayout::default(); - default_layout.children = vec![PaneLayout::default()]; + let mut default_layout = TiledPaneLayout::default(); + default_layout.children = vec![TiledPaneLayout::default()]; default_layout } pub fn add_cwd_to_layout(&mut self, cwd: &PathBuf) { @@ -389,6 +483,79 @@ impl PaneLayout { child.add_cwd_to_layout(cwd); } } + pub fn deepest_depth(&self) -> usize { + let mut deepest_child_depth = 0; + for child in self.children.iter() { + let child_deepest_depth = child.deepest_depth(); + if child_deepest_depth > deepest_child_depth { + deepest_child_depth = child_deepest_depth; + } + } + deepest_child_depth + 1 + } + pub fn focus_deepest_pane(&mut self) { + let mut deepest_child_index = None; + let mut deepest_path = 0; + for (i, child) in self.children.iter().enumerate() { + let child_deepest_path = child.deepest_depth(); + if child_deepest_path >= deepest_path { + deepest_path = child_deepest_path; + deepest_child_index = Some(i) + } + } + match deepest_child_index { + Some(deepest_child_index) => { + if let Some(child) = self.children.get_mut(deepest_child_index) { + child.focus_deepest_pane(); + } + }, + None => { + self.focus = Some(true); + }, + } + } + pub fn truncate(&mut self, max_panes: usize) -> usize { + // returns remaining children length + // if max_panes is 1, it means there's only enough panes for this node, + // if max_panes is 0, this is probably the root layout being called with 0 max panes + if max_panes <= 1 { + self.children.clear(); + } else if max_panes <= self.children.len() { + self.children.truncate(max_panes); + self.children.iter_mut().for_each(|l| l.children.clear()); + } else { + let mut remaining_panes = max_panes + - self + .children + .iter() + .filter(|c| c.children.is_empty()) + .count(); + for child in self.children.iter_mut() { + if remaining_panes > 1 && child.children.len() > 0 { + remaining_panes = + remaining_panes.saturating_sub(child.truncate(remaining_panes)); + } else { + child.children.clear(); + } + } + } + if self.children.len() > 0 { + self.children.len() + } else { + 1 // just me + } + } + pub fn has_focused_node(&self) -> bool { + if self.focus.map(|f| f).unwrap_or(false) { + return true; + }; + for child in &self.children { + if child.has_focused_node() { + return true; + } + } + false + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -429,8 +596,8 @@ impl Layout { pub fn stringified_from_path_or_default( layout_path: Option<&PathBuf>, layout_dir: Option, - ) -> Result<(String, String), ConfigError> { - // (path_to_layout as String, stringified_layout) + ) -> Result<(String, String, Option<(String, String)>), ConfigError> { + // (path_to_layout as String, stringified_layout, Option) match layout_path { Some(layout_path) => { // The way we determine where to look for the layout is similar to @@ -455,24 +622,32 @@ impl Layout { layout_dir: Option, config: Config, ) -> Result<(Layout, Config), ConfigError> { - let (path_to_raw_layout, raw_layout) = + let (path_to_raw_layout, raw_layout, raw_swap_layouts) = Layout::stringified_from_path_or_default(layout_path, layout_dir)?; - let layout = Layout::from_kdl(&raw_layout, path_to_raw_layout, None)?; + let layout = Layout::from_kdl( + &raw_layout, + path_to_raw_layout, + raw_swap_layouts + .as_ref() + .map(|(r, f)| (r.as_str(), f.as_str())), + None, + )?; let config = Config::from_kdl(&raw_layout, Some(config))?; // this merges the two config, with Ok((layout, config)) } pub fn from_str( raw: &str, path_to_raw_layout: String, + swap_layouts: Option<(&str, &str)>, // Option cwd: Option, ) -> Result { - Layout::from_kdl(raw, path_to_raw_layout, cwd) + Layout::from_kdl(raw, path_to_raw_layout, swap_layouts, cwd) } pub fn stringified_from_dir( layout: &PathBuf, layout_dir: Option<&PathBuf>, - ) -> Result<(String, String), ConfigError> { - // (path_to_layout as String, stringified_layout) + ) -> Result<(String, String, Option<(String, String)>), ConfigError> { + // (path_to_layout as String, stringified_layout, Option) match layout_dir { Some(dir) => { let layout_path = &dir.join(layout); @@ -485,18 +660,28 @@ impl Layout { None => Layout::stringified_from_default_assets(layout), } } - pub fn stringified_from_path(layout_path: &Path) -> Result<(String, String), ConfigError> { - // (path_to_layout as String, stringified_layout) + pub fn stringified_from_path( + layout_path: &Path, + ) -> Result<(String, String, Option<(String, String)>), ConfigError> { + // (path_to_layout as String, stringified_layout, Option) let mut layout_file = File::open(&layout_path) .or_else(|_| File::open(&layout_path.with_extension("kdl"))) .map_err(|e| ConfigError::IoPath(e, layout_path.into()))?; + let swap_layout_and_path = Layout::swap_layout_and_path(&layout_path); + let mut kdl_layout = String::new(); layout_file.read_to_string(&mut kdl_layout)?; - Ok((layout_path.as_os_str().to_string_lossy().into(), kdl_layout)) + Ok(( + layout_path.as_os_str().to_string_lossy().into(), + kdl_layout, + swap_layout_and_path, + )) } - pub fn stringified_from_default_assets(path: &Path) -> Result<(String, String), ConfigError> { - // (path_to_layout as String, stringified_layout) + pub fn stringified_from_default_assets( + path: &Path, + ) -> Result<(String, String, Option<(String, String)>), ConfigError> { + // (path_to_layout as String, stringified_layout, Option) // TODO: ideally these should not be hard-coded // we should load layouts by name from the config // and load them from a hashmap or some such @@ -504,18 +689,31 @@ impl Layout { Some("default") => Ok(( "Default layout".into(), Self::stringified_default_from_assets()?, + Some(( + "Default swap layout".into(), + Self::stringified_default_swap_from_assets()?, + )), )), Some("strider") => Ok(( "Strider layout".into(), Self::stringified_strider_from_assets()?, + Some(( + "Strider swap layout".into(), + Self::stringified_strider_swap_from_assets()?, + )), )), Some("disable-status-bar") => Ok(( "Disable Status Bar layout".into(), Self::stringified_disable_status_from_assets()?, + None, )), Some("compact") => Ok(( "Compact layout".into(), Self::stringified_compact_from_assets()?, + Some(( + "Compact layout swap".into(), + Self::stringified_compact_swap_from_assets()?, + )), )), None | Some(_) => Err(ConfigError::IoPath( std::io::Error::new(std::io::ErrorKind::Other, "The layout was not found"), @@ -526,10 +724,15 @@ impl Layout { pub fn stringified_default_from_assets() -> Result { Ok(String::from_utf8(setup::DEFAULT_LAYOUT.to_vec())?) } - + pub fn stringified_default_swap_from_assets() -> Result { + Ok(String::from_utf8(setup::DEFAULT_SWAP_LAYOUT.to_vec())?) + } pub fn stringified_strider_from_assets() -> Result { Ok(String::from_utf8(setup::STRIDER_LAYOUT.to_vec())?) } + pub fn stringified_strider_swap_from_assets() -> Result { + Ok(String::from_utf8(setup::STRIDER_SWAP_LAYOUT.to_vec())?) + } pub fn stringified_disable_status_from_assets() -> Result { Ok(String::from_utf8(setup::NO_STATUS_LAYOUT.to_vec())?) @@ -539,12 +742,12 @@ impl Layout { Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?) } - pub fn new_tab(&self) -> (PaneLayout, Vec) { - let template = match &self.template { - Some(template) => template.clone(), - None => PaneLayout::default(), - }; - (template, self.floating_panes_template.clone()) + pub fn stringified_compact_swap_from_assets() -> Result { + Ok(String::from_utf8(setup::COMPACT_BAR_SWAP_LAYOUT.to_vec())?) + } + + pub fn new_tab(&self) -> (TiledPaneLayout, Vec) { + self.template.clone().unwrap_or_default() } pub fn is_empty(&self) -> bool { @@ -555,7 +758,7 @@ impl Layout { !self.tabs.is_empty() } - pub fn tabs(&self) -> Vec<(Option, PaneLayout, Vec)> { + pub fn tabs(&self) -> Vec<(Option, TiledPaneLayout, Vec)> { // String is the tab name self.tabs.clone() } @@ -563,16 +766,60 @@ impl Layout { pub fn focused_tab_index(&self) -> Option { self.focused_tab_index } + + fn swap_layout_and_path(path: &Path) -> Option<(String, String)> { + // Option + let mut swap_layout_path = PathBuf::from(path); + swap_layout_path.set_extension("swap.kdl"); + match File::open(&swap_layout_path) { + Ok(mut stringified_swap_layout_file) => { + let mut swap_kdl_layout = String::new(); + match stringified_swap_layout_file.read_to_string(&mut swap_kdl_layout) { + Ok(..) => Some(( + swap_layout_path.as_os_str().to_string_lossy().into(), + swap_kdl_layout, + )), + Err(e) => { + log::warn!( + "Failed to read swap layout file: {}. Error: {:?}", + swap_layout_path.as_os_str().to_string_lossy(), + e + ); + None + }, + } + }, + Err(e) => { + log::warn!( + "Failed to read swap layout file: {}. Error: {:?}", + swap_layout_path.as_os_str().to_string_lossy(), + e + ); + None + }, + } + } } fn split_space( space_to_split: &PaneGeom, - layout: &PaneLayout, + layout: &TiledPaneLayout, total_space_to_split: &PaneGeom, -) -> Vec<(PaneLayout, PaneGeom)> { +) -> Result, &'static str> { let mut pane_positions = Vec::new(); - let sizes: Vec> = - layout.children.iter().map(|part| part.split_size).collect(); + let sizes: Vec> = if layout.children_are_stacked { + let mut sizes: Vec> = layout + .children + .iter() + .map(|_part| Some(SplitSize::Fixed(1))) + .collect(); + if let Some(last_size) = sizes.last_mut() { + *last_size = None; + } + sizes + } else { + layout.children.iter().map(|part| part.split_size).collect() + }; let mut split_geom = Vec::new(); let ( @@ -595,7 +842,22 @@ fn split_space( ), }; + let min_size_for_panes = sizes.iter().fold(0, |acc, size| match size { + Some(SplitSize::Percent(_)) | None => acc + 1, // TODO: minimum height/width as relevant here + Some(SplitSize::Fixed(fixed)) => acc + fixed, + }); + if min_size_for_panes > split_dimension_space.as_usize() { + return Err("Not enough room for panes"); // TODO: use error infra + } + let flex_parts = sizes.iter().filter(|s| s.is_none()).count(); + let total_fixed_size = sizes.iter().fold(0, |acc, s| { + if let Some(SplitSize::Fixed(fixed)) = s { + acc + fixed + } else { + acc + } + }); let mut total_pane_size = 0; for (&size, _part) in sizes.iter().zip(&*layout.children) { @@ -617,7 +879,11 @@ fn split_space( Dimension::percent(free_percent / flex_parts as f64) }, }; - split_dimension.adjust_inner(total_split_dimension_space.as_usize()); + split_dimension.adjust_inner( + total_split_dimension_space + .as_usize() + .saturating_sub(total_fixed_size), + ); total_pane_size += split_dimension.as_usize(); let geom = match layout.children_split_direction { @@ -626,20 +892,22 @@ fn split_space( y: space_to_split.y, cols: split_dimension, rows: inherited_dimension, + is_stacked: layout.children_are_stacked, }, SplitDirection::Horizontal => PaneGeom { x: space_to_split.x, y: current_position, cols: inherited_dimension, rows: split_dimension, + is_stacked: layout.children_are_stacked, }, }; split_geom.push(geom); current_position += split_dimension.as_usize(); } - // add extra space from rounding errors to the last pane if total_pane_size < split_dimension_space.as_usize() { + // add extra space from rounding errors to the last pane let increase_by = split_dimension_space.as_usize() - total_pane_size; if let Some(last_geom) = split_geom.last_mut() { match layout.children_split_direction { @@ -647,21 +915,32 @@ fn split_space( SplitDirection::Horizontal => last_geom.rows.increase_inner(increase_by), } } + } else if total_pane_size > split_dimension_space.as_usize() { + // remove extra space from rounding errors to the last pane + let decrease_by = total_pane_size - split_dimension_space.as_usize(); + if let Some(last_geom) = split_geom.last_mut() { + match layout.children_split_direction { + SplitDirection::Vertical => last_geom.cols.decrease_inner(decrease_by), + SplitDirection::Horizontal => last_geom.rows.decrease_inner(decrease_by), + } + } } for (i, part) in layout.children.iter().enumerate() { let part_position_and_size = split_geom.get(i).unwrap(); if !part.children.is_empty() { let mut part_positions = - split_space(part_position_and_size, part, total_space_to_split); + split_space(part_position_and_size, part, total_space_to_split)?; pane_positions.append(&mut part_positions); } else { - pane_positions.push((part.clone(), *part_position_and_size)); + let part = part.clone(); + pane_positions.push((part, *part_position_and_size)); } } if pane_positions.is_empty() { - pane_positions.push((layout.clone(), space_to_split.clone())); + let layout = layout.clone(); + pane_positions.push((layout, space_to_split.clone())); } - pane_positions + Ok(pane_positions) } impl TryFrom for RunPluginLocation { diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index 93f15b2d..684aabc0 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -116,6 +116,11 @@ pub struct Options { #[clap(long, value_parser)] #[serde(default)] pub attach_to_session: Option, + + /// Whether to lay out panes in a predefined set of layouts whenever possible + #[clap(long, value_parser)] + #[serde(default)] + pub auto_layout: Option, } #[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] @@ -157,6 +162,7 @@ impl Options { pub fn merge(&self, other: Options) -> Options { let mouse_mode = other.mouse_mode.or(self.mouse_mode); let pane_frames = other.pane_frames.or(self.pane_frames); + let auto_layout = other.auto_layout.or(self.auto_layout); let mirror_session = other.mirror_session.or(self.mirror_session); let simplified_ui = other.simplified_ui.or(self.simplified_ui); let default_mode = other.default_mode.or(self.default_mode); @@ -197,6 +203,7 @@ impl Options { scrollback_editor, session_name, attach_to_session, + auto_layout, } } @@ -218,6 +225,7 @@ impl Options { let simplified_ui = merge_bool(other.simplified_ui, self.simplified_ui); let mouse_mode = merge_bool(other.mouse_mode, self.mouse_mode); let pane_frames = merge_bool(other.pane_frames, self.pane_frames); + let auto_layout = merge_bool(other.auto_layout, self.auto_layout); let mirror_session = merge_bool(other.mirror_session, self.mirror_session); let default_mode = other.default_mode.or(self.default_mode); @@ -258,6 +266,7 @@ impl Options { scrollback_editor, session_name, attach_to_session, + auto_layout, } } @@ -288,6 +297,7 @@ impl From for Options { fn from(cli_options: CliOptions) -> Self { let mut opts = cli_options.options; + // TODO: what? if cli_options.no_pane_frames { opts.pane_frames = Some(false); } diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index d588fcda..f15fd709 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -4,9 +4,9 @@ use insta::assert_snapshot; #[test] fn empty_layout() { let kdl_layout = "layout"; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout::default()), + template: Some((TiledPaneLayout::default(), vec![])), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -19,12 +19,15 @@ fn layout_with_one_pane() { pane } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout::default()], - ..Default::default() - }), + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout::default()], + ..Default::default() + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -39,16 +42,19 @@ fn layout_with_multiple_panes() { pane } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), - ], - ..Default::default() - }), + template: Some(( + TiledPaneLayout { + children: vec![ + TiledPaneLayout::default(), + TiledPaneLayout::default(), + TiledPaneLayout::default(), + ], + ..Default::default() + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -68,22 +74,25 @@ fn layout_with_nested_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![ - PaneLayout { - children_split_direction: SplitDirection::Vertical, - children: vec![PaneLayout::default(), PaneLayout::default()], - ..Default::default() - }, - PaneLayout { - children: vec![PaneLayout::default(), PaneLayout::default()], - ..Default::default() - }, - ], - ..Default::default() - }), + template: Some(( + TiledPaneLayout { + children: vec![ + TiledPaneLayout { + children_split_direction: SplitDirection::Vertical, + children: vec![TiledPaneLayout::default(), TiledPaneLayout::default()], + ..Default::default() + }, + TiledPaneLayout { + children: vec![TiledPaneLayout::default(), TiledPaneLayout::default()], + ..Default::default() + }, + ], + ..Default::default() + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -106,32 +115,34 @@ fn layout_with_floating_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout::default()), - floating_panes_template: vec![ - FloatingPanesLayout::default(), - FloatingPanesLayout { - x: Some(PercentOrFixed::Fixed(10)), - y: Some(PercentOrFixed::Percent(10)), - width: Some(PercentOrFixed::Fixed(10)), - height: Some(PercentOrFixed::Percent(10)), - ..Default::default() - }, - FloatingPanesLayout { - x: Some(PercentOrFixed::Fixed(10)), - y: Some(PercentOrFixed::Percent(10)), - ..Default::default() - }, - FloatingPanesLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - hold_on_close: true, + template: Some(( + TiledPaneLayout::default(), + vec![ + FloatingPaneLayout::default(), + FloatingPaneLayout { + x: Some(PercentOrFixed::Fixed(10)), + y: Some(PercentOrFixed::Percent(10)), + width: Some(PercentOrFixed::Fixed(10)), + height: Some(PercentOrFixed::Percent(10)), ..Default::default() - })), - ..Default::default() - }, - ], + }, + FloatingPaneLayout { + x: Some(PercentOrFixed::Fixed(10)), + y: Some(PercentOrFixed::Percent(10)), + ..Default::default() + }, + FloatingPaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + hold_on_close: true, + ..Default::default() + })), + ..Default::default() + }, + ], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -148,13 +159,15 @@ fn layout_with_mixed_panes_and_floating_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout::default(), PaneLayout::default()], - ..Default::default() - }), - floating_panes_template: vec![FloatingPanesLayout::default()], + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout::default(), TiledPaneLayout::default()], + ..Default::default() + }, + vec![FloatingPaneLayout::default()], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -175,20 +188,22 @@ fn layout_with_floating_panes_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout::default()], - ..Default::default() - }), - floating_panes_template: vec![ - FloatingPanesLayout::default(), - FloatingPanesLayout { - x: Some(PercentOrFixed::Fixed(10)), - y: Some(PercentOrFixed::Percent(10)), + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout::default()], ..Default::default() }, - ], + vec![ + FloatingPaneLayout::default(), + FloatingPaneLayout { + x: Some(PercentOrFixed::Fixed(10)), + y: Some(PercentOrFixed::Percent(10)), + ..Default::default() + }, + ], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -208,30 +223,32 @@ fn layout_with_shared_tiled_and_floating_panes_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - hold_on_close: true, + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + hold_on_close: true, + ..Default::default() + })), ..Default::default() - })), - ..Default::default() - }], - ..Default::default() - }), - floating_panes_template: vec![ - FloatingPanesLayout::default(), - FloatingPanesLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - hold_on_close: true, - ..Default::default() - })), + }], ..Default::default() }, - ], + vec![ + FloatingPaneLayout::default(), + FloatingPaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + hold_on_close: true, + ..Default::default() + })), + ..Default::default() + }, + ], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -254,7 +271,7 @@ fn layout_with_tabs_and_floating_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -265,10 +282,10 @@ fn layout_with_tabs() { tab } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - tabs: vec![(None, PaneLayout::default(), vec![])], - template: Some(PaneLayout::default()), + tabs: vec![(None, TiledPaneLayout::default(), vec![])], + template: Some((TiledPaneLayout::default(), vec![])), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -289,17 +306,17 @@ fn layout_with_nested_differing_tabs() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { tabs: vec![ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: SplitDirection::Vertical, children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), + TiledPaneLayout::default(), + TiledPaneLayout::default(), + TiledPaneLayout::default(), ], ..Default::default() }, @@ -307,15 +324,15 @@ fn layout_with_nested_differing_tabs() { ), ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: SplitDirection::Horizontal, - children: vec![PaneLayout::default(), PaneLayout::default()], + children: vec![TiledPaneLayout::default(), TiledPaneLayout::default()], ..Default::default() }, vec![], // floating panes ), ], - template: Some(PaneLayout::default()), + template: Some((TiledPaneLayout::default(), vec![])), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -331,29 +348,32 @@ fn layout_with_panes_in_different_mixed_split_sizes() { pane size=2; } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![ - PaneLayout { - split_size: Some(SplitSize::Fixed(1)), - ..Default::default() - }, - PaneLayout { - split_size: Some(SplitSize::Percent(10)), - ..Default::default() - }, - PaneLayout { - split_size: None, - ..Default::default() - }, - PaneLayout { - split_size: Some(SplitSize::Fixed(2)), - ..Default::default() - }, - ], - ..Default::default() - }), + template: Some(( + TiledPaneLayout { + children: vec![ + TiledPaneLayout { + split_size: Some(SplitSize::Fixed(1)), + ..Default::default() + }, + TiledPaneLayout { + split_size: Some(SplitSize::Percent(10)), + ..Default::default() + }, + TiledPaneLayout { + split_size: None, + ..Default::default() + }, + TiledPaneLayout { + split_size: Some(SplitSize::Fixed(2)), + ..Default::default() + }, + ], + ..Default::default() + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -366,19 +386,22 @@ fn layout_with_command_panes() { pane command="htop" } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - hold_on_close: true, + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + hold_on_close: true, + ..Default::default() + })), ..Default::default() - })), + }], ..Default::default() - }], - ..Default::default() - }), + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -391,20 +414,23 @@ fn layout_with_command_panes_and_cwd() { pane command="htop" cwd="/path/to/my/cwd" } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - cwd: Some(PathBuf::from("/path/to/my/cwd")), - hold_on_close: true, + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + hold_on_close: true, + ..Default::default() + })), ..Default::default() - })), + }], ..Default::default() - }], - ..Default::default() - }), + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -419,21 +445,24 @@ fn layout_with_command_panes_and_cwd_and_args() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - run: Some(Run::Command(RunCommand { - command: PathBuf::from("htop"), - cwd: Some(PathBuf::from("/path/to/my/cwd")), - args: vec![String::from("-h"), String::from("-v")], - hold_on_close: true, + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + cwd: Some(PathBuf::from("/path/to/my/cwd")), + args: vec![String::from("-h"), String::from("-v")], + hold_on_close: true, + ..Default::default() + })), ..Default::default() - })), + }], ..Default::default() - }], - ..Default::default() - }), + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -448,7 +477,7 @@ fn layout_with_command_panes_and_close_on_exit() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -461,7 +490,7 @@ fn layout_with_command_panes_and_start_suspended() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -477,27 +506,32 @@ fn layout_with_plugin_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![ - PaneLayout { - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), - _allow_exec_host_cmd: false, - })), - ..Default::default() - }, - PaneLayout { - run: Some(Run::Plugin(RunPlugin { - location: RunPluginLocation::File(PathBuf::from("/path/to/my/plugin.wasm")), - _allow_exec_host_cmd: false, - })), - ..Default::default() - }, - ], - ..Default::default() - }), + template: Some(( + TiledPaneLayout { + children: vec![ + TiledPaneLayout { + run: Some(Run::Plugin(RunPlugin { + location: RunPluginLocation::Zellij(PluginTag::new("tab-bar")), + _allow_exec_host_cmd: false, + })), + ..Default::default() + }, + TiledPaneLayout { + run: Some(Run::Plugin(RunPlugin { + location: RunPluginLocation::File(PathBuf::from( + "/path/to/my/plugin.wasm", + )), + _allow_exec_host_cmd: false, + })), + ..Default::default() + }, + ], + ..Default::default() + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -510,15 +544,18 @@ fn layout_with_borderless_panes() { pane borderless=true } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - borderless: true, + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + borderless: true, + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }), + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -531,15 +568,18 @@ fn layout_with_focused_panes() { pane focus=true } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - focus: Some(true), + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + focus: Some(true), + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }), + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -552,15 +592,18 @@ fn layout_with_pane_names() { pane name="my awesome pane" } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { - template: Some(PaneLayout { - children: vec![PaneLayout { - name: Some("my awesome pane".into()), + template: Some(( + TiledPaneLayout { + children: vec![TiledPaneLayout { + name: Some("my awesome pane".into()), + ..Default::default() + }], ..Default::default() - }], - ..Default::default() - }), + }, + vec![], + )), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -574,12 +617,12 @@ fn layout_with_tab_names() { tab name="my cool tab name 2" } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { tabs: vec![ ( Some("my cool tab name 1".into()), - PaneLayout { + TiledPaneLayout { children: vec![], ..Default::default() }, @@ -587,14 +630,14 @@ fn layout_with_tab_names() { ), ( Some("my cool tab name 2".into()), - PaneLayout { + TiledPaneLayout { children: vec![], ..Default::default() }, vec![], // floating panes ), ], - template: Some(PaneLayout::default()), + template: Some((TiledPaneLayout::default(), vec![])), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -609,14 +652,14 @@ fn layout_with_focused_tab() { tab } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { tabs: vec![ - (None, PaneLayout::default(), vec![]), - (None, PaneLayout::default(), vec![]), - (None, PaneLayout::default(), vec![]), + (None, TiledPaneLayout::default(), vec![]), + (None, TiledPaneLayout::default(), vec![]), + (None, TiledPaneLayout::default(), vec![]), ], - template: Some(PaneLayout::default()), + template: Some((TiledPaneLayout::default(), vec![])), focused_tab_index: Some(1), ..Default::default() }; @@ -643,21 +686,21 @@ fn layout_with_tab_templates() { one-above-one-below } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); let expected_layout = Layout { tabs: vec![ ( Some("my first tab".into()), - PaneLayout { + TiledPaneLayout { children_split_direction: SplitDirection::Horizontal, children: vec![ - PaneLayout::default(), - PaneLayout { + TiledPaneLayout::default(), + TiledPaneLayout { children_split_direction: SplitDirection::Vertical, - children: vec![PaneLayout::default(), PaneLayout::default()], + children: vec![TiledPaneLayout::default(), TiledPaneLayout::default()], ..Default::default() }, - PaneLayout::default(), + TiledPaneLayout::default(), ], ..Default::default() }, @@ -665,16 +708,16 @@ fn layout_with_tab_templates() { ), ( Some("my second tab".into()), - PaneLayout { + TiledPaneLayout { children_split_direction: SplitDirection::Horizontal, children: vec![ - PaneLayout::default(), - PaneLayout { + TiledPaneLayout::default(), + TiledPaneLayout { children_split_direction: SplitDirection::Horizontal, - children: vec![PaneLayout::default(), PaneLayout::default()], + children: vec![TiledPaneLayout::default(), TiledPaneLayout::default()], ..Default::default() }, - PaneLayout::default(), + TiledPaneLayout::default(), ], ..Default::default() }, @@ -682,19 +725,19 @@ fn layout_with_tab_templates() { ), ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: SplitDirection::Horizontal, children: vec![ - PaneLayout::default(), - PaneLayout::default(), - PaneLayout::default(), + TiledPaneLayout::default(), + TiledPaneLayout::default(), + TiledPaneLayout::default(), ], ..Default::default() }, vec![], // floating panes ), ], - template: Some(PaneLayout::default()), + template: Some((TiledPaneLayout::default(), vec![])), ..Default::default() }; assert_eq!(layout, expected_layout); @@ -720,7 +763,7 @@ fn layout_with_default_tab_template() { tab } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -747,7 +790,7 @@ fn layout_with_pane_templates() { left-and-right } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -768,7 +811,7 @@ fn layout_with_tab_and_pane_templates() { left-right-and-htop } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -789,7 +832,7 @@ fn layout_with_nested_pane_templates() { left-and-right } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -815,7 +858,7 @@ fn layout_with_nested_branched_pane_templates() { left-and-right } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -839,7 +882,7 @@ fn circular_dependency_pane_templates_error() { one } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "circular dependency detected"); } @@ -861,7 +904,7 @@ fn children_not_as_first_child_of_tab_template() { horizontal-with-vertical-top } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -884,7 +927,7 @@ fn error_on_more_than_one_children_block_in_tab_template() { horizontal-with-vertical-top } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!( layout.is_err(), "error provided for more than one children block" @@ -909,7 +952,7 @@ fn children_not_as_first_child_of_pane_template() { horizontal-with-vertical-top } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -932,7 +975,7 @@ fn error_on_more_than_one_children_block_in_pane_template() { horizontal-with-vertical-top } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!( layout.is_err(), "error provided for more than one children block" @@ -961,7 +1004,7 @@ fn combined_tab_and_pane_template_both_with_children() { horizontal-with-vertical-top } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -977,7 +1020,7 @@ fn cannot_define_tab_template_name_with_space() { pane } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided for tab name with space"); } @@ -993,7 +1036,7 @@ fn cannot_define_pane_template_name_with_space() { pane } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided for tab name with space"); } @@ -1007,7 +1050,7 @@ fn cannot_define_panes_and_tabs_on_same_level() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!( layout.is_err(), "error provided for tab and pane on the same level" @@ -1041,7 +1084,7 @@ fn cannot_define_tab_template_names_as_keywords() { ", keyword ); - let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None); assert!( layout.is_err(), "{}", @@ -1079,7 +1122,7 @@ fn cannot_define_pane_template_names_as_keywords() { ", keyword ); - let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None); assert!( layout.is_err(), "{}", @@ -1099,7 +1142,8 @@ fn error_on_multiple_layout_nodes_in_file() { layout " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1114,7 +1158,8 @@ fn error_on_unknown_layout_node() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1127,7 +1172,8 @@ fn error_on_unknown_layout_pane_property() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1140,7 +1186,8 @@ fn error_on_unknown_layout_pane_template_property() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1153,7 +1200,8 @@ fn error_on_unknown_layout_tab_property() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1166,7 +1214,8 @@ fn error_on_unknown_layout_tab_template_property() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1183,7 +1232,8 @@ fn error_on_pane_templates_without_a_name() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1200,7 +1250,8 @@ fn error_on_tab_templates_without_a_name() { }} " ); - let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1213,7 +1264,8 @@ fn error_on_more_than_one_focused_tab() { tab } "#; - let layout_error = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap_err(); + let layout_error = + Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap_err(); assert_snapshot!(format!("{:?}", layout_error)); } @@ -1231,7 +1283,7 @@ fn args_override_args_in_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1249,7 +1301,7 @@ fn close_on_exit_overrides_close_on_exit_in_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1266,7 +1318,7 @@ fn args_added_to_args_in_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1283,7 +1335,7 @@ fn close_on_exit_added_to_close_on_exit_in_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1301,7 +1353,7 @@ fn cwd_override_cwd_in_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1318,7 +1370,7 @@ fn cwd_added_to_cwd_in_template() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1331,7 +1383,7 @@ fn error_on_mixed_command_and_child_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided"); } @@ -1345,7 +1397,7 @@ fn error_on_mixed_cwd_and_child_panes() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided"); } @@ -1358,7 +1410,7 @@ fn error_on_bare_args_without_command() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided"); } @@ -1371,7 +1423,7 @@ fn error_on_bare_close_on_exit_without_command() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided"); } @@ -1385,7 +1437,7 @@ fn error_on_bare_args_in_template_without_command() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided"); } @@ -1399,7 +1451,7 @@ fn error_on_bare_close_on_exit_in_template_without_command() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); assert!(layout.is_err(), "error provided"); } @@ -1418,7 +1470,7 @@ fn pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd() { // pane should have /tmp/foo and not /tmp/bar as cwd } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1435,7 +1487,7 @@ fn pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_hav // pane should have /tmp/bar as its cwd with the pwd command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1453,7 +1505,7 @@ fn pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd() { // pane should have /tmp/bar as its cwd with the pwd command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1472,7 +1524,7 @@ fn pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd() { // pane should have /tmp/bar as its cwd with the tail command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1490,7 +1542,7 @@ fn pane_template_command_without_cwd_receives_its_consumers_bare_cwd() { // pane should have /tmp/bar as its cwd with the tail command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1508,7 +1560,7 @@ fn pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd() { // pane should have /tmp/foo without a command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1524,7 +1576,7 @@ fn pane_template_with_bare_propagated_to_its_consumer_command_without_cwd() { // pane should have /tmp/foo with the tail command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1542,7 +1594,7 @@ fn pane_template_with_bare_propagated_to_its_consumer_command_with_cwd() { // pane should have /tmp/bar with the tail command } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1558,7 +1610,7 @@ fn pane_template_with_bare_propagated_to_its_consumer_edit() { // pane should have /tmp/foo/bar with the edit file variant } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1574,7 +1626,7 @@ fn pane_template_with_command_propagated_to_its_consumer_edit() { // pane should have /tmp/foo/bar with the edit file variant } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1588,7 +1640,7 @@ fn global_cwd_given_to_panes_without_cwd() { // both should have the /tmp cwd } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1601,7 +1653,7 @@ fn global_cwd_prepended_to_panes_with_cwd() { pane command="tail" cwd="/home/foo" // should be /home/foo because its an absolute path } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1618,6 +1670,7 @@ fn global_cwd_passed_from_layout_constructor() { let layout = Layout::from_kdl( kdl_layout, "layout_file_name".into(), + None, Some(PathBuf::from("/tmp")), ) .unwrap(); @@ -1638,6 +1691,7 @@ fn global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file let layout = Layout::from_kdl( kdl_layout, "layout_file_name".into(), + None, Some(PathBuf::from("/tmp")), ) .unwrap(); @@ -1656,7 +1710,7 @@ fn global_cwd_with_tab_cwd_given_to_panes_without_cwd() { // both should have the /tmp/foo cwd } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1671,7 +1725,7 @@ fn tab_cwd_given_to_panes_without_cwd() { // both should have the /tmp cwd } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1686,7 +1740,7 @@ fn tab_cwd_prepended_to_panes_with_cwd() { // both should have the /tmp/foo cwd } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1701,7 +1755,7 @@ fn global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd() { } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1722,7 +1776,7 @@ fn global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templa } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); assert_snapshot!(format!("{:#?}", layout)); } @@ -1741,6 +1795,57 @@ fn global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templat } } "#; - let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn can_load_swap_layouts_from_a_different_file() { + let kdl_layout = r#" + layout { + // here we define a tab_template in the main layout and later make sure we can sue it + // in the swap layouts + tab_template name="ui" { + pane size=1 borderless=true { + plugin location="zellij:tab-bar" + } + children + pane size=2 borderless=true { + plugin location="zellij:status-bar" + } + } + pane + } + "#; + let kdl_swap_layout = r#" + swap_tiled_layout name="vertical" { + ui max_panes=5 { + pane split_direction="vertical" { + pane + pane { children; } + } + } + ui max_panes=8 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + } + } + ui max_panes=12 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + pane { pane; pane; pane; pane; } + } + } + } + "#; + let layout = Layout::from_kdl( + kdl_layout, + "layout_file_name".into(), + Some(("swap_layout_file_name".into(), kdl_swap_layout)), + None, + ) + .unwrap(); assert_snapshot!(format!("{:#?}", layout)); } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap index 0fc51aad..79990832 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap @@ -1,66 +1,74 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1101 +assertion_line: 1266 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [ - "-f", - "/tmp/bar", - ], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [ + "-f", + "/tmp/bar", + ], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap index 64d47573..bb691fe7 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap @@ -1,69 +1,77 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1066 +assertion_line: 1231 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [ - "-f", - "/tmp/foo", - ], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [ + "-f", + "/tmp/foo", + ], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [ - "-f", - "/tmp/bar", - ], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [ + "-f", + "/tmp/bar", + ], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap new file mode 100644 index 00000000..be292b58 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap @@ -0,0 +1,536 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1788 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [], + focused_tab_index: None, + template: Some( + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), + ), + swap_layouts: [], + swap_tiled_layouts: [ + ( + { + MaxPanes( + 5, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + MaxPanes( + 8, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + MaxPanes( + 12, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + }, + Some( + "vertical", + ), + ), + ], + swap_floating_layouts: [], +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap index 36eeb717..304f0169 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap @@ -1,142 +1,159 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 744 +assertion_line: 909 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: Some( - "my pane", - ), - children: [ - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: Some( - 1, - ), - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: Some( + "my pane", + ), + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 1, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap index ac027457..ded8fd1f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 696 +assertion_line: 861 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -9,15 +9,15 @@ Layout { Some( "my tab", ), - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -26,12 +26,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -40,8 +41,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -50,6 +52,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -57,6 +60,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -64,8 +68,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -74,6 +79,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -81,20 +87,21 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -103,6 +110,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -112,8 +120,9 @@ Layout { external_children_index: Some( 1, ), + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -122,6 +131,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -129,22 +139,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap index 0386fd19..b7ed1fe8 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap @@ -1,63 +1,71 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1118 +assertion_line: 1283 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: None, - hold_on_close: false, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: None, + hold_on_close: false, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap index 487bfb82..b0c35796 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap @@ -1,63 +1,71 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1084 +assertion_line: 1249 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: None, - hold_on_close: false, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: None, + hold_on_close: false, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap index 0c4cb680..ad335416 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 796 +assertion_line: 961 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -9,15 +9,15 @@ Layout { Some( "my tab", ), - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -26,12 +26,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: Some( "middle", @@ -42,6 +43,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -49,8 +51,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -59,6 +62,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -66,12 +70,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -80,8 +85,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -90,6 +96,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -97,6 +104,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -104,20 +112,21 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -126,12 +135,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: Some( "middle", @@ -142,6 +152,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -149,8 +160,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -159,6 +171,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -166,8 +179,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -176,6 +190,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -183,22 +198,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap index beb1f67f..1940a648 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap @@ -1,65 +1,73 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1153 +assertion_line: 1318 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: None, - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: None, + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/home", - ), - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/home", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap index 36cc0c24..f89eb14e 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap @@ -1,67 +1,75 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1136 +assertion_line: 1301 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/", - ), - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap index 71b46db4..0c0dc06d 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap @@ -1,17 +1,17 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1536 +assertion_line: 1701 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -24,8 +24,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -46,6 +47,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -57,22 +59,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap index 5c39bbd1..7859311c 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap @@ -1,21 +1,21 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1557 +assertion_line: 1722 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -28,8 +28,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -50,12 +51,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -68,6 +70,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -79,6 +82,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -90,6 +94,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -101,22 +106,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap index 4c6bfd2d..76d08d57 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap @@ -1,17 +1,17 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1576 +assertion_line: 1741 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -24,8 +24,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -46,12 +47,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -64,6 +66,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -75,6 +78,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -86,22 +90,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap index 5256565c..c9b824bd 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap @@ -1,59 +1,67 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1423 +assertion_line: 1588 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Cwd( - "/tmp", + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp", + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp", - ), - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap index b0eaf353..328b9bc9 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap @@ -1,59 +1,67 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1455 +assertion_line: 1620 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Cwd( - "/tmp", + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp", + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp", - ), - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap index 5aa11997..d16986e1 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap @@ -1,59 +1,67 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1475 +assertion_line: 1640 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Cwd( - "/tmp", + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp", + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp", - ), - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap index 8c5d71c1..3b445963 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap @@ -1,59 +1,67 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1436 +assertion_line: 1601 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Cwd( - "/tmp/foo", + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/foo", + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/home/foo", - ), - hold_on_close: true, - hold_on_start: false, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/home/foo", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap index b54634fb..58602c68 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap @@ -1,17 +1,17 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1491 +assertion_line: 1656 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -24,8 +24,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -46,6 +47,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -57,22 +59,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap index c16d495e..83f17c11 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap @@ -1,43 +1,50 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 283 +assertion_line: 448 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "htop", - args: [], - cwd: None, - hold_on_close: false, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "htop", + args: [], + cwd: None, + hold_on_close: false, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap index 6723bc21..8b3afcdd 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap @@ -1,43 +1,50 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 296 +assertion_line: 461 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "htop", - args: [], - cwd: None, - hold_on_close: true, - hold_on_start: true, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "htop", + args: [], + cwd: None, + hold_on_close: true, + hold_on_start: true, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap index 8065fc4d..daf284ff 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 555 +assertion_line: 720 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -9,11 +9,11 @@ Layout { Some( "my first tab", ), - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -22,12 +22,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -36,8 +37,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -46,6 +48,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -53,8 +56,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -63,6 +67,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -70,6 +75,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), @@ -77,11 +83,11 @@ Layout { Some( "my second tab", ), - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -90,12 +96,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -104,8 +111,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -114,6 +122,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -121,8 +130,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -131,6 +141,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -138,16 +149,17 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -156,8 +168,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -166,8 +179,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -176,6 +190,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -183,53 +198,63 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap index 687d7618..cb166970 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap @@ -1,126 +1,142 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 650 +assertion_line: 815 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap index e5a655f7..3fab5306 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap @@ -1,95 +1,108 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 624 +assertion_line: 789 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap index cbbddf74..f52d2c97 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap @@ -1,240 +1,267 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 582 +assertion_line: 747 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Vertical, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap index 464c1647..9126477e 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap @@ -1,21 +1,21 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 603 +assertion_line: 768 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Vertical, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -24,12 +24,13 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -48,6 +49,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -55,8 +57,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -65,6 +68,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -72,6 +76,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -79,22 +84,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap index 5cdf517a..61286160 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap @@ -1,17 +1,17 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 263 +assertion_line: 254 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -20,6 +20,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -27,9 +28,10 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [ - FloatingPanesLayout { + FloatingPaneLayout { name: None, height: None, width: None, @@ -42,11 +44,11 @@ Layout { ), ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -55,6 +57,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -62,9 +65,10 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [ - FloatingPanesLayout { + FloatingPaneLayout { name: None, height: None, width: None, @@ -73,7 +77,7 @@ Layout { run: None, focus: None, }, - FloatingPanesLayout { + FloatingPaneLayout { name: None, height: None, width: None, @@ -87,16 +91,22 @@ Layout { ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap index 4f4552ee..a3a4965c 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1307 +assertion_line: 1472 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp/bar", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/bar", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap index fc5523fa..a0d1a943 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1253 +assertion_line: 1418 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "pwd", - args: [], - cwd: Some( - "/tmp/foo", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "pwd", + args: [], + cwd: Some( + "/tmp/foo", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap index 3c00d46f..81f0a09f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1270 +assertion_line: 1435 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "pwd", - args: [], - cwd: Some( - "/tmp/bar", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "pwd", + args: [], + cwd: Some( + "/tmp/bar", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap index b88f2a9e..ddcd1225 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1288 +assertion_line: 1453 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "pwd", - args: [], - cwd: Some( - "/tmp/bar", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "pwd", + args: [], + cwd: Some( + "/tmp/bar", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap index 3d561463..74318d3a 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1325 +assertion_line: 1490 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp/bar", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/bar", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap index 89332ea3..13492fea 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap @@ -1,37 +1,44 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1343 +assertion_line: 1508 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Cwd( - "/tmp/bar", + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/bar", + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap index 3797afc5..bb193e7c 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1377 +assertion_line: 1542 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp/bar", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/bar", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap index 072d0bc5..d28a7be1 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap @@ -1,45 +1,52 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1359 +assertion_line: 1524 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - Command( - RunCommand { - command: "tail", - args: [], - cwd: Some( - "/tmp/foo", - ), - hold_on_close: true, - hold_on_start: false, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/foo", + ), + hold_on_close: true, + hold_on_start: false, + }, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap index a6aac1f1..34d0fb32 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap @@ -1,38 +1,45 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1393 +assertion_line: 1558 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - EditFile( - "/tmp/foo/bar", - None, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + EditFile( + "/tmp/foo/bar", + None, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap index e77e7b95..298be4e0 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap @@ -1,38 +1,45 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1409 +assertion_line: 1574 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: Some( - EditFile( - "/tmp/foo/bar", - None, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + EditFile( + "/tmp/foo/bar", + None, + ), ), - ), - borderless: false, - focus: None, - external_children_index: None, - }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap index 004238aa..718b0edb 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap @@ -1,17 +1,17 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1506 +assertion_line: 1671 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -24,8 +24,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -46,6 +47,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -57,22 +59,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap index 573e0465..1a205760 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap @@ -1,17 +1,17 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1521 +assertion_line: 1686 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [ ( None, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [ - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -24,8 +24,9 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + TiledPaneLayout { children_split_direction: Horizontal, name: None, children: [], @@ -46,6 +47,7 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, ], split_size: None, @@ -57,22 +59,29 @@ Layout { borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, [], ), ], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index fad5fafe..7125d1fe 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -2,14 +2,15 @@ use crate::input::{ command::RunCommand, config::ConfigError, layout::{ - FloatingPanesLayout, Layout, PaneLayout, PercentOrFixed, Run, RunPlugin, RunPluginLocation, - SplitDirection, SplitSize, + FloatingPaneLayout, Layout, LayoutConstraint, PercentOrFixed, Run, RunPlugin, + RunPluginLocation, SplitDirection, SplitSize, SwapFloatingLayout, SwapTiledLayout, + TiledPaneLayout, }, }; use kdl::*; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::str::FromStr; use crate::{ @@ -28,17 +29,17 @@ use url::Url; #[derive(Debug, Clone, PartialEq, Eq)] pub enum PaneOrFloatingPane { - Pane(PaneLayout), - FloatingPane(FloatingPanesLayout), - Either(PaneLayout), + Pane(TiledPaneLayout), + FloatingPane(FloatingPaneLayout), + Either(TiledPaneLayout), } pub struct KdlLayoutParser<'a> { global_cwd: Option, raw_layout: &'a str, - tab_templates: HashMap, KdlNode)>, + tab_templates: HashMap, KdlNode)>, pane_templates: HashMap, - default_tab_template: Option<(PaneLayout, Vec, KdlNode)>, + default_tab_template: Option<(TiledPaneLayout, Vec, KdlNode)>, } impl<'a> KdlLayoutParser<'a> { @@ -71,6 +72,8 @@ impl<'a> KdlLayoutParser<'a> { || word == "size" || word == "cwd" || word == "split_direction" + || word == "swap_tiled_layout" + || word == "swap_floating_layout" } fn is_a_valid_pane_property(&self, property_name: &str) -> bool { property_name == "borderless" @@ -110,6 +113,9 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "split_direction" || property_name == "cwd" || property_name == "floating_panes" + || property_name == "children" + || property_name == "max_panes" + || property_name == "min_panes" } fn assert_legal_node_name(&self, name: &str, kdl_node: &KdlNode) -> Result<(), ConfigError> { if name.contains(char::is_whitespace) { @@ -402,7 +408,7 @@ impl<'a> KdlLayoutParser<'a> { } Ok(run) } - fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { + fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { self.assert_valid_pane_properties(kdl_node)?; let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); @@ -411,12 +417,13 @@ impl<'a> KdlLayoutParser<'a> { let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_plugin_or_edit_block(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, children) = match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, vec![]), - }; + let (external_children_index, children_are_stacked, children) = + match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, + None => (None, false, vec![]), + }; self.assert_no_mixed_children_and_properties(kdl_node)?; - Ok(PaneLayout { + Ok(TiledPaneLayout { borderless: borderless.unwrap_or_default(), focus, name, @@ -425,13 +432,14 @@ impl<'a> KdlLayoutParser<'a> { children_split_direction, external_children_index, children, + children_are_stacked, ..Default::default() }) } fn parse_floating_pane_node( &self, kdl_node: &KdlNode, - ) -> Result { + ) -> Result { self.assert_valid_floating_pane_properties(kdl_node)?; let height = self.parse_percent_or_fixed(kdl_node, "height", false)?; let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; @@ -442,7 +450,7 @@ impl<'a> KdlLayoutParser<'a> { let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") .map(|name| name.to_string()); self.assert_no_mixed_children_and_properties(kdl_node)?; - Ok(FloatingPanesLayout { + Ok(FloatingPaneLayout { name, height, width, @@ -456,19 +464,21 @@ impl<'a> KdlLayoutParser<'a> { fn insert_children_to_pane_template( &self, kdl_node: &KdlNode, - pane_template: &mut PaneLayout, + pane_template: &mut TiledPaneLayout, pane_template_kdl_node: &KdlNode, ) -> Result<(), ConfigError> { let children_split_direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, vec![]), - }; + let (external_children_index, children_are_stacked, pane_parts) = + match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, + None => (None, false, vec![]), + }; if pane_parts.len() > 0 { - let child_panes_layout = PaneLayout { + let child_panes_layout = TiledPaneLayout { children_split_direction, children: pane_parts, external_children_index, + children_are_stacked, ..Default::default() }; self.assert_one_children_block(&pane_template, pane_template_kdl_node)?; @@ -480,12 +490,47 @@ impl<'a> KdlLayoutParser<'a> { } Ok(()) } + fn populate_external_children_index( + &self, + kdl_node: &KdlNode, + ) -> Result, ConfigError> { + // Option<(external_children_index, is_stacked)> + if let Some(pane_child_nodes) = kdl_children_nodes!(kdl_node) { + for (i, child) in pane_child_nodes.iter().enumerate() { + if kdl_name!(child) == "children" { + let stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked") + .unwrap_or(false); + if let Some(grand_children) = kdl_children_nodes!(child) { + let grand_children: Vec<&str> = grand_children + .iter() + .map(|g| kdl_name!(g)) + .filter(|g| g != &"stacked") + .collect(); + if !grand_children.is_empty() { + return Err(ConfigError::new_layout_kdl_error( + format!( + "Invalid `children` properties: {}", + grand_children.join(", ") + ), + child.span().offset(), + child.span().len(), + )); + } + } + return Ok(Some((i, stacked))); + } + } + } + return Ok(None); + } fn parse_pane_node_with_template( &self, kdl_node: &KdlNode, pane_template: PaneOrFloatingPane, + should_mark_external_children_index: bool, pane_template_kdl_node: &KdlNode, - ) -> Result { + ) -> Result { match pane_template { PaneOrFloatingPane::Pane(mut pane_template) | PaneOrFloatingPane::Either(mut pane_template) => { @@ -501,6 +546,15 @@ impl<'a> KdlLayoutParser<'a> { kdl_get_bool_property_or_child_value_with_error!(kdl_node, "start_suspended"); let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?; + + // TODO: change should_insert_children to should_keep_pane_external_children_index + // or smth + let external_children_index_and_is_stacked = if should_mark_external_children_index + { + self.populate_external_children_index(kdl_node)? + } else { + None + }; self.assert_no_bare_attributes_in_pane_node_with_template( &run, &pane_template.run, @@ -537,9 +591,13 @@ impl<'a> KdlLayoutParser<'a> { if let Some(index_of_children) = pane_template.external_children_index { pane_template .children - .insert(index_of_children, PaneLayout::default()); + .insert(index_of_children, TiledPaneLayout::default()); } - pane_template.external_children_index = None; + pane_template.external_children_index = + external_children_index_and_is_stacked.map(|(index, _is_stacked)| index); + pane_template.children_are_stacked = external_children_index_and_is_stacked + .map(|(_index, is_stacked)| is_stacked) + .unwrap_or(false); Ok(pane_template) }, PaneOrFloatingPane::FloatingPane(_) => { @@ -561,7 +619,7 @@ impl<'a> KdlLayoutParser<'a> { kdl_node: &KdlNode, pane_template: PaneOrFloatingPane, pane_template_kdl_node: &KdlNode, - ) -> Result { + ) -> Result { match pane_template { PaneOrFloatingPane::Pane(_) => { let pane_template_name = kdl_get_string_property_or_child_value_with_error!( @@ -611,7 +669,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; - // let mut floating_pane = FloatingPanesLayout::from(&pane_template); + // let mut floating_pane = FloatingPaneLayout::from(&pane_template); if let Some(height) = height { pane_template.height = Some(height); } @@ -662,7 +720,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; - let mut floating_pane = FloatingPanesLayout::from(&pane_template); + let mut floating_pane = FloatingPaneLayout::from(&pane_template); if let Some(height) = height { floating_pane.height = Some(height); } @@ -812,7 +870,7 @@ impl<'a> KdlLayoutParser<'a> { self.pane_templates.insert( template_name, ( - PaneOrFloatingPane::Either(PaneLayout { + PaneOrFloatingPane::Either(TiledPaneLayout { focus, run, ..Default::default() @@ -830,7 +888,7 @@ impl<'a> KdlLayoutParser<'a> { self.pane_templates.insert( template_name, ( - PaneOrFloatingPane::FloatingPane(FloatingPanesLayout { + PaneOrFloatingPane::FloatingPane(FloatingPaneLayout { focus, run, height, @@ -849,15 +907,16 @@ impl<'a> KdlLayoutParser<'a> { kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); let split_size = self.parse_split_size(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, vec![]), - }; + let (external_children_index, children_are_stacked, pane_parts) = + match kdl_children_nodes!(kdl_node) { + Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, + None => (None, false, vec![]), + }; self.assert_no_mixed_children_and_properties(kdl_node)?; self.pane_templates.insert( template_name, ( - PaneOrFloatingPane::Pane(PaneLayout { + PaneOrFloatingPane::Pane(TiledPaneLayout { borderless: borderless.unwrap_or_default(), focus, split_size, @@ -865,6 +924,7 @@ impl<'a> KdlLayoutParser<'a> { children_split_direction, external_children_index, children: pane_parts, + children_are_stacked, ..Default::default() }), kdl_node.clone(), @@ -877,8 +937,16 @@ impl<'a> KdlLayoutParser<'a> { fn parse_tab_node( &mut self, kdl_node: &KdlNode, - ) -> Result<(bool, Option, PaneLayout, Vec), ConfigError> { - // (is_focused, Option, PaneLayout, Vec) + ) -> Result< + ( + bool, + Option, + TiledPaneLayout, + Vec, + ), + ConfigError, + > { + // (is_focused, Option, PaneLayout, Vec) self.assert_valid_tab_properties(kdl_node)?; let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); @@ -889,11 +957,16 @@ impl<'a> KdlLayoutParser<'a> { let mut child_floating_panes = vec![]; let children = match kdl_children_nodes!(kdl_node) { Some(children) => { - self.parse_child_pane_nodes_for_tab(children, &mut child_floating_panes)? + let should_mark_external_children_index = false; + self.parse_child_pane_nodes_for_tab( + children, + should_mark_external_children_index, + &mut child_floating_panes, + )? }, None => vec![], }; - let mut pane_layout = PaneLayout { + let mut pane_layout = TiledPaneLayout { children_split_direction, children, ..Default::default() @@ -906,8 +979,9 @@ impl<'a> KdlLayoutParser<'a> { fn parse_child_pane_nodes_for_tab( &self, children: &[KdlNode], - child_floating_panes: &mut Vec, - ) -> Result, ConfigError> { + should_mark_external_children_index: bool, + child_floating_panes: &mut Vec, + ) -> Result, ConfigError> { let mut nodes = vec![]; for child in children { if kdl_name!(child) == "pane" { @@ -918,6 +992,7 @@ impl<'a> KdlLayoutParser<'a> { nodes.push(self.parse_pane_node_with_template( child, pane_template, + should_mark_external_children_index, &pane_template_kdl_node, )?); } else if kdl_name!(child) == "floating_panes" { @@ -937,37 +1012,51 @@ impl<'a> KdlLayoutParser<'a> { } } if nodes.is_empty() { - nodes.push(PaneLayout::default()); + nodes.push(TiledPaneLayout::default()); } Ok(nodes) } fn parse_child_pane_nodes_for_pane( &self, children: &[KdlNode], - ) -> Result<(Option, Vec), ConfigError> { - // usize is external_children_index + ) -> Result<(Option, bool, Vec), ConfigError> { + // usize is external_children_index, bool is "children_are_stacked" let mut external_children_index = None; + let mut children_are_stacked = false; let mut nodes = vec![]; for (i, child) in children.iter().enumerate() { if kdl_name!(child) == "pane" { nodes.push(self.parse_pane_node(child)?); } else if kdl_name!(child) == "children" { - let node_has_child_nodes = child.children().map(|c| !c.is_empty()).unwrap_or(false); - let node_has_entries = !child.entries().is_empty(); - if node_has_child_nodes || node_has_entries { - return Err(ConfigError::new_layout_kdl_error( - format!("The `children` node must be bare. All properties should be places on the node consuming this template."), - child.span().offset(), - child.span().len(), - )); + let stacked = kdl_get_bool_property_or_child_value_with_error!(child, "stacked") + .unwrap_or(false); + if let Some(grand_children) = kdl_children_nodes!(child) { + let grand_children: Vec<&str> = grand_children + .iter() + .map(|g| kdl_name!(g)) + .filter(|g| g != &"stacked") + .collect(); + if !grand_children.is_empty() { + return Err(ConfigError::new_layout_kdl_error( + format!( + "Invalid `children` properties: {}", + grand_children.join(", ") + ), + child.span().offset(), + child.span().len(), + )); + } } external_children_index = Some(i); + children_are_stacked = stacked; } else if let Some((pane_template, pane_template_kdl_node)) = self.pane_templates.get(kdl_name!(child)).cloned() { + let should_mark_external_children_index = false; nodes.push(self.parse_pane_node_with_template( child, pane_template, + should_mark_external_children_index, &pane_template_kdl_node, )?); } else if !self.is_a_valid_pane_property(kdl_name!(child)) { @@ -978,7 +1067,7 @@ impl<'a> KdlLayoutParser<'a> { )); } } - Ok((external_children_index, nodes)) + Ok((external_children_index, children_are_stacked, nodes)) } fn has_child_nodes(&self, kdl_node: &KdlNode) -> bool { if let Some(children) = kdl_children_nodes!(kdl_node) { @@ -1076,7 +1165,7 @@ impl<'a> KdlLayoutParser<'a> { } fn assert_one_children_block( &self, - layout: &PaneLayout, + layout: &TiledPaneLayout, kdl_node: &KdlNode, ) -> Result<(), ConfigError> { let children_block_count = layout.children_block_count(); @@ -1193,8 +1282,6 @@ impl<'a> KdlLayoutParser<'a> { ) -> Result<(), ConfigError> { let has_borderless_prop = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless").is_some(); - let has_focus_prop = - kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus").is_some(); let has_cwd_prop = kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd").is_some(); let has_non_cwd_run_prop = self @@ -1206,15 +1293,12 @@ impl<'a> KdlLayoutParser<'a> { .unwrap_or(false); let has_nested_nodes_or_children_block = self.has_child_panes_tabs_or_templates(kdl_node); if has_nested_nodes_or_children_block - && (has_borderless_prop || has_focus_prop || has_non_cwd_run_prop || has_cwd_prop) + && (has_borderless_prop || has_non_cwd_run_prop || has_cwd_prop) { let mut offending_nodes = vec![]; if has_borderless_prop { offending_nodes.push("borderless"); } - if has_focus_prop { - offending_nodes.push("focus"); - } if has_non_cwd_run_prop { offending_nodes.push("command/edit/plugin"); } @@ -1235,8 +1319,8 @@ impl<'a> KdlLayoutParser<'a> { } fn insert_layout_children_or_error( &self, - layout: &mut PaneLayout, - mut child_panes_layout: PaneLayout, + layout: &mut TiledPaneLayout, + mut child_panes_layout: TiledPaneLayout, kdl_node: &KdlNode, ) -> Result<(), ConfigError> { let successfully_inserted = layout.insert_children_layout(&mut child_panes_layout)?; @@ -1253,11 +1337,20 @@ impl<'a> KdlLayoutParser<'a> { fn parse_tab_node_with_template( &self, kdl_node: &KdlNode, - mut tab_layout: PaneLayout, - mut tab_template_floating_panes: Vec, + mut tab_layout: TiledPaneLayout, + mut tab_template_floating_panes: Vec, + should_mark_external_children_index: bool, tab_layout_kdl_node: &KdlNode, - ) -> Result<(bool, Option, PaneLayout, Vec), ConfigError> { - // (is_focused, Option, PaneLayout, Vec) + ) -> Result< + ( + bool, + Option, + TiledPaneLayout, + Vec, + ), + ConfigError, + > { + // (is_focused, Option, PaneLayout, Vec) let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); let tab_cwd = @@ -1266,9 +1359,12 @@ impl<'a> KdlLayoutParser<'a> { let children_split_direction = self.parse_split_direction(kdl_node)?; match kdl_children_nodes!(kdl_node) { Some(children) => { - let child_panes = self - .parse_child_pane_nodes_for_tab(children, &mut tab_template_floating_panes)?; - let child_panes_layout = PaneLayout { + let child_panes = self.parse_child_pane_nodes_for_tab( + children, + should_mark_external_children_index, + &mut tab_template_floating_panes, + )?; + let child_panes_layout = TiledPaneLayout { children_split_direction, children: child_panes, ..Default::default() @@ -1284,7 +1380,7 @@ impl<'a> KdlLayoutParser<'a> { if let Some(index_of_children) = tab_layout.external_children_index { tab_layout .children - .insert(index_of_children, PaneLayout::default()); + .insert(index_of_children, TiledPaneLayout::default()); } }, } @@ -1342,7 +1438,7 @@ impl<'a> KdlLayoutParser<'a> { fn parse_tab_template_node( &self, kdl_node: &KdlNode, - ) -> Result<(PaneLayout, Vec), ConfigError> { + ) -> Result<(TiledPaneLayout, Vec), ConfigError> { self.assert_valid_tab_properties(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; let mut tab_children = vec![]; @@ -1368,9 +1464,11 @@ impl<'a> KdlLayoutParser<'a> { } else if let Some((pane_template, pane_template_kdl_node)) = self.pane_templates.get(kdl_name!(child)).cloned() { + let should_mark_external_children_index = false; tab_children.push(self.parse_pane_node_with_template( child, pane_template, + should_mark_external_children_index, &pane_template_kdl_node, )?); } else if kdl_name!(child) == "floating_panes" { @@ -1392,7 +1490,7 @@ impl<'a> KdlLayoutParser<'a> { } } Ok(( - PaneLayout { + TiledPaneLayout { children_split_direction, children: tab_children, external_children_index, @@ -1401,14 +1499,14 @@ impl<'a> KdlLayoutParser<'a> { tab_floating_children, )) } - fn default_template(&self) -> Result, ConfigError> { + fn default_template(&self) -> Result, ConfigError> { match &self.default_tab_template { Some((template, _template_floating_panes, _kdl_node)) => { let mut template = template.clone(); if let Some(children_index) = template.external_children_index { template .children - .insert(children_index, PaneLayout::default()) + .insert(children_index, TiledPaneLayout::default()) } template.external_children_index = None; Ok(Some(template)) @@ -1545,28 +1643,230 @@ impl<'a> KdlLayoutParser<'a> { } Ok(()) } + fn populate_swap_tiled_layouts( + &mut self, + layout_children: &[KdlNode], + swap_tiled_layouts: &mut Vec, + ) -> Result<(), ConfigError> { + for child in layout_children.iter() { + let child_name = kdl_name!(child); + if child_name == "swap_tiled_layout" { + let swap_layout_name = + kdl_get_string_property_or_child_value!(child, "name").map(|n| String::from(n)); + if let Some(swap_tiled_layout_group) = kdl_children_nodes!(child) { + let mut swap_tiled_layout = BTreeMap::new(); + for layout in swap_tiled_layout_group { + let layout_node_name = kdl_name!(layout); + if layout_node_name == "tab" { + let layout_constraint = self.parse_constraint(layout)?; + + match &self.default_tab_template { + Some(( + default_tab_template, + _default_tab_template_floating_panes, + default_tab_template_kdl_node, + )) => { + let default_tab_template = default_tab_template.clone(); + let layout = self + .populate_one_swap_tiled_layout_with_template( + layout, + default_tab_template, + default_tab_template_kdl_node.clone(), + )?; + swap_tiled_layout.insert(layout_constraint, layout); + }, + None => { + let layout = self.populate_one_swap_tiled_layout(layout)?; + swap_tiled_layout.insert(layout_constraint, layout); + }, + } + } else if let Some(( + tab_template, + _tab_template_floating_panes, + tab_template_kdl_node, + )) = self.tab_templates.get(layout_node_name).cloned() + { + let layout_constraint = self.parse_constraint(layout)?; + let layout = self.populate_one_swap_tiled_layout_with_template( + layout, + tab_template, + tab_template_kdl_node, + )?; + swap_tiled_layout.insert(layout_constraint, layout); + } + } + swap_tiled_layouts.push((swap_tiled_layout, swap_layout_name)); + } + } + } + Ok(()) + } + fn populate_swap_floating_layouts( + &mut self, + layout_children: &[KdlNode], + swap_floating_layouts: &mut Vec, + ) -> Result<(), ConfigError> { + for child in layout_children.iter() { + let child_name = kdl_name!(child); + if child_name == "swap_floating_layout" { + let swap_layout_name = + kdl_get_string_property_or_child_value!(child, "name").map(|n| String::from(n)); + if let Some(swap_floating_layout_group) = kdl_children_nodes!(child) { + let mut swap_floating_layout = BTreeMap::new(); + for layout in swap_floating_layout_group { + let layout_node_name = kdl_name!(layout); + if layout_node_name == "floating_panes" { + let layout_constraint = self.parse_constraint(layout)?; + let layout = self.populate_one_swap_floating_layout(layout)?; + swap_floating_layout.insert(layout_constraint, layout); + } else if let Some(( + tab_template, + tab_template_floating_panes, + tab_template_kdl_node, + )) = self.tab_templates.get(layout_node_name).cloned() + { + let layout_constraint = self.parse_constraint(layout)?; + let layout = self.populate_one_swap_floating_layout_with_template( + layout, + tab_template, + tab_template_floating_panes, + tab_template_kdl_node, + )?; + swap_floating_layout.insert(layout_constraint, layout); + } + } + swap_floating_layouts.push((swap_floating_layout, swap_layout_name)); + } + } + } + Ok(()) + } + fn parse_constraint(&mut self, layout_node: &KdlNode) -> Result { + if let Some(max_panes) = kdl_get_string_property_or_child_value!(layout_node, "max_panes") { + return Err(kdl_parsing_error!( + format!( + "max_panes should be a fixed number (eg. 1) and not a quoted string (\"{}\")", + max_panes + ), + layout_node + )); + }; + if let Some(min_panes) = kdl_get_string_property_or_child_value!(layout_node, "min_panes") { + return Err(kdl_parsing_error!( + format!( + "min_panes should be a fixed number (eg. 1) and not a quoted string (\"{}\")", + min_panes + ), + layout_node + )); + }; + let max_panes = kdl_get_int_property_or_child_value!(layout_node, "max_panes"); + let min_panes = kdl_get_int_property_or_child_value!(layout_node, "min_panes"); + match (min_panes, max_panes) { + (Some(_min_panes), Some(_max_panes)) => Err(kdl_parsing_error!( + format!("cannot have more than one constraint (eg. max_panes + min_panes)'"), + layout_node + )), + (Some(min_panes), None) => Ok(LayoutConstraint::MinPanes(min_panes as usize)), + (None, Some(max_panes)) => Ok(LayoutConstraint::MaxPanes(max_panes as usize)), + _ => Ok(LayoutConstraint::NoConstraint), + } + } + fn populate_one_swap_tiled_layout( + &self, + layout_node: &KdlNode, + ) -> Result { + self.assert_valid_tab_properties(layout_node)?; + let children_split_direction = self.parse_split_direction(layout_node)?; + let mut child_floating_panes = vec![]; + let children = match kdl_children_nodes!(layout_node) { + Some(children) => { + let should_mark_external_children_index = true; + self.parse_child_pane_nodes_for_tab( + children, + should_mark_external_children_index, + &mut child_floating_panes, + )? + }, + None => vec![], + }; + let pane_layout = TiledPaneLayout { + children_split_direction, + children, + ..Default::default() + }; + Ok(pane_layout) + } + fn populate_one_swap_tiled_layout_with_template( + &self, + layout_node: &KdlNode, + tab_template: TiledPaneLayout, + tab_template_kdl_node: KdlNode, + ) -> Result { + let should_mark_external_children_index = true; + let layout = self.parse_tab_node_with_template( + layout_node, + tab_template, + vec![], // no floating_panes in swap tiled node + should_mark_external_children_index, + &tab_template_kdl_node, + )?; + Ok(layout.2) + } + fn populate_one_swap_floating_layout( + &self, + layout_node: &KdlNode, + ) -> Result, ConfigError> { + let mut floating_panes = vec![]; + self.assert_valid_tab_properties(layout_node)?; + self.populate_floating_pane_children(layout_node, &mut floating_panes)?; + Ok(floating_panes) + } + fn populate_one_swap_floating_layout_with_template( + &self, + layout_node: &KdlNode, + tab_template: TiledPaneLayout, + tab_template_floating_panes: Vec, + tab_template_kdl_node: KdlNode, + ) -> Result, ConfigError> { + let should_mark_external_children_index = false; + let layout = self.parse_tab_node_with_template( + layout_node, + tab_template, + tab_template_floating_panes, + should_mark_external_children_index, + &tab_template_kdl_node, + )?; + Ok(layout.3) + } fn layout_with_tabs( &self, - tabs: Vec<(Option, PaneLayout, Vec)>, + tabs: Vec<(Option, TiledPaneLayout, Vec)>, focused_tab_index: Option, + swap_tiled_layouts: Vec, + swap_floating_layouts: Vec, ) -> Result { let template = self .default_template()? - .unwrap_or_else(|| PaneLayout::default()); + .unwrap_or_else(|| TiledPaneLayout::default()); Ok(Layout { tabs: tabs, - template: Some(template), + template: Some((template, vec![])), focused_tab_index, + swap_tiled_layouts, + swap_floating_layouts, ..Default::default() }) } fn layout_with_one_tab( &self, - panes: Vec, - floating_panes: Vec, + panes: Vec, + floating_panes: Vec, + swap_tiled_layouts: Vec, + swap_floating_layouts: Vec, ) -> Result { - let main_tab_layout = PaneLayout { + let main_tab_layout = TiledPaneLayout { children: panes, ..Default::default() }; @@ -1582,30 +1882,39 @@ impl<'a> KdlLayoutParser<'a> { // create a layout with one tab that has these child panes Ok(Layout { tabs, - template: Some(template), - floating_panes_template: floating_panes, + template: Some((template, floating_panes)), + swap_tiled_layouts, + swap_floating_layouts, ..Default::default() }) } fn layout_with_one_pane( &self, - child_floating_panes: Vec, + child_floating_panes: Vec, + swap_tiled_layouts: Vec, + swap_floating_layouts: Vec, ) -> Result { let template = self .default_template()? - .unwrap_or_else(|| PaneLayout::default()); + .unwrap_or_else(|| TiledPaneLayout::default()); Ok(Layout { - template: Some(template), - floating_panes_template: child_floating_panes, + template: Some((template, child_floating_panes)), + swap_tiled_layouts, + swap_floating_layouts, ..Default::default() }) } fn populate_layout_child( &mut self, child: &KdlNode, - child_tabs: &mut Vec<(bool, Option, PaneLayout, Vec)>, - child_panes: &mut Vec, - child_floating_panes: &mut Vec, + child_tabs: &mut Vec<( + bool, + Option, + TiledPaneLayout, + Vec, + )>, + child_panes: &mut Vec, + child_floating_panes: &mut Vec, ) -> Result<(), ConfigError> { let child_name = kdl_name!(child); if (child_name == "pane" || child_name == "floating_panes") && !child_tabs.is_empty() { @@ -1638,10 +1947,12 @@ impl<'a> KdlLayoutParser<'a> { default_tab_template_kdl_node, )) => { let default_tab_template = default_tab_template.clone(); + let should_mark_external_children_index = false; child_tabs.push(self.parse_tab_node_with_template( child, default_tab_template, default_tab_template_floating_panes.clone(), + should_mark_external_children_index, default_tab_template_kdl_node, )?); }, @@ -1659,10 +1970,12 @@ impl<'a> KdlLayoutParser<'a> { child.span().len(), )); } + let should_mark_external_children_index = false; child_tabs.push(self.parse_tab_node_with_template( child, tab_template, tab_template_floating_panes, + should_mark_external_children_index, &tab_template_kdl_node, )?); } else if let Some((pane_template, pane_template_kdl_node)) = @@ -1675,8 +1988,13 @@ impl<'a> KdlLayoutParser<'a> { child.span().len(), )); } - let mut pane_template = - self.parse_pane_node_with_template(child, pane_template, &pane_template_kdl_node)?; + let should_mark_external_children_index = false; + let mut pane_template = self.parse_pane_node_with_template( + child, + pane_template, + should_mark_external_children_index, + &pane_template_kdl_node, + )?; if let Some(cwd_prefix) = &self.cwd_prefix(None)? { pane_template.add_cwd_to_layout(&cwd_prefix); } @@ -1693,7 +2011,7 @@ impl<'a> KdlLayoutParser<'a> { fn populate_floating_pane_children( &self, child: &KdlNode, - child_floating_panes: &mut Vec, + child_floating_panes: &mut Vec, ) -> Result<(), ConfigError> { if let Some(children) = kdl_children_nodes!(child) { for child in children { @@ -1713,7 +2031,6 @@ impl<'a> KdlLayoutParser<'a> { )?; child_floating_panes.push(pane_node); } else { - // TODO: invalid node name return Err(ConfigError::new_layout_kdl_error( format!( "floating_panes can only contain pane nodes, found: {}", @@ -1727,6 +2044,54 @@ impl<'a> KdlLayoutParser<'a> { }; Ok(()) } + pub fn parse_external_swap_layouts( + &mut self, + raw_swap_layouts: &str, + mut existing_layout: Layout, + ) -> Result { + let kdl_swap_layout: KdlDocument = raw_swap_layouts.parse()?; + let mut swap_tiled_layouts = vec![]; + let mut swap_floating_layouts = vec![]; + + for node in kdl_swap_layout.nodes() { + let node_name = kdl_name!(node); + if node_name == "swap_floating_layout" + || node_name == "swap_tiled_layout" + || node_name == "tab_template" + || node_name == "pane_template" + { + continue; + } else if node_name == "layout" { + return Err(ConfigError::new_layout_kdl_error( + "Swap layouts should not have their own layout node".into(), + node.span().offset(), + node.span().len(), + ))?; + } else if self.is_a_reserved_word(node_name) { + return Err(ConfigError::new_layout_kdl_error( + format!( + "Swap layouts should not contain bare nodes of type: {}", + node_name + ), + node.span().offset(), + node.span().len(), + ))?; + } + } + + self.populate_pane_templates(kdl_swap_layout.nodes(), &kdl_swap_layout)?; + self.populate_tab_templates(kdl_swap_layout.nodes())?; + self.populate_swap_tiled_layouts(kdl_swap_layout.nodes(), &mut swap_tiled_layouts)?; + self.populate_swap_floating_layouts(kdl_swap_layout.nodes(), &mut swap_floating_layouts)?; + + existing_layout + .swap_tiled_layouts + .append(&mut swap_tiled_layouts); + existing_layout + .swap_floating_layouts + .append(&mut swap_floating_layouts); + Ok(existing_layout) + } pub fn parse(&mut self) -> Result { let kdl_layout: KdlDocument = self.raw_layout.parse()?; let layout_node = kdl_layout @@ -1754,10 +2119,14 @@ impl<'a> KdlLayoutParser<'a> { let mut child_tabs = vec![]; let mut child_panes = vec![]; let mut child_floating_panes = vec![]; + let mut swap_tiled_layouts = vec![]; + let mut swap_floating_layouts = vec![]; if let Some(children) = kdl_children_nodes!(layout_node) { self.populate_global_cwd(layout_node)?; self.populate_pane_templates(children, &kdl_layout)?; self.populate_tab_templates(children)?; + self.populate_swap_tiled_layouts(children, &mut swap_tiled_layouts)?; + self.populate_swap_floating_layouts(children, &mut swap_floating_layouts)?; for child in children { self.populate_layout_child( child, @@ -1783,7 +2152,7 @@ impl<'a> KdlLayoutParser<'a> { let focused_tab_index = child_tabs .iter() .position(|(is_focused, _, _, _)| *is_focused); - let child_tabs: Vec<(Option, PaneLayout, Vec)> = + let child_tabs: Vec<(Option, TiledPaneLayout, Vec)> = child_tabs .drain(..) .map( @@ -1792,11 +2161,25 @@ impl<'a> KdlLayoutParser<'a> { }, ) .collect(); - self.layout_with_tabs(child_tabs, focused_tab_index) + self.layout_with_tabs( + child_tabs, + focused_tab_index, + swap_tiled_layouts, + swap_floating_layouts, + ) } else if !child_panes.is_empty() { - self.layout_with_one_tab(child_panes, child_floating_panes) + self.layout_with_one_tab( + child_panes, + child_floating_panes, + swap_tiled_layouts, + swap_floating_layouts, + ) } else { - self.layout_with_one_pane(child_floating_panes) + self.layout_with_one_pane( + child_floating_panes, + swap_tiled_layouts, + swap_floating_layouts, + ) } } } diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index f7a6ac4d..f3c7d9fb 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -66,6 +66,8 @@ macro_rules! parse_kdl_action_arguments { "Confirm" => Ok(Action::Confirm), "Deny" => Ok(Action::Deny), "ToggleMouseMode" => Ok(Action::ToggleMouseMode), + "PreviousSwapLayout" => Ok(Action::PreviousSwapLayout), + "NextSwapLayout" => Ok(Action::NextSwapLayout), _ => Err(ConfigError::new_kdl_error( format!("Unsupported action: {:?}", $action_name), $action_node.span().offset(), @@ -452,6 +454,7 @@ impl Action { Ok(Action::MovePane(Some(direction))) } }, + "MovePaneBackwards" => Ok(Action::MovePaneBackwards), "DumpScreen" => Ok(Action::DumpScreen(string, false)), "NewPane" => { if string.is_empty() { @@ -729,6 +732,11 @@ impl TryFrom<(&KdlNode, &Options)> for Action { action_arguments, kdl_action ), + "MovePaneBackwards" => parse_kdl_action_char_or_string_arguments!( + action_name, + action_arguments, + kdl_action + ), "DumpScreen" => parse_kdl_action_char_or_string_arguments!( action_name, action_arguments, @@ -745,7 +753,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { "NewTab" => { let command_metadata = action_children.iter().next(); if command_metadata.is_none() { - return Ok(Action::NewTab(None, vec![], None)); + return Ok(Action::NewTab(None, vec![], None, None, None)); } let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); @@ -762,7 +770,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { .and_then(|c_m| kdl_child_string_value_for_entry(c_m, "name")) .map(|name_string| name_string.to_string()); - let (path_to_raw_layout, raw_layout) = + let (path_to_raw_layout, raw_layout, swap_layouts) = Layout::stringified_from_path_or_default(layout.as_ref(), None).map_err( |e| { ConfigError::new_kdl_error( @@ -773,14 +781,19 @@ impl TryFrom<(&KdlNode, &Options)> for Action { }, )?; - let layout = - Layout::from_str(&raw_layout, path_to_raw_layout, cwd).map_err(|e| { - ConfigError::new_kdl_error( - format!("Failed to load layout: {}", e), - kdl_action.span().offset(), - kdl_action.span().len(), - ) - })?; + let layout = Layout::from_str( + &raw_layout, + path_to_raw_layout, + swap_layouts.as_ref().map(|(f, p)| (f.as_str(), p.as_str())), + cwd, + ) + .map_err(|e| { + ConfigError::new_kdl_error( + format!("Failed to load layout: {}", e), + kdl_action.span().offset(), + kdl_action.span().len(), + ) + })?; let mut tabs = layout.tabs(); if tabs.len() > 1 { @@ -793,11 +806,23 @@ impl TryFrom<(&KdlNode, &Options)> for Action { let (tab_name, layout, floating_panes_layout) = tabs.drain(..).next().unwrap(); let name = tab_name.or(name); - Ok(Action::NewTab(Some(layout), floating_panes_layout, name)) + Ok(Action::NewTab( + Some(layout), + floating_panes_layout, + None, + None, + name, + )) } else { let (layout, floating_panes_layout) = layout.new_tab(); - Ok(Action::NewTab(Some(layout), floating_panes_layout, name)) + Ok(Action::NewTab( + Some(layout), + floating_panes_layout, + None, + None, + name, + )) } }, "GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action), @@ -847,6 +872,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action { }; Ok(Action::Run(run_command_action)) }, + "PreviousSwapLayout" => Ok(Action::PreviousSwapLayout), + "NextSwapLayout" => Ok(Action::NextSwapLayout), _ => Err(ConfigError::new_kdl_error( format!("Unsupported action: {}", action_name).into(), kdl_action.span().offset(), @@ -1273,6 +1300,8 @@ impl Options { .map(|(string, _entry)| PathBuf::from(string)); let pane_frames = kdl_property_first_arg_as_bool_or_error!(kdl_options, "pane_frames").map(|(v, _)| v); + let auto_layout = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "auto_layout").map(|(v, _)| v); let theme = kdl_property_first_arg_as_string_or_error!(kdl_options, "theme") .map(|(theme, _entry)| theme.to_string()); let default_mode = @@ -1337,6 +1366,7 @@ impl Options { scrollback_editor, session_name, attach_to_session, + auto_layout, }) } } @@ -1370,36 +1400,66 @@ impl Layout { pub fn from_kdl( raw_layout: &str, file_name: String, + raw_swap_layouts: Option<(&str, &str)>, // raw_swap_layouts swap_layouts_file_name cwd: Option, ) -> Result { - KdlLayoutParser::new(raw_layout, cwd).parse().map_err(|e| { - match e { - ConfigError::KdlError(kdl_error) => ConfigError::KdlError(kdl_error.add_src(file_name, String::from(raw_layout))), - ConfigError::KdlDeserializationError(kdl_error) => { - let error_message = match kdl_error.kind { - kdl::KdlErrorKind::Context("valid node terminator") => { - format!("Failed to deserialize KDL node. \nPossible reasons:\n{}\n{}\n{}\n{}", - "- Missing `;` after a node name, eg. { node; another_node; }", - "- Missing quotations (\") around an argument node eg. { first_node \"argument_node\"; }", - "- Missing an equal sign (=) between node arguments on a title line. eg. argument=\"value\"", - "- Found an extraneous equal sign (=) between node child arguments and their values. eg. { argument=\"value\" }") + let mut kdl_layout_parser = KdlLayoutParser::new(raw_layout, cwd); + let layout = kdl_layout_parser.parse().map_err(|e| match e { + ConfigError::KdlError(kdl_error) => { + ConfigError::KdlError(kdl_error.add_src(file_name, String::from(raw_layout))) + }, + ConfigError::KdlDeserializationError(kdl_error) => { + kdl_layout_error(kdl_error, file_name, raw_layout) + }, + e => e, + })?; + match raw_swap_layouts { + Some((raw_swap_layout_filename, raw_swap_layout)) => { + // here we use the same parser to parse the swap layout so that we can reuse assets + // (eg. pane and tab templates) + kdl_layout_parser + .parse_external_swap_layouts(raw_swap_layout, layout) + .map_err(|e| match e { + ConfigError::KdlError(kdl_error) => { + ConfigError::KdlError(kdl_error.add_src( + String::from(raw_swap_layout_filename), + String::from(raw_swap_layout), + )) }, - _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), - }; - let kdl_error = KdlError { - error_message, - src: Some(NamedSource::new(file_name, String::from(raw_layout))), - offset: Some(kdl_error.span.offset()), - len: Some(kdl_error.span.len()), - help_message: None, - }; - ConfigError::KdlError(kdl_error) - }, - e => e - } - }) + ConfigError::KdlDeserializationError(kdl_error) => kdl_layout_error( + kdl_error, + raw_swap_layout_filename.into(), + raw_swap_layout, + ), + e => e, + }) + }, + None => Ok(layout), + } } } + +fn kdl_layout_error(kdl_error: kdl::KdlError, file_name: String, raw_layout: &str) -> ConfigError { + let error_message = match kdl_error.kind { + kdl::KdlErrorKind::Context("valid node terminator") => { + format!("Failed to deserialize KDL node. \nPossible reasons:\n{}\n{}\n{}\n{}", + "- Missing `;` after a node name, eg. { node; another_node; }", + "- Missing quotations (\") around an argument node eg. { first_node \"argument_node\"; }", + "- Missing an equal sign (=) between node arguments on a title line. eg. argument=\"value\"", + "- Found an extraneous equal sign (=) between node child arguments and their values. eg. { argument=\"value\" }") + }, + _ => String::from(kdl_error.help.unwrap_or("Kdl Deserialization Error")), + }; + let kdl_error = KdlError { + error_message, + src: Some(NamedSource::new(file_name, String::from(raw_layout))), + offset: Some(kdl_error.span.offset()), + len: Some(kdl_error.span.len()), + help_message: None, + }; + ConfigError::KdlError(kdl_error) +} + impl EnvironmentVariables { pub fn from_kdl(kdl_env_variables: &KdlNode) -> Result { let mut env: HashMap = HashMap::new(); diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index 66f66ad5..1df73a32 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -1,15 +1,17 @@ use serde::{Deserialize, Serialize}; +use std::hash::{Hash, Hasher}; use crate::position::Position; /// Contains the position and size of a [`Pane`], or more generally of any terminal, measured /// in character rows and columns. -#[derive(Clone, Copy, Default, PartialEq, Debug, Serialize, Deserialize, Eq)] +#[derive(Clone, Copy, Default, PartialEq, Debug, Serialize, Deserialize, Eq, Hash)] pub struct PaneGeom { pub x: usize, pub y: usize, pub rows: Dimension, pub cols: Dimension, + pub is_stacked: bool, } #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] @@ -40,7 +42,7 @@ pub struct SizeInPixels { pub width: usize, } -#[derive(Eq, Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Eq, Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Hash)] pub struct Dimension { pub constraint: Constraint, inner: usize, @@ -109,10 +111,16 @@ impl Dimension { pub fn increase_inner(&mut self, by: usize) { self.inner += by; } + pub fn decrease_inner(&mut self, by: usize) { + self.inner -= by; + } pub fn is_fixed(&self) -> bool { matches!(self.constraint, Constraint::Fixed(_)) } + pub fn is_percent(&self) -> bool { + matches!(self.constraint, Constraint::Percent(_)) + } } #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] @@ -123,6 +131,16 @@ pub enum Constraint { Percent(f64), } +#[allow(clippy::derive_hash_xor_eq)] +impl Hash for Constraint { + fn hash(&self, state: &mut H) { + match self { + Constraint::Fixed(size) => size.hash(state), + Constraint::Percent(size) => (*size as usize).hash(state), + } + } +} + impl Eq for Constraint {} impl PaneGeom { @@ -149,6 +167,14 @@ impl Offset { } } + pub fn shift_right_and_top(right: usize, top: usize) -> Self { + Self { + right, + top, + ..Default::default() + } + } + // FIXME: This should be top and left, not bottom and right, but `boundaries.rs` would need // some changing pub fn shift(bottom: usize, right: usize) -> Self { @@ -189,3 +215,17 @@ impl From<&PaneGeom> for Size { } } } + +impl From<&Size> for PaneGeom { + fn from(size: &Size) -> Self { + let mut rows = Dimension::percent(100.0); + let mut cols = Dimension::percent(100.0); + rows.set_inner(size.rows); + cols.set_inner(size.cols); + Self { + rows, + cols, + ..Default::default() + } + } +} diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index ddbc5967..2c4d8989 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -104,12 +104,24 @@ pub const DEFAULT_LAYOUT: &[u8] = include_bytes!(concat!( "assets/layouts/default.kdl" )); +pub const DEFAULT_SWAP_LAYOUT: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + "assets/layouts/default.swap.kdl" +)); + pub const STRIDER_LAYOUT: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", "assets/layouts/strider.kdl" )); +pub const STRIDER_SWAP_LAYOUT: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + "assets/layouts/strider.swap.kdl" +)); + pub const NO_STATUS_LAYOUT: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", @@ -122,6 +134,12 @@ pub const COMPACT_BAR_LAYOUT: &[u8] = include_bytes!(concat!( "assets/layouts/compact.kdl" )); +pub const COMPACT_BAR_SWAP_LAYOUT: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + "assets/layouts/compact.swap.kdl" +)); + pub const FISH_EXTRA_COMPLETION: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/", @@ -175,6 +193,18 @@ pub fn dump_specified_layout(layout: &str) -> std::io::Result<()> { } } +pub fn dump_specified_swap_layout(swap_layout: &str) -> std::io::Result<()> { + match swap_layout { + "strider" => dump_asset(STRIDER_SWAP_LAYOUT), + "default" => dump_asset(DEFAULT_SWAP_LAYOUT), + "compact" => dump_asset(COMPACT_BAR_SWAP_LAYOUT), + not_found => Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Swap Layout not found for: {}", not_found), + )), + } +} + #[cfg(not(target_family = "wasm"))] pub fn dump_builtin_plugins(path: &PathBuf) -> Result<()> { for (asset_path, bytes) in ASSET_MAP.iter() { @@ -230,6 +260,10 @@ pub struct Setup { #[clap(long, value_parser)] pub dump_layout: Option, + /// Dump the specified swap layout file to stdout + #[clap(long, value_parser)] + pub dump_swap_layout: Option, + /// Dump the builtin plugins to DIR or "DATA DIR" if unspecified #[clap( long, @@ -337,6 +371,11 @@ impl Setup { std::process::exit(0); } + if let Some(swap_layout) = &self.dump_swap_layout { + dump_specified_swap_layout(swap_layout)?; + std::process::exit(0); + } + Ok(()) } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap index 00801403..dfe128da 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 561 +assertion_line: 597 expression: "format!(\"{:#?}\", options)" --- Options { @@ -24,4 +24,5 @@ Options { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap index 8c6d5992..2b284c4a 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap @@ -7,16 +7,22 @@ Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap index a36a7543..881d22ee 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 584 +assertion_line: 625 expression: "format!(\"{:#?}\", options)" --- Options { @@ -24,4 +24,5 @@ Options { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap index 8d89277c..b87b9344 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap @@ -1,83 +1,1605 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 583 +assertion_line: 601 expression: "format!(\"{:#?}\", layout)" --- Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [ - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: Some( - Fixed( - 1, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), ), - ), - run: Some( - Plugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "tab-bar", + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), ), - ), - }, + }, + ), ), - ), - borderless: true, - focus: None, - external_children_index: None, - }, - PaneLayout { + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), + ), + swap_layouts: [], + swap_tiled_layouts: [ + ( + { + MaxPanes( + 5, + ): TiledPaneLayout { children_split_direction: Horizontal, name: None, - children: [], + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], split_size: None, run: None, borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - PaneLayout { + MaxPanes( + 8, + ): TiledPaneLayout { children_split_direction: Horizontal, name: None, - children: [], - split_size: Some( - Fixed( - 2, - ), - ), - run: Some( - Plugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "status-bar", - ), + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, ), - }, - ), - ), - borderless: true, + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, focus: None, external_children_index: None, + children_are_stacked: false, }, - ], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, - ), - floating_panes_template: [], + MaxPanes( + 12, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + }, + Some( + "vertical", + ), + ), + ( + { + MaxPanes( + 5, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + MaxPanes( + 8, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + MaxPanes( + 12, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + }, + Some( + "horizontal", + ), + ), + ( + { + MinPanes( + 5, + ): TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 1, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "tab-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Vertical, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: Some( + 0, + ), + children_are_stacked: true, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: Some( + Fixed( + 2, + ), + ), + run: Some( + Plugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "status-bar", + ), + ), + }, + ), + ), + borderless: true, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + }, + Some( + "stacked", + ), + ), + ], + swap_floating_layouts: [ + ( + { + NoConstraint: [], + }, + Some( + "staggered", + ), + ), + ( + { + MaxPanes( + 10, + ): [ + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 1, + ), + ), + y: Some( + Fixed( + 1, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 2, + ), + ), + y: Some( + Fixed( + 2, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 3, + ), + ), + y: Some( + Fixed( + 3, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 4, + ), + ), + y: Some( + Fixed( + 4, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 5, + ), + ), + y: Some( + Fixed( + 5, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 6, + ), + ), + y: Some( + Fixed( + 6, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 7, + ), + ), + y: Some( + Fixed( + 7, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 8, + ), + ), + y: Some( + Fixed( + 8, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 9, + ), + ), + y: Some( + Fixed( + 9, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 90, + ), + ), + width: Some( + Percent( + 90, + ), + ), + x: Some( + Fixed( + 10, + ), + ), + y: Some( + Fixed( + 10, + ), + ), + run: None, + focus: Some( + true, + ), + }, + ], + }, + Some( + "enlarged", + ), + ), + ( + { + MaxPanes( + 1, + ): [ + FloatingPaneLayout { + name: None, + height: None, + width: None, + x: Some( + Percent( + 50, + ), + ), + y: Some( + Percent( + 50, + ), + ), + run: None, + focus: None, + }, + ], + MaxPanes( + 2, + ): [ + FloatingPaneLayout { + name: None, + height: None, + width: Some( + Percent( + 45, + ), + ), + x: Some( + Percent( + 1, + ), + ), + y: Some( + Percent( + 25, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: None, + width: Some( + Percent( + 45, + ), + ), + x: Some( + Percent( + 50, + ), + ), + y: Some( + Percent( + 25, + ), + ), + run: None, + focus: None, + }, + ], + MaxPanes( + 3, + ): [ + FloatingPaneLayout { + name: None, + height: Some( + Percent( + 45, + ), + ), + width: Some( + Percent( + 45, + ), + ), + x: None, + y: Some( + Percent( + 55, + ), + ), + run: None, + focus: Some( + true, + ), + }, + FloatingPaneLayout { + name: None, + height: None, + width: Some( + Percent( + 45, + ), + ), + x: Some( + Percent( + 1, + ), + ), + y: Some( + Percent( + 1, + ), + ), + run: None, + focus: None, + }, + FloatingPaneLayout { + name: None, + height: None, + width: Some( + Percent( + 45, + ), + ), + x: Some( + Percent( + 50, + ), + ), + y: Some( + Percent( + 1, + ), + ), + run: None, + focus: None, + }, + ], + }, + Some( + "spread", + ), + ), + ], } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap index e23c0d24..b8c2a501 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 546 +assertion_line: 584 expression: "format!(\"{:#?}\", options)" --- Options { @@ -22,4 +22,5 @@ Options { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 2ccadb3c..6ba66680 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -1,5 +1,6 @@ --- source: zellij-utils/src/setup.rs +assertion_line: 621 expression: "format!(\"{:#?}\", config)" --- Config { @@ -35,6 +36,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -362,6 +377,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -691,6 +720,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -977,6 +1020,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -1040,6 +1085,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1298,6 +1357,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1496,6 +1569,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1773,6 +1860,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1971,6 +2072,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2166,6 +2281,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2366,6 +2495,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2594,6 +2737,11 @@ Config { None, ), ], + Char( + 'p', + ): [ + MovePaneBackwards, + ], Alt( Char( '+', @@ -2624,6 +2772,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2816,6 +2978,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3005,6 +3181,11 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + NextSwapLayout, + ], Char( '"', ): [ @@ -3052,6 +3233,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -3169,6 +3352,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3343,6 +3540,7 @@ Config { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, }, themes: {}, plugins: { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 6bc0fbba..97a16c77 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -1,5 +1,6 @@ --- source: zellij-utils/src/setup.rs +assertion_line: 679 expression: "format!(\"{:#?}\", config)" --- Config { @@ -35,6 +36,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -362,6 +377,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -691,6 +720,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -977,6 +1020,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -1040,6 +1085,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1298,6 +1357,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1496,6 +1569,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1773,6 +1860,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1971,6 +2072,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2166,6 +2281,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2366,6 +2495,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2594,6 +2737,11 @@ Config { None, ), ], + Char( + 'p', + ): [ + MovePaneBackwards, + ], Alt( Char( '+', @@ -2624,6 +2772,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2816,6 +2978,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3005,6 +3181,11 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + NextSwapLayout, + ], Char( '"', ): [ @@ -3052,6 +3233,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -3169,6 +3352,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3343,6 +3540,7 @@ Config { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, }, themes: {}, plugins: { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap index fd8007af..e8d8bccb 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 625 +assertion_line: 696 expression: "format!(\"{:#?}\", config)" --- Config { @@ -80,6 +80,7 @@ Config { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, }, themes: {}, plugins: { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap index 97b1c8b6..790df2f6 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap @@ -7,16 +7,22 @@ Layout { tabs: [], focused_tab_index: None, template: Some( - PaneLayout { - children_split_direction: Horizontal, - name: None, - children: [], - split_size: None, - run: None, - borderless: false, - focus: None, - external_children_index: None, - }, + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), ), - floating_panes_template: [], + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap index d8fc7879..2a3e7ace 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 567 +assertion_line: 607 expression: "format!(\"{:#?}\", options)" --- Options { @@ -24,4 +24,5 @@ Options { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap index d2391edd..2b07b2f9 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap @@ -1,5 +1,6 @@ --- source: zellij-utils/src/setup.rs +assertion_line: 707 expression: "format!(\"{:#?}\", config)" --- Config { @@ -35,6 +36,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -362,6 +377,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -691,6 +720,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -977,6 +1020,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -1040,6 +1085,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1298,6 +1357,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1496,6 +1569,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1773,6 +1860,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1971,6 +2072,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2166,6 +2281,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2366,6 +2495,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2594,6 +2737,11 @@ Config { None, ), ], + Char( + 'p', + ): [ + MovePaneBackwards, + ], Alt( Char( '+', @@ -2624,6 +2772,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2816,6 +2978,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3005,6 +3181,11 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + NextSwapLayout, + ], Char( '"', ): [ @@ -3052,6 +3233,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -3169,6 +3352,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3343,6 +3540,7 @@ Config { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, }, themes: {}, plugins: { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index a69286fe..13e440e0 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -1,5 +1,6 @@ --- source: zellij-utils/src/setup.rs +assertion_line: 721 expression: "format!(\"{:#?}\", config)" --- Config { @@ -35,6 +36,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -362,6 +377,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -691,6 +720,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -977,6 +1020,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -1040,6 +1085,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1298,6 +1357,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1496,6 +1569,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1773,6 +1860,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1971,6 +2072,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2166,6 +2281,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2366,6 +2495,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2594,6 +2737,11 @@ Config { None, ), ], + Char( + 'p', + ): [ + MovePaneBackwards, + ], Alt( Char( '+', @@ -2624,6 +2772,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2816,6 +2978,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3005,6 +3181,11 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + NextSwapLayout, + ], Char( '"', ): [ @@ -3052,6 +3233,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -3169,6 +3352,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3343,6 +3540,7 @@ Config { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, }, themes: { "other-theme-from-config": Theme { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index dc086ff0..072f2182 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -1,5 +1,6 @@ --- source: zellij-utils/src/setup.rs +assertion_line: 693 expression: "format!(\"{:#?}\", config)" --- Config { @@ -35,6 +36,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -362,6 +377,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -691,6 +720,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -977,6 +1020,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -1040,6 +1085,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1298,6 +1357,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1496,6 +1569,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1773,6 +1860,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -1971,6 +2072,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2166,6 +2281,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2366,6 +2495,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2594,6 +2737,11 @@ Config { None, ), ], + Char( + 'p', + ): [ + MovePaneBackwards, + ], Alt( Char( '+', @@ -2624,6 +2772,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -2816,6 +2978,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3005,6 +3181,11 @@ Config { Normal, ), ], + Char( + ' ', + ): [ + NextSwapLayout, + ], Char( '"', ): [ @@ -3052,6 +3233,8 @@ Config { None, [], None, + None, + None, ), SwitchToMode( Normal, @@ -3169,6 +3352,20 @@ Config { None, ), ], + Alt( + Char( + '[', + ), + ): [ + PreviousSwapLayout, + ], + Alt( + Char( + ']', + ), + ): [ + NextSwapLayout, + ], Alt( Char( 'h', @@ -3343,6 +3540,7 @@ Config { scrollback_editor: None, session_name: None, attach_to_session: None, + auto_layout: None, }, themes: {}, plugins: {