fix(tab-bar): prevent active tab from being hidden (#703)
This commit is contained in:
parent
f0da6872df
commit
f2850d2931
3 changed files with 82 additions and 96 deletions
|
|
@ -8,39 +8,82 @@ fn get_current_title_len(current_title: &[LinePart]) -> usize {
|
||||||
current_title.iter().map(|p| p.len).sum()
|
current_title.iter().map(|p| p.len).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// move elements from before_active and after_active into tabs_to_render while they fit in cols
|
||||||
|
// adds collapsed_tabs to the left and right if there's left over tabs that don't fit
|
||||||
fn populate_tabs_in_tab_line(
|
fn populate_tabs_in_tab_line(
|
||||||
tabs_before_active: &mut Vec<LinePart>,
|
tabs_before_active: &mut Vec<LinePart>,
|
||||||
tabs_after_active: &mut Vec<LinePart>,
|
tabs_after_active: &mut Vec<LinePart>,
|
||||||
tabs_to_render: &mut Vec<LinePart>,
|
tabs_to_render: &mut Vec<LinePart>,
|
||||||
cols: usize,
|
cols: usize,
|
||||||
|
palette: Palette,
|
||||||
|
capabilities: PluginCapabilities,
|
||||||
) {
|
) {
|
||||||
let mut take_next_tab_from_tabs_after = true;
|
let mut middle_size = get_current_title_len(tabs_to_render);
|
||||||
|
|
||||||
|
let mut total_left = 0;
|
||||||
|
let mut total_right = 0;
|
||||||
loop {
|
loop {
|
||||||
if tabs_before_active.is_empty() && tabs_after_active.is_empty() {
|
let left_count = tabs_before_active.len();
|
||||||
|
let right_count = tabs_after_active.len();
|
||||||
|
let collapsed_left = left_more_message(left_count, palette, tab_separator(capabilities));
|
||||||
|
let collapsed_right = right_more_message(right_count, palette, tab_separator(capabilities));
|
||||||
|
|
||||||
|
let total_size = collapsed_left.len + middle_size + collapsed_right.len;
|
||||||
|
|
||||||
|
if total_size > cols {
|
||||||
|
// break and dont add collapsed tabs to tabs_to_render, they will not fit
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let current_title_len = get_current_title_len(tabs_to_render);
|
|
||||||
if current_title_len >= cols {
|
let left = if let Some(tab) = tabs_before_active.last() {
|
||||||
break;
|
tab.len
|
||||||
}
|
|
||||||
let should_take_next_tab = take_next_tab_from_tabs_after;
|
|
||||||
let can_take_next_tab = !tabs_after_active.is_empty()
|
|
||||||
&& tabs_after_active.get(0).unwrap().len + current_title_len <= cols;
|
|
||||||
let can_take_previous_tab = !tabs_before_active.is_empty()
|
|
||||||
&& tabs_before_active.last().unwrap().len + current_title_len <= cols;
|
|
||||||
if should_take_next_tab && can_take_next_tab {
|
|
||||||
let next_tab = tabs_after_active.remove(0);
|
|
||||||
tabs_to_render.push(next_tab);
|
|
||||||
take_next_tab_from_tabs_after = false;
|
|
||||||
} else if can_take_previous_tab {
|
|
||||||
let previous_tab = tabs_before_active.pop().unwrap();
|
|
||||||
tabs_to_render.insert(0, previous_tab);
|
|
||||||
take_next_tab_from_tabs_after = true;
|
|
||||||
} else if can_take_next_tab {
|
|
||||||
let next_tab = tabs_after_active.remove(0);
|
|
||||||
tabs_to_render.push(next_tab);
|
|
||||||
take_next_tab_from_tabs_after = false;
|
|
||||||
} else {
|
} else {
|
||||||
|
usize::MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
let right = if let Some(tab) = tabs_after_active.first() {
|
||||||
|
tab.len
|
||||||
|
} else {
|
||||||
|
usize::MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
// total size is shortened if the next tab to be added is the last one, as that will remove the collapsed tab
|
||||||
|
let size_by_adding_left =
|
||||||
|
left.saturating_add(total_size)
|
||||||
|
.saturating_sub(if left_count == 1 {
|
||||||
|
collapsed_left.len
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
});
|
||||||
|
let size_by_adding_right =
|
||||||
|
right
|
||||||
|
.saturating_add(total_size)
|
||||||
|
.saturating_sub(if right_count == 1 {
|
||||||
|
collapsed_right.len
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
});
|
||||||
|
|
||||||
|
let left_fits = size_by_adding_left <= cols;
|
||||||
|
let right_fits = size_by_adding_right <= cols;
|
||||||
|
// active tab is kept in the middle by adding to the side that
|
||||||
|
// has less width, or if the tab on the other side doesn' fit
|
||||||
|
if (total_left <= total_right || !right_fits) && left_fits {
|
||||||
|
// add left tab
|
||||||
|
let tab = tabs_before_active.pop().unwrap();
|
||||||
|
middle_size += tab.len;
|
||||||
|
total_left += tab.len;
|
||||||
|
tabs_to_render.insert(0, tab);
|
||||||
|
} else if right_fits {
|
||||||
|
// add right tab
|
||||||
|
let tab = tabs_after_active.remove(0);
|
||||||
|
middle_size += tab.len;
|
||||||
|
total_right += tab.len;
|
||||||
|
tabs_to_render.push(tab);
|
||||||
|
} else {
|
||||||
|
// there's either no space to add more tabs or no more tabs to add, so we're done
|
||||||
|
tabs_to_render.insert(0, collapsed_left);
|
||||||
|
tabs_to_render.push(collapsed_right);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +99,8 @@ fn left_more_message(tab_count_to_the_left: usize, palette: Palette, separator:
|
||||||
" ← +many ".to_string()
|
" ← +many ".to_string()
|
||||||
};
|
};
|
||||||
// 238
|
// 238
|
||||||
let more_text_len = more_text.chars().count() + 2; // 2 for the arrows
|
// chars length plus separator length on both sides
|
||||||
|
let more_text_len = more_text.chars().count() + 2 * separator.chars().count();
|
||||||
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
|
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
|
||||||
let more_styled_text = style!(palette.black, palette.orange)
|
let more_styled_text = style!(palette.black, palette.orange)
|
||||||
.bold()
|
.bold()
|
||||||
|
|
@ -85,7 +129,8 @@ fn right_more_message(
|
||||||
} else {
|
} else {
|
||||||
" +many → ".to_string()
|
" +many → ".to_string()
|
||||||
};
|
};
|
||||||
let more_text_len = more_text.chars().count() + 1; // 2 for the arrow
|
// chars length plus separator length on both sides
|
||||||
|
let more_text_len = more_text.chars().count() + 2 * separator.chars().count();
|
||||||
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
|
let left_separator = style!(palette.cyan, palette.orange).paint(separator);
|
||||||
let more_styled_text = style!(palette.black, palette.orange)
|
let more_styled_text = style!(palette.black, palette.orange)
|
||||||
.bold()
|
.bold()
|
||||||
|
|
@ -101,48 +146,6 @@ fn right_more_message(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_previous_tabs_msg(
|
|
||||||
tabs_before_active: &mut Vec<LinePart>,
|
|
||||||
tabs_to_render: &mut Vec<LinePart>,
|
|
||||||
title_bar: &mut Vec<LinePart>,
|
|
||||||
cols: usize,
|
|
||||||
palette: Palette,
|
|
||||||
separator: &str,
|
|
||||||
) {
|
|
||||||
while get_current_title_len(tabs_to_render)
|
|
||||||
+ left_more_message(tabs_before_active.len(), palette, separator).len
|
|
||||||
>= cols
|
|
||||||
&& !tabs_to_render.is_empty()
|
|
||||||
{
|
|
||||||
tabs_before_active.push(tabs_to_render.remove(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let left_more_message = left_more_message(tabs_before_active.len(), palette, separator);
|
|
||||||
if left_more_message.len <= cols {
|
|
||||||
title_bar.push(left_more_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_next_tabs_msg(
|
|
||||||
tabs_after_active: &mut Vec<LinePart>,
|
|
||||||
title_bar: &mut Vec<LinePart>,
|
|
||||||
cols: usize,
|
|
||||||
palette: Palette,
|
|
||||||
separator: &str,
|
|
||||||
) {
|
|
||||||
while get_current_title_len(title_bar)
|
|
||||||
+ right_more_message(tabs_after_active.len(), palette, separator).len
|
|
||||||
>= cols
|
|
||||||
&& !title_bar.is_empty()
|
|
||||||
{
|
|
||||||
tabs_after_active.insert(0, title_bar.pop().unwrap());
|
|
||||||
}
|
|
||||||
let right_more_message = right_more_message(tabs_after_active.len(), palette, separator);
|
|
||||||
if right_more_message.len < cols {
|
|
||||||
title_bar.push(right_more_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tab_line_prefix(session_name: Option<&str>, palette: Palette, cols: usize) -> Vec<LinePart> {
|
fn tab_line_prefix(session_name: Option<&str>, palette: Palette, cols: usize) -> Vec<LinePart> {
|
||||||
let prefix_text = " Zellij ".to_string();
|
let prefix_text = " Zellij ".to_string();
|
||||||
|
|
||||||
|
|
@ -184,7 +187,6 @@ pub fn tab_line(
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
capabilities: PluginCapabilities,
|
capabilities: PluginCapabilities,
|
||||||
) -> Vec<LinePart> {
|
) -> Vec<LinePart> {
|
||||||
let mut tabs_to_render = Vec::new();
|
|
||||||
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
|
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
|
||||||
let mut tabs_before_active = all_tabs;
|
let mut tabs_before_active = all_tabs;
|
||||||
let active_tab = if !tabs_after_active.is_empty() {
|
let active_tab = if !tabs_after_active.is_empty() {
|
||||||
|
|
@ -194,38 +196,22 @@ pub fn tab_line(
|
||||||
};
|
};
|
||||||
let mut prefix = tab_line_prefix(session_name, palette, cols);
|
let mut prefix = tab_line_prefix(session_name, palette, cols);
|
||||||
let prefix_len = get_current_title_len(&prefix);
|
let prefix_len = get_current_title_len(&prefix);
|
||||||
if prefix_len + active_tab.len <= cols {
|
|
||||||
tabs_to_render.push(active_tab);
|
// if active tab alone won't fit in cols, don't draw any tabs
|
||||||
|
if prefix_len + active_tab.len > cols {
|
||||||
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut tabs_to_render = vec![active_tab];
|
||||||
|
|
||||||
populate_tabs_in_tab_line(
|
populate_tabs_in_tab_line(
|
||||||
&mut tabs_before_active,
|
&mut tabs_before_active,
|
||||||
&mut tabs_after_active,
|
&mut tabs_after_active,
|
||||||
&mut tabs_to_render,
|
&mut tabs_to_render,
|
||||||
cols.saturating_sub(prefix_len),
|
cols.saturating_sub(prefix_len),
|
||||||
);
|
|
||||||
|
|
||||||
let mut tab_line: Vec<LinePart> = vec![];
|
|
||||||
if !tabs_before_active.is_empty() {
|
|
||||||
add_previous_tabs_msg(
|
|
||||||
&mut tabs_before_active,
|
|
||||||
&mut tabs_to_render,
|
|
||||||
&mut tab_line,
|
|
||||||
cols.saturating_sub(prefix_len),
|
|
||||||
palette,
|
palette,
|
||||||
tab_separator(capabilities),
|
capabilities,
|
||||||
);
|
);
|
||||||
}
|
prefix.append(&mut tabs_to_render);
|
||||||
tab_line.append(&mut tabs_to_render);
|
|
||||||
if !tabs_after_active.is_empty() {
|
|
||||||
add_next_tabs_msg(
|
|
||||||
&mut tabs_after_active,
|
|
||||||
&mut tab_line,
|
|
||||||
cols.saturating_sub(prefix_len),
|
|
||||||
palette,
|
|
||||||
tab_separator(capabilities),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
prefix.append(&mut tab_line);
|
|
||||||
prefix
|
prefix
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ impl ZellijPlugin for State {
|
||||||
self.mode_info.session_name.as_deref(),
|
self.mode_info.session_name.as_deref(),
|
||||||
all_tabs,
|
all_tabs,
|
||||||
active_tab_index,
|
active_tab_index,
|
||||||
cols,
|
cols.saturating_sub(1),
|
||||||
self.mode_info.palette,
|
self.mode_info.palette,
|
||||||
self.mode_info.capabilities,
|
self.mode_info.capabilities,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use zellij_tile_utils::style;
|
||||||
|
|
||||||
pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
|
pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
|
||||||
let left_separator = style!(palette.cyan, palette.green).paint(separator);
|
let left_separator = style!(palette.cyan, palette.green).paint(separator);
|
||||||
let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the text padding
|
let tab_text_len = text.chars().count() + 2 + separator.chars().count() * 2; // 2 for left and right separators, 2 for the text padding
|
||||||
let tab_styled_text = style!(palette.black, palette.green)
|
let tab_styled_text = style!(palette.black, palette.green)
|
||||||
.bold()
|
.bold()
|
||||||
.paint(format!(" {} ", text));
|
.paint(format!(" {} ", text));
|
||||||
|
|
@ -22,7 +22,7 @@ pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
|
||||||
|
|
||||||
pub fn non_active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
|
pub fn non_active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
|
||||||
let left_separator = style!(palette.cyan, palette.fg).paint(separator);
|
let left_separator = style!(palette.cyan, palette.fg).paint(separator);
|
||||||
let tab_text_len = text.chars().count() + 4; // 2 for left and right separators, 2 for the padding
|
let tab_text_len = text.chars().count() + 2 + separator.chars().count() * 2; // 2 for left and right separators, 2 for the text padding
|
||||||
let tab_styled_text = style!(palette.black, palette.fg)
|
let tab_styled_text = style!(palette.black, palette.fg)
|
||||||
.bold()
|
.bold()
|
||||||
.paint(format!(" {} ", text));
|
.paint(format!(" {} ", text));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue