feat(ui): swap layouts and stacked panes (#2167)
* relayout working with hard coded layout * work * refactor(layout): PaneLayout => TiledPaneLayout * tests passing * tests passing * tests passing * stacked panes and passing tests * tests for stacked panes * refactor(panes): stacked panes * fix: focusing into stacked panes from the left/right * fix(layouts): handle stacked layouts in the middle of the screen * fix(pane-stack): focus correctly when coming to stack from above/below * fix(stacked-panes): resize stack * fix(stacked-panes): focus with mouse * fix(stacked-panes): focus next pane * fix(layout-applier): sane focus order * fix(stacked-panes): better titles for one-liners * fix(stacked-panes): handle moving pane location in stack * fix(relayout): properly calculate display area * fix(relayout): properly calculate rounding errors * fix(stacked-panes): properly handle closing a pane near a stack * fix(swap-layouts): adjust swap layout sort order * feat(swap-layouts): ui + ux * fix(swap-layouts): include base layout * refactor(layout): remove unused method * fix(swap-layouts): respect pane contents and focus * work * fix(swap-layouts): load swap layouts from external file * fix(swap-layouts): properly truncate layout children * fix(stacked-panes): allow stacked panes to become fullscreen * fix(swap-layouts): work with multiple tabs * fix(swap-layouts): embed/eject panes properly with auto-layout * fix(stacked-panes): close last pane in stack * fix(stacked-panes): move focus for all clients in stack * fix(floating-panes): set layout damaged when moving panes * fix(relayout): move out of unfitting layout when resizing whole tab * fix(ui): background color for swap layout indicator * fix(keybinds): add switch next layout in tmux * fix(ui): swap layout indication in compact layout * fix(compact): correct swap constraint * fix(tests): tmux swap config shortcut * fix(resizes): cache resizes so as not to confuse panes (eg. vim) with multiple resizes that it debounces weirdly * feat(cli): dump swap layouts * fix(ui): stacked panes without pane frames * fix(ux): move pane forward/backwards also with floating panes * refactor(lint): remove unused stuff * refactor(tab): move swap layouts to separate file * style(fmt): rustfmt * style(fmt): rustfmt * refactor(panes): various cleanups * chore(deps): upgrade termwiz to get alt left-bracket * fix(assets): merge conflicts of binary files * style(fmt): rustfmt * style(clippy): no thank you! * chore(repo): remove garbage file
This commit is contained in:
parent
1517036c24
commit
f1ff272b0b
204 changed files with 16278 additions and 2904 deletions
|
|
@ -256,6 +256,8 @@ pub fn tab_line(
|
|||
palette: Palette,
|
||||
capabilities: PluginCapabilities,
|
||||
mode: InputMode,
|
||||
active_swap_layout_name: &Option<String>,
|
||||
is_swap_layout_dirty: bool,
|
||||
) -> Vec<LinePart> {
|
||||
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<String>,
|
||||
is_swap_layout_damaged: bool,
|
||||
input_mode: InputMode,
|
||||
palette: &Palette,
|
||||
separator: &str,
|
||||
) -> Option<LinePart> {
|
||||
match swap_layout_name {
|
||||
Some(swap_layout_name) => {
|
||||
let mut swap_layout_name = format!(" {} ", swap_layout_name);
|
||||
swap_layout_name.make_ascii_uppercase();
|
||||
let 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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ impl ZellijPlugin for State {
|
|||
}
|
||||
let mut all_tabs: Vec<LinePart> = 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;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use ansi_term::ANSIStrings;
|
||||
use ansi_term::{unstyled_len, ANSIStrings};
|
||||
use zellij_tile::prelude::actions::Action;
|
||||
use zellij_tile::prelude::*;
|
||||
|
||||
use crate::color_elements;
|
||||
use crate::{action_key, get_common_modifier, TO_NORMAL};
|
||||
use crate::{
|
||||
action_key, action_key_group, get_common_modifier, style_key_with_modifier, TO_NORMAL,
|
||||
};
|
||||
use crate::{ColoredElements, LinePart};
|
||||
|
||||
struct KeyShortcut {
|
||||
|
|
@ -232,6 +234,102 @@ fn key_indicators(
|
|||
line_part
|
||||
}
|
||||
|
||||
fn swap_layout_keycode(mode_info: &ModeInfo, palette: &Palette) -> LinePart {
|
||||
let mode_keybinds = mode_info.get_mode_keybinds();
|
||||
let prev_next_keys = action_key_group(
|
||||
&mode_keybinds,
|
||||
&[&[Action::PreviousSwapLayout], &[Action::NextSwapLayout]],
|
||||
);
|
||||
let prev_next_keys_indicator =
|
||||
style_key_with_modifier(&prev_next_keys, palette, Some(palette.black));
|
||||
let keycode = ANSIStrings(&prev_next_keys_indicator);
|
||||
let len = unstyled_len(&keycode);
|
||||
let part = keycode.to_string();
|
||||
LinePart { part, len }
|
||||
}
|
||||
|
||||
fn swap_layout_status(
|
||||
max_len: usize,
|
||||
swap_layout_name: &Option<String>,
|
||||
is_swap_layout_damaged: bool,
|
||||
mode_info: &ModeInfo,
|
||||
colored_elements: ColoredElements,
|
||||
palette: &Palette,
|
||||
separator: &str,
|
||||
) -> Option<LinePart> {
|
||||
match swap_layout_name {
|
||||
Some(swap_layout_name) => {
|
||||
let mut swap_layout_name = format!(" {} ", swap_layout_name);
|
||||
swap_layout_name.make_ascii_uppercase();
|
||||
let keycode = swap_layout_keycode(mode_info, palette);
|
||||
let swap_layout_name_len = swap_layout_name.len() + 3; // 2 for the arrow separators, one for the screen end buffer
|
||||
//
|
||||
macro_rules! style_swap_layout_indicator {
|
||||
($style_name:ident) => {{
|
||||
(
|
||||
colored_elements
|
||||
.$style_name
|
||||
.prefix_separator
|
||||
.paint(separator),
|
||||
colored_elements
|
||||
.$style_name
|
||||
.styled_text
|
||||
.paint(&swap_layout_name),
|
||||
colored_elements
|
||||
.$style_name
|
||||
.suffix_separator
|
||||
.paint(separator),
|
||||
)
|
||||
}};
|
||||
}
|
||||
let (prefix_separator, swap_layout_name, suffix_separator) =
|
||||
if mode_info.mode == InputMode::Locked {
|
||||
style_swap_layout_indicator!(disabled)
|
||||
} else if is_swap_layout_damaged {
|
||||
style_swap_layout_indicator!(unselected)
|
||||
} else {
|
||||
style_swap_layout_indicator!(selected)
|
||||
};
|
||||
let swap_layout_indicator = format!(
|
||||
"{}{}{}",
|
||||
prefix_separator, swap_layout_name, suffix_separator
|
||||
);
|
||||
let (part, full_len) = if mode_info.mode == InputMode::Locked {
|
||||
(
|
||||
format!("{}", swap_layout_indicator),
|
||||
swap_layout_name_len, // 1 is the space between
|
||||
)
|
||||
} else {
|
||||
(
|
||||
format!(
|
||||
"{}{}{}{}",
|
||||
keycode,
|
||||
colored_elements.superkey_prefix.paint(" "),
|
||||
swap_layout_indicator,
|
||||
colored_elements.superkey_prefix.paint(" ")
|
||||
),
|
||||
keycode.len + swap_layout_name_len + 1, // 1 is the space between
|
||||
)
|
||||
};
|
||||
let short_len = swap_layout_name_len + 1; // 1 is the space between
|
||||
if full_len <= max_len {
|
||||
Some(LinePart {
|
||||
part,
|
||||
len: full_len,
|
||||
})
|
||||
} else if short_len <= max_len && mode_info.mode != InputMode::Locked {
|
||||
Some(LinePart {
|
||||
part: swap_layout_indicator,
|
||||
len: short_len,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the keybindings for switching `InputMode`s and `Quit` visible in status bar.
|
||||
///
|
||||
/// Return a Vector of `Key`s where each `Key` is a shortcut to switch to some `InputMode` or Quit
|
||||
|
|
@ -351,7 +449,12 @@ fn get_key_shortcut_for_mode<'a>(
|
|||
None
|
||||
}
|
||||
|
||||
pub fn first_line(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
pub fn first_line(
|
||||
help: &ModeInfo,
|
||||
tab_info: Option<&TabInfo>,
|
||||
max_len: usize,
|
||||
separator: &str,
|
||||
) -> LinePart {
|
||||
let supports_arrow_fonts = !help.capabilities.arrow_fonts;
|
||||
let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts);
|
||||
let binds = &help.get_mode_keybinds();
|
||||
|
|
@ -432,7 +535,32 @@ pub fn first_line(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart
|
|||
));
|
||||
}
|
||||
|
||||
key_indicators(max_len, &default_keys, colored_elements, separator, help)
|
||||
let mut key_indicators =
|
||||
key_indicators(max_len, &default_keys, colored_elements, separator, help);
|
||||
if key_indicators.len < max_len {
|
||||
if let Some(tab_info) = tab_info {
|
||||
let mut remaining_space = max_len - key_indicators.len;
|
||||
if let Some(swap_layout_status) = swap_layout_status(
|
||||
remaining_space,
|
||||
&tab_info.active_swap_layout_name,
|
||||
tab_info.is_swap_layout_dirty,
|
||||
help,
|
||||
colored_elements,
|
||||
&help.style.colors,
|
||||
separator,
|
||||
) {
|
||||
remaining_space -= swap_layout_status.len;
|
||||
for _ in 0..remaining_space {
|
||||
key_indicators.part.push_str(
|
||||
&ANSIStrings(&[colored_elements.superkey_prefix.paint(" ")]).to_string(),
|
||||
);
|
||||
key_indicators.len += 1;
|
||||
}
|
||||
key_indicators.append(&swap_layout_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
key_indicators
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -735,7 +863,7 @@ mod tests {
|
|||
..ModeInfo::default()
|
||||
};
|
||||
|
||||
let ret = first_line(&mode_info, 500, ">");
|
||||
let ret = first_line(&mode_info, None, 500, ">");
|
||||
let ret = unstyle(ret);
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -759,7 +887,7 @@ mod tests {
|
|||
..ModeInfo::default()
|
||||
};
|
||||
|
||||
let ret = first_line(&mode_info, 500, ">");
|
||||
let ret = first_line(&mode_info, None, 500, ">");
|
||||
let ret = unstyle(ret);
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -785,7 +913,7 @@ mod tests {
|
|||
..ModeInfo::default()
|
||||
};
|
||||
|
||||
let ret = first_line(&mode_info, 500, ">");
|
||||
let ret = first_line(&mode_info, None, 500, ">");
|
||||
let ret = unstyle(ret);
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -812,7 +940,7 @@ mod tests {
|
|||
..ModeInfo::default()
|
||||
};
|
||||
|
||||
let ret = first_line(&mode_info, 50, ">");
|
||||
let ret = first_line(&mode_info, None, 50, ">");
|
||||
let ret = unstyle(ret);
|
||||
|
||||
assert_eq!(ret, " Ctrl + >> a >> b >> c >> d >> e >".to_string());
|
||||
|
|
@ -833,7 +961,7 @@ mod tests {
|
|||
..ModeInfo::default()
|
||||
};
|
||||
|
||||
let ret = first_line(&mode_info, 30, "");
|
||||
let ret = first_line(&mode_info, None, 30, "");
|
||||
let ret = unstyle(ret);
|
||||
|
||||
assert_eq!(ret, " Ctrl + a b c ".to_string());
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@ pub struct LinePart {
|
|||
len: usize,
|
||||
}
|
||||
|
||||
impl LinePart {
|
||||
pub fn append(&mut self, to_append: &LinePart) {
|
||||
self.part.push_str(&to_append.part);
|
||||
self.len += to_append.len;
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LinePart {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
write!(f, "{}", self.part)
|
||||
|
|
@ -236,7 +243,8 @@ impl ZellijPlugin for State {
|
|||
""
|
||||
};
|
||||
|
||||
let first_line = first_line(&self.mode_info, cols, separator);
|
||||
let active_tab = self.tabs.iter().find(|t| t.active);
|
||||
let first_line = first_line(&self.mode_info, active_tab, cols, separator);
|
||||
let second_line = self.second_line(cols);
|
||||
|
||||
let background = match self.mode_info.style.colors.theme_hue {
|
||||
|
|
@ -396,7 +404,11 @@ pub fn action_key_group(keymap: &[(Key, Vec<Action>)], actions: &[&[Action]]) ->
|
|||
///
|
||||
/// The returned Vector of [`ANSIString`] is suitable for transformation into an [`ANSIStrings`]
|
||||
/// type.
|
||||
pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIString<'static>> {
|
||||
pub fn style_key_with_modifier(
|
||||
keyvec: &[Key],
|
||||
palette: &Palette,
|
||||
background: Option<PaletteColor>,
|
||||
) -> Vec<ANSIString<'static>> {
|
||||
// Nothing to do, quit...
|
||||
if keyvec.is_empty() {
|
||||
return vec![];
|
||||
|
|
@ -419,13 +431,32 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIStr
|
|||
let painted_modifier = if modifier_str.is_empty() {
|
||||
Style::new().paint("")
|
||||
} else {
|
||||
Style::new().fg(orange_color).bold().paint(modifier_str)
|
||||
if let Some(background) = background {
|
||||
let background = palette_match!(background);
|
||||
Style::new()
|
||||
.fg(orange_color)
|
||||
.on(background)
|
||||
.bold()
|
||||
.paint(modifier_str)
|
||||
} else {
|
||||
Style::new().fg(orange_color).bold().paint(modifier_str)
|
||||
}
|
||||
};
|
||||
ret.push(painted_modifier);
|
||||
|
||||
// Prints key group start
|
||||
let group_start_str = if no_modifier { "<" } else { " + <" };
|
||||
ret.push(Style::new().fg(text_color).paint(group_start_str));
|
||||
if let Some(background) = background {
|
||||
let background = palette_match!(background);
|
||||
ret.push(
|
||||
Style::new()
|
||||
.fg(text_color)
|
||||
.on(background)
|
||||
.paint(group_start_str),
|
||||
);
|
||||
} else {
|
||||
ret.push(Style::new().fg(text_color).paint(group_start_str));
|
||||
}
|
||||
|
||||
// Prints the keys
|
||||
let key = keyvec
|
||||
|
|
@ -451,18 +482,50 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIStr
|
|||
"←↓↑→" => "",
|
||||
"←→" => "",
|
||||
"↓↑" => "",
|
||||
"[]" => "",
|
||||
_ => "|",
|
||||
};
|
||||
|
||||
for (idx, key) in key.iter().enumerate() {
|
||||
if idx > 0 && !key_separator.is_empty() {
|
||||
ret.push(Style::new().fg(text_color).paint(key_separator));
|
||||
if let Some(background) = background {
|
||||
let background = palette_match!(background);
|
||||
ret.push(
|
||||
Style::new()
|
||||
.fg(text_color)
|
||||
.on(background)
|
||||
.paint(key_separator),
|
||||
);
|
||||
} else {
|
||||
ret.push(Style::new().fg(text_color).paint(key_separator));
|
||||
}
|
||||
}
|
||||
if let Some(background) = background {
|
||||
let background = palette_match!(background);
|
||||
ret.push(
|
||||
Style::new()
|
||||
.fg(green_color)
|
||||
.on(background)
|
||||
.bold()
|
||||
.paint(key.clone()),
|
||||
);
|
||||
} else {
|
||||
ret.push(Style::new().fg(green_color).bold().paint(key.clone()));
|
||||
}
|
||||
ret.push(Style::new().fg(green_color).bold().paint(key.clone()));
|
||||
}
|
||||
|
||||
let group_end_str = ">";
|
||||
ret.push(Style::new().fg(text_color).paint(group_end_str));
|
||||
if let Some(background) = background {
|
||||
let background = palette_match!(background);
|
||||
ret.push(
|
||||
Style::new()
|
||||
.fg(text_color)
|
||||
.on(background)
|
||||
.paint(group_end_str),
|
||||
);
|
||||
} else {
|
||||
ret.push(Style::new().fg(text_color).paint(group_end_str));
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
|
@ -623,7 +686,7 @@ pub mod tests {
|
|||
let keyvec = vec![Key::Char('a'), Key::Char('b'), Key::Char('c')];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<a|b|c>".to_string())
|
||||
|
|
@ -639,7 +702,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<hjkl>".to_string())
|
||||
|
|
@ -656,7 +719,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<h|k|j|l>".to_string())
|
||||
|
|
@ -672,7 +735,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<←↓↑→>".to_string())
|
||||
|
|
@ -683,7 +746,7 @@ pub mod tests {
|
|||
let keyvec = vec![Key::Char('←'), Key::Char('→')];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<←→>".to_string())
|
||||
|
|
@ -694,7 +757,7 @@ pub mod tests {
|
|||
let keyvec = vec![Key::Char('↓'), Key::Char('↑')];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<↓↑>".to_string())
|
||||
|
|
@ -710,7 +773,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "Ctrl + <a|b|c|d>".to_string())
|
||||
|
|
@ -726,7 +789,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "Alt + <a|b|c|d>".to_string())
|
||||
|
|
@ -742,7 +805,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "Alt + <←↓↑→>".to_string())
|
||||
|
|
@ -757,7 +820,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "<Alt+a|Ctrl+b|c>".to_string())
|
||||
|
|
@ -780,7 +843,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -794,7 +857,7 @@ pub mod tests {
|
|||
let keyvec = vec![Key::Ctrl('\n'), Key::Ctrl(' '), Key::Ctrl('\t')];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "Ctrl + <ENTER|SPACE|TAB>".to_string())
|
||||
|
|
@ -809,7 +872,7 @@ pub mod tests {
|
|||
];
|
||||
let palette = get_palette();
|
||||
|
||||
let ret = style_key_with_modifier(&keyvec, &palette);
|
||||
let ret = style_key_with_modifier(&keyvec, &palette, None);
|
||||
let ret = unstyle(&ANSIStrings(&ret));
|
||||
|
||||
assert_eq!(ret, "Alt + <ENTER|SPACE|TAB>".to_string())
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ fn full_length_shortcut(
|
|||
|
||||
let separator = if is_first_shortcut { " " } else { " / " };
|
||||
let mut bits: Vec<ANSIString> = vec![Style::new().fg(text_color).paint(separator)];
|
||||
bits.extend(style_key_with_modifier(&key, &palette));
|
||||
bits.extend(style_key_with_modifier(&key, &palette, None));
|
||||
bits.push(
|
||||
Style::new()
|
||||
.fg(text_color)
|
||||
|
|
@ -179,7 +179,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
|||
|
||||
vec![
|
||||
(s("Move focus"), s("Move"), focus_keys),
|
||||
(s("New"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None), TO_NORMAL])),
|
||||
(s("New"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])),
|
||||
(s("Close"), s("Close"), action_key(&km, &[A::CloseTab, TO_NORMAL])),
|
||||
(s("Rename"), s("Rename"),
|
||||
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
|
||||
|
|
@ -210,6 +210,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
|||
&[Action::MovePane(Some(Dir::Left))], &[Action::MovePane(Some(Dir::Down))],
|
||||
&[Action::MovePane(Some(Dir::Up))], &[Action::MovePane(Some(Dir::Right))]])),
|
||||
(s("Next pane"), s("Next"), action_key(&km, &[Action::MovePane(None)])),
|
||||
(s("Previous pane"), s("Previous"), action_key(&km, &[Action::MovePaneBackwards])),
|
||||
]} else if mi.mode == IM::Scroll { vec![
|
||||
(s("Scroll"), s("Scroll"),
|
||||
action_key_group(&km, &[&[Action::ScrollDown], &[Action::ScrollUp]])),
|
||||
|
|
@ -253,7 +254,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
|
|||
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])),
|
||||
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])),
|
||||
(s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
|
||||
(s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None), TO_NORMAL])),
|
||||
(s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])),
|
||||
(s("Rename tab"), s("Rename"),
|
||||
action_key(&km, &[A::SwitchToMode(IM::RenameTab), A::TabNameInput(vec![0])])),
|
||||
(s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
|
||||
|
|
|
|||
|
|
@ -93,8 +93,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
|
|||
}
|
||||
|
||||
let mut bits = vec![];
|
||||
bits.extend(style_key_with_modifier(&to_pane, &help.style.colors));
|
||||
bits.extend(style_key_with_modifier(&to_pane, &help.style.colors, None));
|
||||
bits.push(Style::new().paint(", "));
|
||||
bits.extend(style_key_with_modifier(&pane_frames, &help.style.colors));
|
||||
bits.extend(style_key_with_modifier(
|
||||
&pane_frames,
|
||||
&help.style.colors,
|
||||
None,
|
||||
));
|
||||
bits
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,8 +83,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
|
|||
}
|
||||
|
||||
let mut bits = vec![];
|
||||
bits.extend(style_key_with_modifier(&to_pane, &help.style.colors));
|
||||
bits.extend(style_key_with_modifier(&to_pane, &help.style.colors, None));
|
||||
bits.push(Style::new().paint(", "));
|
||||
bits.extend(style_key_with_modifier(&edit_buffer, &help.style.colors));
|
||||
bits.extend(style_key_with_modifier(
|
||||
&edit_buffer,
|
||||
&help.style.colors,
|
||||
None,
|
||||
));
|
||||
bits
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,11 +62,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
|
|||
}
|
||||
|
||||
let mut bits = vec![];
|
||||
bits.extend(style_key_with_modifier(&to_pane, &help.style.colors));
|
||||
bits.extend(style_key_with_modifier(&to_pane, &help.style.colors, None));
|
||||
bits.push(Style::new().paint(", "));
|
||||
bits.extend(style_key_with_modifier(
|
||||
&floating_toggle,
|
||||
&help.style.colors,
|
||||
None,
|
||||
));
|
||||
bits
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
|
|||
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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -61,8 +61,12 @@ fn add_keybinds(help: &ModeInfo) -> Vec<ANSIString> {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ expression: last_snapshot
|
|||
│ ││ │
|
||||
│ ││ │
|
||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ expression: second_runner_snapshot
|
|||
│ ││ │
|
||||
│ ││ │
|
||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
<←→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <TAB> Toggle / <ENTER> Select pane
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ expression: first_runner_snapshot
|
|||
│ ││ │
|
||||
│ ││ │
|
||||
└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <n> / Alt + <←↓↑→> or Alt + <hjkl> / Alt + <+|->
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
<↓↑> Scroll / <PgDn|PgUp> Scroll / <d|u> Scroll / <e> Edit / <s> Search / <ENTER> Select
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
<↓↑> Scroll / <PgDn|PgUp> Scroll / <d|u> Scroll / <e> Edit / <s> Search / <ENTER> Select
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: src/tests/e2e/cases.rs
|
||||
assertion_line: 1998
|
||||
assertion_line: 1990
|
||||
expression: last_snapshot
|
||||
---
|
||||
Zellij (e2e-test) Tab #1
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
(FLOATING PANES VISIBLE): Press Ctrl+p, <w> to hide.
|
||||
(FLOATING PANES VISIBLE): Press Ctrl+p, <w> to hide.
|
||||
|
|
|
|||
|
|
@ -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 + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT
|
||||
Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT BASE
|
||||
(FULLSCREEN): + 1 hidden panes
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ pub(crate) struct SessionMetaData {
|
|||
pub capabilities: PluginCapabilities,
|
||||
pub client_attributes: ClientAttributes,
|
||||
pub default_shell: Option<TerminalAction>,
|
||||
pub layout: Box<Layout>,
|
||||
screen_thread: Option<thread::JoinHandle<()>>,
|
||||
pty_thread: Option<thread::JoinHandle<()>>,
|
||||
plugin_thread: Option<thread::JoinHandle<()>>,
|
||||
|
|
@ -346,7 +347,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, 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<dyn ServerOsApi>, 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<dyn ServerOsApi>, 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<dyn ServerOsApi>, 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),
|
||||
|
|
|
|||
|
|
@ -396,10 +396,11 @@ pub struct ServerOsInputOutput {
|
|||
orig_termios: Arc<Mutex<termios::Termios>>,
|
||||
client_senders: Arc<Mutex<HashMap<ClientId, ClientSender>>>,
|
||||
terminal_id_to_raw_fd: Arc<Mutex<BTreeMap<u32, Option<RawFd>>>>, // 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<Mutex<Option<BTreeMap<u32, (u16, u16)>>>>, // <terminal_id, (cols, rows)>
|
||||
}
|
||||
|
||||
// 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<dyn Fn(PaneId, Option<i32>, 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<dyn ServerOsApi> {
|
||||
|
|
@ -767,6 +787,7 @@ pub fn get_server_os_input() -> Result<ServerOsInputOutput, nix::Error> {
|
|||
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)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ClientId, PaneId>,
|
||||
os_api: Box<dyn ServerOsApi>,
|
||||
|
|
|
|||
|
|
@ -703,6 +703,56 @@ impl<'a> FloatingPaneGrid<'a> {
|
|||
.copied();
|
||||
next_index
|
||||
}
|
||||
pub fn next_selectable_pane_id(&self, current_pane_id: &PaneId) -> Option<PaneId> {
|
||||
let panes = self.panes.borrow();
|
||||
let mut panes: Vec<(PaneId, &&mut Box<dyn Pane>)> = 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<PaneId> {
|
||||
let panes = self.panes.borrow();
|
||||
let mut panes: Vec<(PaneId, &&mut Box<dyn Pane>)> = 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<PaneGeom> {
|
||||
let panes = self.panes.borrow();
|
||||
let pane_geoms: Vec<PaneGeom> = 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);
|
||||
|
|
|
|||
|
|
@ -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<PaneId> {
|
||||
self.active_panes.get(&client_id).copied()
|
||||
}
|
||||
pub fn active_pane_id_or_focused_pane_id(&self, client_id: Option<ClientId>) -> Option<PaneId> {
|
||||
// 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<dyn ServerOsApi>,
|
||||
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<Item = (&PaneId, &Box<dyn Pane>)> {
|
||||
self.panes.iter()
|
||||
}
|
||||
pub fn visible_panes_count(&self) -> usize {
|
||||
self.panes.len()
|
||||
}
|
||||
pub fn drain(&mut self) -> BTreeMap<PaneId, Box<dyn Pane>> {
|
||||
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<ClientId> = 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<dyn ServerOsApi>, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ClientId, PaneFrame>,
|
||||
borderless: bool,
|
||||
pane_frame_color_override: Option<(PaletteColor, Option<String>)>,
|
||||
invoked_with: Option<Run>,
|
||||
}
|
||||
|
||||
impl PluginPane {
|
||||
|
|
@ -80,6 +82,7 @@ impl PluginPane {
|
|||
link_handler: Rc<RefCell<LinkHandler>>,
|
||||
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
|
||||
style: Style,
|
||||
invoked_with: Option<Run>,
|
||||
) -> 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<Run> {
|
||||
&self.invoked_with
|
||||
}
|
||||
fn set_title(&mut self, title: String) {
|
||||
self.pane_title = title;
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginPane {
|
||||
|
|
|
|||
|
|
@ -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<String>, // 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<String>)>,
|
||||
invoked_with: Option<Run>,
|
||||
}
|
||||
|
||||
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<Run> {
|
||||
&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<RefCell<Palette>>,
|
||||
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
|
||||
initial_pane_title: Option<String>,
|
||||
invoked_with: Option<Run>,
|
||||
) -> 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) {
|
||||
|
|
|
|||
|
|
@ -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<ClientId> =
|
||||
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<ClientId> =
|
||||
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::<ZellijError>() {
|
||||
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::<ZellijError>() {
|
||||
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<ClientId> =
|
||||
{ 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<ClientId> =
|
||||
{ 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<ClientId> =
|
||||
{ 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<PaneId, Box<dyn Pane>> {
|
||||
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<ClientId> = self
|
||||
.active_panes
|
||||
|
|
|
|||
|
|
@ -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<Vec<Span>>;
|
|||
impl<'a> PaneResizer<'a> {
|
||||
pub fn new(panes: Rc<RefCell<HashMap<PaneId, &'a mut Box<dyn Pane>>>>) -> 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<Span>) -> 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<Span> {
|
||||
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<cassowary::Constrain
|
|||
let full_size = spans
|
||||
.iter()
|
||||
.fold(Expression::from_constant(0.0), |acc, s| acc + s.size_var);
|
||||
constraints.insert(full_size | EQ(REQUIRED) | space as f64);
|
||||
constraints.insert(full_size.clone() | EQ(REQUIRED) | space as f64);
|
||||
|
||||
// Try to maintain ratios and lock non-flexible sizes
|
||||
for span in spans {
|
||||
|
|
|
|||
630
zellij-server/src/panes/tiled_panes/stacked_panes.rs
Normal file
630
zellij-server/src/panes/tiled_panes/stacked_panes.rs
Normal file
|
|
@ -0,0 +1,630 @@
|
|||
use crate::{panes::PaneId, tab::Pane};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
use zellij_utils::{
|
||||
errors::prelude::*,
|
||||
pane_size::{Dimension, PaneGeom},
|
||||
};
|
||||
|
||||
pub struct StackedPanes<'a> {
|
||||
panes: Rc<RefCell<HashMap<PaneId, &'a mut Box<dyn Pane>>>>,
|
||||
}
|
||||
|
||||
impl<'a> StackedPanes<'a> {
|
||||
pub fn new(panes: Rc<RefCell<HashMap<PaneId, &'a mut Box<dyn Pane>>>>) -> Self {
|
||||
StackedPanes { panes }
|
||||
}
|
||||
pub fn new_from_btreemap(
|
||||
panes: impl IntoIterator<Item = (&'a PaneId, &'a mut Box<dyn Pane>)>,
|
||||
panes_to_hide: &HashSet<PaneId>,
|
||||
) -> 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<PaneId> {
|
||||
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<PaneGeom> {
|
||||
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<usize> {
|
||||
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<bool> {
|
||||
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<Vec<(PaneId, PaneGeom)>> {
|
||||
// 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<dyn Pane>,
|
||||
) -> Result<usize> {
|
||||
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<usize> {
|
||||
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<bool> {
|
||||
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<PaneId>, HashSet<PaneId>)> {
|
||||
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<PaneId> = {
|
||||
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<bool> {
|
||||
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<bool> {
|
||||
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<bool> {
|
||||
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<bool> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PaneGeom> {
|
||||
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<bool> {
|
||||
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<Vec<PaneId>> {
|
||||
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<Vec<PaneId>> {
|
||||
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<PaneId> = result.iter().map(|t| t.pid()).collect();
|
||||
let pane_ids: Vec<PaneId> = 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<PaneId> {
|
||||
|
|
@ -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<PaneId> {
|
||||
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<dyn Pane>)> = 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<PaneId> {
|
||||
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<dyn Pane>)> = 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<PaneId> {
|
||||
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<usize> {
|
||||
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<usize>,
|
||||
) -> Option<(PaneId, SplitDirection)> {
|
||||
let panes = self.panes.borrow();
|
||||
let pane_sequence: Vec<(&PaneId, &&mut Box<dyn Pane>)> =
|
||||
panes.iter().filter(|(_, p)| p.selectable()).collect();
|
||||
let pane_sequence: Vec<(&PaneId, &&mut Box<dyn Pane>)> = 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| {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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<TerminalAction>,
|
||||
Option<PaneLayout>,
|
||||
Vec<FloatingPanesLayout>,
|
||||
Option<TiledPaneLayout>,
|
||||
Vec<FloatingPaneLayout>,
|
||||
Option<String>, // 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<Option<Run>> = floating_panes_layout
|
||||
.iter()
|
||||
.map(|f| f.run.clone())
|
||||
|
|
|
|||
|
|
@ -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<TerminalAction>,
|
||||
Option<PaneLayout>,
|
||||
Vec<FloatingPanesLayout>,
|
||||
Option<TiledPaneLayout>,
|
||||
Vec<FloatingPaneLayout>,
|
||||
Option<String>,
|
||||
usize, // tab_index
|
||||
HashMap<RunPluginLocation, Vec<u32>>, // plugin_ids
|
||||
|
|
@ -575,8 +575,8 @@ impl Pty {
|
|||
}
|
||||
pub fn spawn_terminals_for_layout(
|
||||
&mut self,
|
||||
layout: PaneLayout,
|
||||
floating_panes_layout: Vec<FloatingPanesLayout>,
|
||||
layout: TiledPaneLayout,
|
||||
floating_panes_layout: Vec<FloatingPaneLayout>,
|
||||
default_shell: Option<TerminalAction>,
|
||||
plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||
tab_index: usize,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TerminalAction>,
|
||||
Option<PaneLayout>,
|
||||
Vec<FloatingPanesLayout>,
|
||||
Option<TiledPaneLayout>,
|
||||
Vec<FloatingPaneLayout>,
|
||||
Option<String>,
|
||||
(Vec<SwapTiledLayout>, Vec<SwapFloatingLayout>), // swap layouts
|
||||
ClientId,
|
||||
),
|
||||
ApplyLayout(
|
||||
PaneLayout,
|
||||
Vec<FloatingPanesLayout>,
|
||||
TiledPaneLayout,
|
||||
Vec<FloatingPaneLayout>,
|
||||
Vec<(u32, HoldForCommand)>, // new pane pids
|
||||
Vec<(u32, HoldForCommand)>, // new floating pane pids
|
||||
HashMap<RunPluginLocation, Vec<u32>>,
|
||||
|
|
@ -196,7 +200,12 @@ pub enum ScreenInstruction {
|
|||
ToggleActiveSyncTab(ClientId),
|
||||
CloseTab(ClientId),
|
||||
GoToTab(u32, Option<ClientId>), // this Option is a hacky workaround, please do not copy this behaviour
|
||||
GoToTabName(String, bool, Option<ClientId>),
|
||||
GoToTabName(
|
||||
String,
|
||||
(Vec<SwapTiledLayout>, Vec<SwapFloatingLayout>), // swap layouts
|
||||
bool,
|
||||
Option<ClientId>,
|
||||
),
|
||||
ToggleTab(ClientId),
|
||||
UpdateTabName(Vec<u8>, ClientId),
|
||||
UndoRenameTab(ClientId),
|
||||
|
|
@ -231,6 +240,8 @@ pub enum ScreenInstruction {
|
|||
SearchToggleWrap(ClientId),
|
||||
AddRedPaneFrameColorOverride(Vec<PaneId>, Option<String>), // Option<String> => optional error text
|
||||
ClearPaneFrameColorOverride(Vec<PaneId>),
|
||||
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<usize>,
|
||||
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<SwapTiledLayout>, Vec<SwapFloatingLayout>),
|
||||
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<FloatingPanesLayout>,
|
||||
layout: TiledPaneLayout,
|
||||
floating_panes_layout: Vec<FloatingPaneLayout>,
|
||||
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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<FloatingPanesLayout>,
|
||||
layout: TiledPaneLayout,
|
||||
floating_panes_layout: Vec<FloatingPaneLayout>,
|
||||
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
mut new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||
|
|
@ -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<ClientId>,
|
||||
) -> 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<PaneId> = 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<RunPluginLocation, Vec<u32>>,
|
||||
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<PaneId> = 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<FloatingPanesLayout>,
|
||||
floating_panes_layout: Vec<FloatingPaneLayout>,
|
||||
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
new_plugin_ids: &mut HashMap<RunPluginLocation, Vec<u32>>,
|
||||
layout_name: Option<String>,
|
||||
|
|
@ -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<FloatingPaneLayout>,
|
||||
refocus_pane: bool,
|
||||
client_id: Option<ClientId>,
|
||||
) -> Result<bool> {
|
||||
// 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<PaneId> = 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<dyn Pane>,
|
||||
layout: &TiledPaneLayout,
|
||||
position_and_size: Option<PaneGeom>,
|
||||
) {
|
||||
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<dyn Pane>,
|
||||
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<PaneId, Box<dyn Pane>>,
|
||||
currently_focused_pane_id: Option<PaneId>,
|
||||
}
|
||||
|
||||
impl ExistingTabState {
|
||||
pub fn new(
|
||||
existing_panes: BTreeMap<PaneId, Box<dyn Pane>>,
|
||||
currently_focused_pane_id: Option<PaneId>,
|
||||
) -> Self {
|
||||
ExistingTabState {
|
||||
existing_panes,
|
||||
currently_focused_pane_id,
|
||||
}
|
||||
}
|
||||
pub fn find_and_extract_pane(
|
||||
&mut self,
|
||||
run: &Option<Run>,
|
||||
position_and_size: &PaneGeom,
|
||||
is_focused: bool,
|
||||
default_to_closest_position: bool,
|
||||
) -> Option<Box<dyn Pane>> {
|
||||
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<PaneId> {
|
||||
self.existing_panes.keys().copied().collect()
|
||||
}
|
||||
pub fn remove_pane(&mut self, pane_id: &PaneId) -> Option<Box<dyn Pane>> {
|
||||
self.existing_panes.remove(pane_id)
|
||||
}
|
||||
fn pane_candidates(
|
||||
&self,
|
||||
run: &Option<Run>,
|
||||
position_and_size: &PaneGeom,
|
||||
default_to_closest_position: bool,
|
||||
) -> Vec<(&PaneId, &Box<dyn Pane>)> {
|
||||
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<dyn Pane>)>,
|
||||
) -> Option<PaneId> {
|
||||
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<dyn Pane>)>,
|
||||
run: &Option<Run>,
|
||||
) -> Option<PaneId> {
|
||||
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<PaneId>,
|
||||
}
|
||||
|
||||
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<bool>,
|
||||
pane: &Box<dyn Pane>,
|
||||
) {
|
||||
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<dyn ServerOsApi>,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<RefCell<HashSet<ClientId>>>,
|
||||
draw_pane_frames: bool,
|
||||
auto_layout: bool,
|
||||
pending_vte_events: HashMap<u32, Vec<VteBytes>>,
|
||||
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<RefCell<LinkHandler>>,
|
||||
|
|
@ -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<BufferedTabInstruction>, // 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<String>);
|
||||
fn clear_pane_frame_color_override(&mut self);
|
||||
fn frame_color_override(&self) -> Option<PaletteColor>;
|
||||
fn invoked_with(&self) -> &Option<Run>;
|
||||
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<RefCell<HashSet<ClientId>>>,
|
||||
session_is_mirrored: bool,
|
||||
client_id: ClientId,
|
||||
copy_options: CopyOptions,
|
||||
terminal_emulator_colors: Rc<RefCell<Palette>>,
|
||||
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
|
||||
swap_layouts: (Vec<SwapTiledLayout>, Vec<SwapFloatingLayout>),
|
||||
) -> 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<FloatingPanesLayout>,
|
||||
layout: TiledPaneLayout,
|
||||
floating_panes_layout: Vec<FloatingPaneLayout>,
|
||||
new_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
|
||||
new_plugin_ids: HashMap<RunPluginLocation, Vec<u32>>,
|
||||
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<String>, 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<ClientId>,
|
||||
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<ClientId>,
|
||||
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<ClientId>) -> 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<ClientId>,
|
||||
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<BufferedTabInstruction> =
|
||||
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<ClientId>,
|
||||
default_shell: Option<TerminalAction>,
|
||||
) -> 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<ClientId>,
|
||||
) -> Option<Box<dyn Pane>> {
|
||||
// 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;
|
||||
|
|
|
|||
284
zellij-server/src/tab/swap_layouts.rs
Normal file
284
zellij-server/src/tab/swap_layouts.rs
Normal file
|
|
@ -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<SwapTiledLayout>,
|
||||
swap_floating_layouts: Vec<SwapFloatingLayout>,
|
||||
current_floating_layout_position: usize,
|
||||
current_tiled_layout_position: usize,
|
||||
is_floating_damaged: bool,
|
||||
is_tiled_damaged: bool,
|
||||
display_area: Rc<RefCell<Size>>, // includes all panes (including eg. the status bar and tab bar in the default layout)
|
||||
}
|
||||
|
||||
impl SwapLayouts {
|
||||
pub fn new(
|
||||
swap_layouts: (Vec<SwapTiledLayout>, Vec<SwapFloatingLayout>),
|
||||
display_area: Rc<RefCell<Size>>,
|
||||
) -> 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<FloatingPaneLayout>)) {
|
||||
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<String>, 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<String>, 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<Vec<FloatingPaneLayout>> {
|
||||
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<TiledPaneLayout> {
|
||||
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<TiledPaneLayout> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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): │ <ENTER> to run, <Ctrl-c> to exit │ <ENTER> to run, <Ctrl-c> to exit │ │
|
||||
17 (C): │ │ │ │
|
||||
18 (C): │ │ │ │
|
||||
19 (C): └─────────────────────────────────────────────────└──────────────────────────────────────────────────────────┘──────────┘
|
||||
|
||||
|
|
@ -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): │ <ENTER> to run, <Ctrl-c> to exit ││ ││ <ENTER> to run, <Ctrl-c> 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
|
||||
|
||||
|
|
@ -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): └─────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └────────────────────────────────────────────────────┘└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └──────────────────────────────────────────────────────────┘└───────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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 ─────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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 ─────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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 ─────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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):
|
||||
|
||||
|
|
@ -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): │ <ENTER> to run, <Ctrl-c> to exit │ <ENTER> to run, <Ctrl-c> to exit │ │
|
||||
17 (C): │ │ │ │
|
||||
18 (C): │ │ │ │
|
||||
19 (C): └─────────────────────────────────────────────────└──────────────────────────────────────────────────────────┘──────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): │ <ENTER> to run, <Ctrl-c> to exit │
|
||||
07 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
08 (C): ┌ command1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
09 (C): │ │
|
||||
10 (C): │ Waiting to run: command1 │
|
||||
11 (C): │ │
|
||||
12 (C): │ <ENTER> to run, <Ctrl-c> to exit │
|
||||
13 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
14 (C): ┌ Pane #2 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
15 (C): │ │
|
||||
16 (C): │ │
|
||||
17 (C): │ │
|
||||
18 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
19 (C): I am a tab bar
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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 ─────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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 ──────────────────────────────────────────────────┐
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └─────────────────────────────└─────────────────────────────────────────────────────┘───────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └────────────────────────────────────────────────────────────└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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 ┐─────┐
|
||||
|
|
|
|||
|
|
@ -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 ──────────────────────────────────────┐─────────┐
|
||||
|
|
|
|||
|
|
@ -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 ┐
|
||||
|
|
|
|||
|
|
@ -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 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
|
|
|
|||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └─────────└──────────────────────────────────────────────────────────┘──────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -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): │ │ │ <ENTER> to run, <Ctrl-c> to exit │ │
|
||||
16 (C): │ └─│ │ │
|
||||
17 (C): │ │ │ │
|
||||
18 (C): │ └──────────────────────────────────────────────────────────┘ │
|
||||
19 (C): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue