Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tab-bar): prevent active tab from being hidden #703

Merged
merged 1 commit into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 79 additions & 93 deletions default-plugins/tab-bar/src/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,82 @@ fn get_current_title_len(current_title: &[LinePart]) -> usize {
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(
tabs_before_active: &mut Vec<LinePart>,
tabs_after_active: &mut Vec<LinePart>,
tabs_to_render: &mut Vec<LinePart>,
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 {
if tabs_before_active.is_empty() && tabs_after_active.is_empty() {
break;
}
let current_title_len = get_current_title_len(tabs_to_render);
if current_title_len >= cols {
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;
}
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;

let left = if let Some(tab) = tabs_before_active.last() {
tab.len
} 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;
}
}
Expand All @@ -56,7 +99,8 @@ fn left_more_message(tab_count_to_the_left: usize, palette: Palette, separator:
" ← +many ".to_string()
};
// 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 more_styled_text = style!(palette.black, palette.orange)
.bold()
Expand Down Expand Up @@ -85,7 +129,8 @@ fn right_more_message(
} else {
" +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 more_styled_text = style!(palette.black, palette.orange)
.bold()
Expand All @@ -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> {
let prefix_text = " Zellij ".to_string();

Expand Down Expand Up @@ -184,7 +187,6 @@ pub fn tab_line(
palette: Palette,
capabilities: PluginCapabilities,
) -> Vec<LinePart> {
let mut tabs_to_render = Vec::new();
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
let mut tabs_before_active = all_tabs;
let active_tab = if !tabs_after_active.is_empty() {
Expand All @@ -194,38 +196,22 @@ pub fn tab_line(
};
let mut prefix = tab_line_prefix(session_name, palette, cols);
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(
&mut tabs_before_active,
&mut tabs_after_active,
&mut tabs_to_render,
cols.saturating_sub(prefix_len),
palette,
capabilities,
);

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,
tab_separator(capabilities),
);
}
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.append(&mut tabs_to_render);
prefix
}
2 changes: 1 addition & 1 deletion default-plugins/tab-bar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl ZellijPlugin for State {
self.mode_info.session_name.as_deref(),
all_tabs,
active_tab_index,
cols,
cols.saturating_sub(1),
self.mode_info.palette,
self.mode_info.capabilities,
);
Expand Down
4 changes: 2 additions & 2 deletions default-plugins/tab-bar/src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use zellij_tile_utils::style;

pub fn active_tab(text: String, palette: Palette, separator: &str) -> LinePart {
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)
.bold()
.paint(format!(" {} ", text));
Expand All @@ -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 {
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)
.bold()
.paint(format!(" {} ", text));
Expand Down