diff --git a/docs/changelog.rst b/docs/changelog.rst index 56f55eac4..e8eda1051 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,12 @@ Changelog |kitty| is a feature-rich, cross-platform, *fast*, GPU based terminal. To update |kitty|, :doc:`follow the instructions `. +0.23.0 [future] +---------------------- + +- A new style for the tab bar that makes tabs looks like the tabs in a physical + tabbed file, see :opt:`tab_bar_style` + 0.22.2 [2021-08-02] ---------------------- diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 66e29e70a..9437e4230 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -872,15 +872,16 @@ opt('tab_bar_style', 'fade', - choices=('fade', 'hidden', 'powerline', 'separator'), ctype='!tab_bar_style', + choices=('fade', 'hidden', 'powerline', 'separator', 'slant'), ctype='!tab_bar_style', long_text=''' The tab bar style, can be one of: :code:`fade`, :code:`separator`, :code:`powerline`, or :code:`hidden`. In the fade style, each tab's edges fade -into the background color, in the separator style, tabs are separated by a -configurable separator, and the powerline shows the tabs as a continuous line. -If you use the hidden style, you might want to create a mapping for the -:ref:`action-select_tab` action which presents you with a list of tabs -and allows for easy switching to a tab. +into the background color, in the slant style tabs look like the tabs in file +folder, in the separator style, tabs are separated by a configurable separator, +the powerline shows the tabs as a continuous line. If you use the hidden +style, you might want to create a mapping for the :ref:`action-select_tab` +action which presents you with a list of tabs and allows for easy switching to +a tab. ''' ) diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 93db1a6f8..a9d87321b 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1167,7 +1167,7 @@ def tab_bar_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: raise ValueError(f"The value {val} is not a valid choice for tab_bar_style") ans["tab_bar_style"] = val - choices_for_tab_bar_style = frozenset(('fade', 'hidden', 'powerline', 'separator')) + choices_for_tab_bar_style = frozenset(('fade', 'hidden', 'powerline', 'separator', 'slant')) def tab_fade(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['tab_fade'] = tab_fade(val) diff --git a/kitty/options/types.py b/kitty/options/types.py index 87560f785..058194f70 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -23,7 +23,7 @@ choices_for_pointer_shape_when_dragging = typing.Literal['arrow', 'beam', 'hand'] choices_for_pointer_shape_when_grabbed = typing.Literal['arrow', 'beam', 'hand'] choices_for_strip_trailing_spaces = typing.Literal['always', 'never', 'smart'] - choices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separator'] + choices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separator', 'slant'] choices_for_tab_powerline_style = typing.Literal['angled', 'round', 'slanted'] choices_for_tab_switch_strategy = typing.Literal['last', 'left', 'previous', 'right'] else: diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index 35227c94f..a0b9a0963 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -13,7 +13,7 @@ from .layout.base import Rect from .rgb import Color, alpha_blend, color_as_sgr, color_from_int, to_color from .types import WindowGeometry, run_once -from .typing import PowerlineStyle +from .typing import PowerlineStyle, EdgeLiteral from .utils import color_as_int, log_error from .window import calculate_gl_geometry @@ -44,6 +44,7 @@ class DrawData(NamedTuple): active_title_template: Optional[str] tab_activity_symbol: Optional[str] powerline_style: PowerlineStyle + tab_bar_edge: EdgeLiteral def as_rgb(x: int) -> int: @@ -164,6 +165,44 @@ def draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int) screen.draw(title) +def draw_tab_with_slant(draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_title_length: int, index: int, is_last: bool) -> int: + left_sep, right_sep = ('', '') if draw_data.tab_bar_edge == 'top' else ('', '') + tab_bg = as_rgb(color_as_int(draw_data.active_bg if tab.is_active else draw_data.inactive_bg)) + slant_fg = as_rgb(color_as_int(draw_data.default_bg)) + + def draw_sep(which: str) -> None: + screen.cursor.bg = tab_bg + screen.cursor.fg = slant_fg + screen.draw(which) + screen.cursor.bg = tab_bg + screen.cursor.fg = 0 + + max_title_length += 1 + if max_title_length <= 1: + screen.draw('…') + elif max_title_length == 2: + screen.draw('…|') + elif max_title_length < 6: + draw_sep(left_sep) + screen.draw((' ' if max_title_length == 5 else '') + '…' + (' ' if max_title_length >= 4 else '')) + draw_sep(right_sep) + else: + draw_sep(left_sep) + screen.draw(' ') + draw_title(draw_data, screen, tab, index) + extra = screen.cursor.x - before - max_title_length + if extra >= 0: + screen.cursor.x -= extra + 3 + screen.draw('…') + elif extra == -1: + screen.cursor.x -= 2 + screen.draw('…') + screen.draw(' ') + draw_sep(right_sep) + + return screen.cursor.x + + def draw_tab_with_separator(draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_title_length: int, index: int, is_last: bool) -> int: tab_bg = draw_data.active_bg if tab.is_active else draw_data.inactive_bg screen.cursor.bg = as_rgb(color_as_int(tab_bg)) @@ -332,12 +371,15 @@ def apply_options(self) -> None: opts.tab_bar_background or opts.background, opts.tab_title_template, opts.active_tab_title_template, opts.tab_activity_symbol, - opts.tab_powerline_style + opts.tab_powerline_style, + 'top' if opts.tab_bar_edge == 1 else 'bottom' ) if opts.tab_bar_style == 'separator': self.draw_func = draw_tab_with_separator elif opts.tab_bar_style == 'powerline': self.draw_func = draw_tab_with_powerline + elif opts.tab_bar_style == 'slant': + self.draw_func = draw_tab_with_slant else: self.draw_func = draw_tab_with_fade