diff --git a/docs/changelog.rst b/docs/changelog.rst index 3b1a643fc..9b8f31c05 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -211,6 +211,8 @@ Detailed list of changes - Custom shaders: The builtin :code:`dim-inactive-windows` shader no longer dims the tab bar and the padding, border and margin around the active window (:iss:`10524`) +- Vertical tabs: Improve rendering of tabs when using the ``powerline`` :opt:`tab_bar_style` + 0.49.1 [2026-09-24] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index 7de0b6e96..dc0d915d0 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -624,6 +624,15 @@ def draw_tab_with_powerline( screen.cursor.x -= extra + 1 screen.draw('…') + if draw_data.tab_bar_edge in ('left', 'right'): + # Clear anything left after a truncation ellipsis. + screen.cursor.bg = tab_bg + screen.draw(' ' * (screen.columns - 1 - screen.cursor.x)) + screen.cursor.fg = tab_bg + screen.cursor.bg = default_bg + screen.draw(separator_symbol) + return screen.cursor.x + if not needs_soft_separator: screen.draw(' ') screen.cursor.fg = tab_bg diff --git a/kitty_tests/tab_bar.py b/kitty_tests/tab_bar.py index 30da8812d..ce79fd73d 100644 --- a/kitty_tests/tab_bar.py +++ b/kitty_tests/tab_bar.py @@ -5,7 +5,8 @@ from unittest.mock import patch from kitty.fast_data_types import LEFT_EDGE, Color, Region from kitty.options.utils import tab_title_wrap -from kitty.tab_bar import CellRange, TabBar, TabBarData, truncate_line, wrap_title +from kitty.tab_bar import CellRange, TabBar, TabBarData, as_rgb, powerline_symbols, truncate_line, wrap_title +from kitty.utils import color_as_int from .base import BaseTest @@ -413,3 +414,38 @@ class TestTabBar(BaseTest): # Issue 2: no background-coloured separator after the trailing fades. self.ae(bg(11), fade_bg) # col 11, last trailing fade, must not be default_bg + + def test_vertical_tab_bar_powerline_style(self) -> None: + tb = self.vertical_tab_bar( + num_tabs=3, + tab_bar_style='powerline', + tab_powerline_style='slanted', + active_tab_background=Color(200, 0, 0), + inactive_tab_background=Color(0, 200, 0), + tab_bar_background=Color(0, 0, 100), + ) + s = tb.screen + separator, soft_separator = powerline_symbols['slanted'] + active_bg = as_rgb(color_as_int(Color(200, 0, 0))) + inactive_bg = as_rgb(color_as_int(Color(0, 200, 0))) + tab_bar_bg = as_rgb(color_as_int(Color(0, 0, 100))) + for row, tab_bg in ((0, active_bg), (2, inactive_bg), (4, inactive_bg)): + line = s.line(row) + text = str(line) + self.assertNotIn(soft_separator, text) + self.ae(text.index(separator), s.columns - 1) + self.ae(int(line.cursor_from(3).bg), tab_bg) + sep = line.cursor_from(s.columns - 1) + self.ae((int(sep.fg), int(sep.bg)), (tab_bg, tab_bar_bg)) + + tb = self.vertical_tab_bar(num_tabs=1, tab_bar_style='powerline', tab_powerline_style='slanted', tab_title_template='{title}-a-long-title') + self.ae(str(tb.screen.line(0)), f' t0-a-lon… {separator}') + + tb = self.vertical_tab_bar(num_tabs=1, tab_bar_style='powerline', tab_powerline_style='slanted', tab_title_template='abcdef', tab_title_max_length=3) + self.ae(str(tb.screen.line(0)), f' ab… {separator}') + + tb = self.vertical_tab_bar( + num_tabs=1, tab_bar_style='powerline', tab_title_template='{fmt.fg.red}{title}{fmt.reset}', active_tab_background=Color(200, 0, 0) + ) + line = tb.screen.line(0) + self.ae({int(line.cursor_from(x).bg) for x in range(tb.screen.columns - 1)}, {active_bg})