From d92140afeab9fb24c904d18eddabb6d9e6647304 Mon Sep 17 00:00:00 2001 From: Valdemar Lorenzen Date: Mon, 12 Oct 2026 10:02:20 +0200 Subject: [PATCH] style: refine tab bar appearance --- docs/editing.md | 2 +- src/tui.rs | 35 +++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/editing.md b/docs/editing.md index 68a72d7..dc10794 100644 --- a/docs/editing.md +++ b/docs/editing.md @@ -218,7 +218,7 @@ Use `:tab.new` to open the current buffer in a new, focused tab with one pane. E | `:tab.close` | `config.core.tab.close` | Close the current tab and all its panes, protecting unsaved changes | | `:tab.force_close` | `config.core.tab.force_close` | Explicitly discard exclusively displayed edits and close the tab | -Multiple tabs show a top row with numbered filenames, dirty markers, and an active-tab highlight. Narrow terminals keep the active tab visible. New/close actions have no default keybinding; for example, `config.bind "leader t", config.core.tab.new`. +Multiple tabs show a single top row with padded numbered filenames, subtle separators, and dirty markers. The active tab uses bold, underlined accent text; the row keeps the terminal's background instead of painting a full-width band. Narrow terminals keep the active tab visible. New/close actions have no default keybinding; for example, `config.bind "leader t", config.core.tab.new`. Closing a tab refuses dirty buffers whose final displayed owner would disappear, including a comparison's working and return buffers. Buffers displayed in another tab remain open with their edits, even on force-close. Other hidden buffers remain loaded. Closing the last tab quits only if every open buffer is clean; force-close quits without saving. diff --git a/src/tui.rs b/src/tui.rs index 8a62fe1..eafd8e7 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1040,21 +1040,28 @@ fn draw_tab_bar(frame: &mut Frame, editor: &Editor, area: Rect) -> Rect { .collect(); let active = editor.tab_index(); let mut first = active; - let mut width = labels[active].width(); - while first > 0 && width + labels[first - 1].width() <= usize::from(bar.width) { + let padding = u16::from(bar.width > 1); + let mut width = labels[active].width() + usize::from(padding); + while first > 0 && width + 2 + labels[first - 1].width() <= usize::from(bar.width) { first -= 1; - width += labels[first].width(); + width += 2 + labels[first].width(); } - let inactive = Style::default().fg(Color::Gray).bg(Color::DarkGray); - frame.render_widget(Paragraph::new("").style(inactive), bar); - let mut x = bar.x; + let inactive = Style::default().fg(Color::Gray); + let mut x = bar.x + padding; for (index, label) in labels.iter().enumerate().skip(first) { + if index != first { + let width = 2.min(bar.right() - x); + frame.render_widget( + Paragraph::new("│ ").style(Style::default().fg(Color::DarkGray)), + Rect::new(x, bar.y, width, 1), + ); + x += width; + } let width = label.width().min(usize::from(bar.right() - x)) as u16; let style = if index == active { Style::default() - .fg(Color::Black) - .bg(Color::Cyan) - .add_modifier(Modifier::BOLD) + .fg(Color::Cyan) + .add_modifier(Modifier::BOLD | Modifier::UNDERLINED) } else { inactive }; @@ -5189,12 +5196,6 @@ mod tests { let titles = pane_screen(&terminal, Rect::new(0, 0, 80, 1)); assert!(titles.contains("1 a�name.txt [+]"), "{titles}"); assert!(titles.contains("2 a�name.txt [+]"), "{titles}"); - let active_x = display_column(&titles[..titles.find("2 a�name.txt").unwrap()], usize::MAX); - assert_eq!(terminal.backend().buffer()[(0, 0)].bg, Color::DarkGray); - assert_eq!( - terminal.backend().buffer()[(active_x as u16, 0)].bg, - Color::Cyan - ); assert_eq!(views.document.geometry.unwrap().1.y, 1); assert!(terminal.get_cursor_position().unwrap().y >= 1); assert!(pane_screen(&terminal, views.rects[0].1).contains("changed DOCUMENT")); @@ -5231,9 +5232,7 @@ mod tests { .unwrap(); if width > 0 && height > 0 { let title = pane_screen(&terminal, Rect::new(0, 0, width, 1)); - let expected: String = "3 active.txt".chars().take(usize::from(width)).collect(); - assert_eq!(title, expected); - assert_eq!(terminal.backend().buffer()[(0, 0)].bg, Color::Cyan); + assert!(title.trim_start().starts_with('3'), "{title}"); } }