diff --git a/docs/changelog.rst b/docs/changelog.rst index b18d01a62..d11d395e2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,12 @@ To update |kitty|, :doc:`follow the instructions `. Recent major new features --------------------------- +Vertical tabs [0.48] +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +kitty now has support for :pull:`vertical tabs <9855>` along the left or right edge of the OS +Window. Useful for people that have wide aspect ratio windows. + Drag and drop for terminal programs [0.47] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -173,9 +179,11 @@ consumption to do the same tasks. Detailed list of changes ------------------------------------- -0.50.0 [future] +0.48.0 [future] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Implement vertical tabs by setting :opt:`tab_bar_edge` to ``left`` or ``right`` (:pull:`9855`) + - Graphics protocol: Add a new :ref:`transient usage hint ` that clients can send to terminals to indicate an image is meant for only short duration use (:pull:`10092`) - kitten @ get-text: Add support for :code:`alternate` and :code:`alternate_scrollback` extents to fetch text from the alternate screen buffer (:iss:`10165`) diff --git a/kitty/options/utils.py b/kitty/options/utils.py index d47f2a7af..18ef6c4ab 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -751,12 +751,15 @@ def tab_separator(x: str) -> str: def tab_bar_edge(x: str) -> int: - return { - 'left': defines.LEFT_EDGE, - 'top': defines.TOP_EDGE, - 'right': defines.RIGHT_EDGE, - 'bottom': defines.BOTTOM_EDGE, - }.get(x.lower(), defines.BOTTOM_EDGE) + match x.lower(): + case 'top': + return defines.TOP_EDGE + case 'left': + return defines.LEFT_EDGE + case 'right': + return defines.RIGHT_EDGE + case _: + return defines.BOTTOM_EDGE def tab_font_style(x: str) -> tuple[bool, bool]: diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index 5292e42d1..94db2f69a 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -8,6 +8,7 @@ from string import Formatter as StringFormatter from typing import ( Any, + Literal, NamedTuple, ) @@ -16,12 +17,12 @@ from .fast_data_types import ( BOTTOM_EDGE, DECAWM, - Color, LEFT_EDGE, - Region, RIGHT_EDGE, - Screen, TOP_EDGE, + Color, + Region, + Screen, background_opacity_of, cell_size_for_window, get_boss, @@ -112,21 +113,21 @@ def is_vertical_edge(edge: int) -> bool: return edge in VERTICAL_EDGES -def edge_name(edge: int) -> EdgeLiteral: - return { - LEFT_EDGE: 'left', - TOP_EDGE: 'top', - RIGHT_EDGE: 'right', - BOTTOM_EDGE: 'bottom', - }.get(edge, 'bottom') +edge_name_map: dict[int, EdgeLiteral] = { + LEFT_EDGE: 'left', + TOP_EDGE: 'top', + RIGHT_EDGE: 'right', + BOTTOM_EDGE: 'bottom', +} -def normalized_tab_bar_align(align: str) -> str: - if align == 'left': - return 'start' - if align == 'right': - return 'end' - return align +def normalized_tab_bar_align(align: str) -> Literal['start', 'end', 'center']: + match align: + case 'left' | 'start': + return 'start' + case 'right' | 'end': + return 'end' + return 'center' @lru_cache @@ -656,7 +657,7 @@ def apply_options(self) -> None: opts.active_tab_title_template, opts.tab_activity_symbol, opts.tab_powerline_style, - edge_name(opts.tab_bar_edge), + edge_name_map[opts.tab_bar_edge], opts.tab_title_max_length, self.os_window_id, ) ts = opts.tab_bar_style @@ -671,18 +672,13 @@ def apply_options(self) -> None: else: self.draw_func = draw_tab_with_fade self.tab_bar_align = normalized_tab_bar_align(opts.tab_bar_align) - if self.tab_bar_align == 'center': - self.align_factor = 2 - elif self.tab_bar_align == 'end': - self.align_factor = 1 - else: - self.align_factor = 0 - if self.tab_bar_align == 'center': - self.align: Callable[[], None] = partial(self.align_with_factor, 2) - elif self.tab_bar_align == 'end': - self.align = self.align_with_factor - else: - self.align = lambda: None + match self.tab_bar_align: + case 'center': + self.align: Callable[[], None] = partial(self.align_with_factor, 2) + case 'end': + self.align = self.align_with_factor + case 'start': + self.align = lambda: None def patch_colors(self, spec: dict[str, int | None]) -> None: opts = get_options() @@ -946,7 +942,7 @@ def destroy(self) -> None: self.screen.reset_callbacks() del self.screen - def tab_id_at(self, x: int, y: int = 0) -> int: + def tab_id_at(self, x: int, y: int) -> int: if self.laid_out_once: g = self.window_geometry if not (g.left <= x < g.right and g.top <= y < g.bottom):