aboutsummaryrefslogtreecommitdiff
path: root/src/client/shell/tabs.rs
diff options
context:
space:
mode:
authorakbash <akbash@herdr.dev>2026-09-15 04:20:59 +0300
committerGitHub <noreply@github.com>2026-09-15 03:20:59 +0200
commit052779c4159ed851ae78ed1271130811986d1166 (patch)
tree7b3cb372966549abf5cb41a96952997eda1304c9 /src/client/shell/tabs.rs
parent32503f81f7f22552d86f048afb3de179f44e049a (diff)
fix: reveal the full last tab in overflowing tab strips (#4152)
refs #4151 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Diffstat (limited to 'src/client/shell/tabs.rs')
-rw-r--r--src/client/shell/tabs.rs56
1 files changed, 39 insertions, 17 deletions
diff --git a/src/client/shell/tabs.rs b/src/client/shell/tabs.rs
index 9e9901ff..3fdcf7dc 100644
--- a/src/client/shell/tabs.rs
+++ b/src/client/shell/tabs.rs
@@ -134,7 +134,7 @@ pub(crate) fn render_tab_bar(
if overflow && mouse_chrome {
hits.tab_scroll_right = Rect::new(tab_right, area.y, TAB_SCROLL_BUTTON_WIDTH, 1);
- let can_scroll_right = last_visible.is_some_and(|index| index + 1 < tabs.len());
+ let can_scroll_right = *tab_scroll < max_scroll;
put_text(
buffer,
hits.tab_scroll_right.x,
@@ -353,25 +353,22 @@ fn centered_tab_scroll(focused: usize, widths: &[u16], available: u16) -> usize
}
fn max_tab_scroll(widths: &[u16], available: u16) -> usize {
- (0..widths.len())
- .find(|start| last_visible_tab(*start, widths, available) == widths.len().checked_sub(1))
- .unwrap_or(0)
-}
-
-fn last_visible_tab(start: usize, widths: &[u16], available: u16) -> Option<usize> {
- let mut remaining = available;
- let mut last = None;
- for (index, width) in widths.iter().copied().enumerate().skip(start) {
- if remaining == 0 {
- break;
- }
- last = Some(index);
- if width >= remaining {
+ let Some((&last, preceding)) = widths.split_last() else {
+ return 0;
+ };
+ let mut start = preceding.len();
+ let mut used = u32::from(last);
+ // Keep the longest fully visible suffix, not merely a sliver of the last tab.
+ // An oversized last tab must still be reachable at the start of the strip.
+ for width in preceding.iter().rev() {
+ let required = used + 1 + u32::from(*width);
+ if required > u32::from(available) {
break;
}
- remaining = remaining.saturating_sub(width.saturating_add(1));
+ used = required;
+ start -= 1;
}
- last
+ start
}
fn tab_label(tab: &ClientShellTab) -> String {
@@ -381,3 +378,28 @@ fn tab_label(tab: &ClientShellTab) -> String {
tab.label.clone()
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::max_tab_scroll;
+
+ #[test]
+ fn trailing_scroll_limit_accounts_for_full_widths_and_separators() {
+ for (widths, available, expected) in [
+ (&[][..], 0, 0),
+ (&[8, 13][..], 0, 1),
+ (&[8, 13][..], 1, 1),
+ (&[8, 13][..], 12, 1),
+ (&[8, 13][..], 21, 1),
+ (&[8, 13][..], 22, 0),
+ (&[8, 13][..], 30, 0),
+ (&[8, u16::MAX][..], u16::MAX, 1),
+ ] {
+ assert_eq!(
+ max_tab_scroll(widths, available),
+ expected,
+ "widths={widths:?}, available={available}"
+ );
+ }
+ }
+}