diff options
author | Paulo Coelho <9609090+prscoelho@users.noreply.github.com> | 2021-09-09 15:38:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-09 16:38:10 +0200 |
commit | f2850d2931dddbc3d3ac4f4ac3c656df63bc1ebb (patch) | |
tree | 271e345fa5651da2461cc6ec3756a1dce6f9d356 /default-plugins | |
parent | f0da6872df49815de723b11fe9cf151be0de4e61 (diff) |
fix(tab-bar): prevent active tab from being hidden (#703)
Diffstat (limited to 'default-plugins')
-rw-r--r-- | default-plugins/tab-bar/src/line.rs | 172 | ||||
-rw-r--r-- | default-plugins/tab-bar/src/main.rs | 2 | ||||
-rw-r--r-- | default-plugins/tab-bar/src/tab.rs | 4 |
3 files changed, 82 insertions, 96 deletions
diff --git a/default-plugins/tab-bar/src/line.rs b/default-plugins/tab-bar/src/line.rs index 4cdc02c44..be5623ff9 100644 --- a/default-plugins/tab-bar/src/line.rs +++ b/default-plugins/tab-bar/src/line.rs @@ -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; } } @@ -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() @@ -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() @@ -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(); @@ -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() { @@ -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 } diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs index d4111cfae..850781261 100644 --- a/default-plugins/tab-bar/src/main.rs +++ b/default-plugins/tab-bar/src/main.rs @@ -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, ); diff --git a/default-plugins/tab-bar/src/tab.rs b/default-plugins/tab-bar/src/tab.rs index 9edfba13f..dfa5ab776 100644 --- a/default-plugins/tab-bar/src/tab.rs +++ b/default-plugins/tab-bar/src/tab.rs @@ -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)); @@ -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)); |