mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-22 00:08:04 +02:00
Add pane title bar feature for window splits
Add an optional title bar that displays above or below each window pane when multiple windows are visible in a tab. This is similar to tmux's pane-border-format or Terminator's pane title bars. New configuration options: - pane_title_bar: none/top/bottom (default: none) - pane_title_template: f-string template (same syntax as tab_title_template) - active_pane_title_template: override for active pane - pane_title_bar_active_fg/bg: colors for active pane title - pane_title_bar_inactive_fg/bg: colors for inactive pane titles - pane_title_bar_align: left/center/right text alignment The title bars are rendered using virtual Screen objects registered with the GPU, following the same model as the tab bar. Title bars are automatically hidden when only a single window is visible. Ref: https://github.com/kovidgoyal/kitty/discussions/9448 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1466,6 +1466,59 @@ dragging of borders. Note that because kitty uses layouts, dragging borders does
|
||||
actually resize the window itself, but instead, the layout row/column/slot, which can result
|
||||
in multiple windows getting resized.
|
||||
''')
|
||||
|
||||
opt('pane_title_bar', 'none',
|
||||
choices=('none', 'top', 'bottom'),
|
||||
long_text='''
|
||||
Show a title bar for each window/pane when there are multiple windows in a tab.
|
||||
The title bar displays the window title and is hidden when only a single window
|
||||
is visible. The value controls the position of the title bar relative to the
|
||||
window content. Set to :code:`none` to disable.
|
||||
'''
|
||||
)
|
||||
|
||||
opt('pane_title_template', '"{title}"',
|
||||
option_type='tab_title_template',
|
||||
long_text='''
|
||||
A template to render the pane title bar text. Uses the same template syntax as
|
||||
:opt:`tab_title_template`. Available variables include: :code:`{title}`,
|
||||
:code:`{index}`, :code:`{layout_name}`, :code:`{num_windows}`,
|
||||
:code:`{num_window_groups}`, :code:`{tab.active_wd}`, etc.
|
||||
'''
|
||||
)
|
||||
|
||||
opt('active_pane_title_template', 'none',
|
||||
option_type='tab_title_template',
|
||||
long_text='''
|
||||
Template to use for the active pane title bar. If not set (the value
|
||||
:code:`none`), the :opt:`pane_title_template` is used.
|
||||
'''
|
||||
)
|
||||
|
||||
opt('pane_title_bar_active_fg', '#000000',
|
||||
option_type='to_color',
|
||||
long_text='Foreground color for the active pane title bar.'
|
||||
)
|
||||
|
||||
opt('pane_title_bar_active_bg', '#00ff00',
|
||||
option_type='to_color',
|
||||
long_text='Background color for the active pane title bar.'
|
||||
)
|
||||
|
||||
opt('pane_title_bar_inactive_fg', '#cccccc',
|
||||
option_type='to_color',
|
||||
long_text='Foreground color for inactive pane title bars.'
|
||||
)
|
||||
|
||||
opt('pane_title_bar_inactive_bg', '#333333',
|
||||
option_type='to_color',
|
||||
long_text='Background color for inactive pane title bars.'
|
||||
)
|
||||
|
||||
opt('pane_title_bar_align', 'center',
|
||||
choices=('left', 'center', 'right'),
|
||||
long_text='Horizontal alignment of the text in pane title bars.'
|
||||
)
|
||||
egr() # }}}
|
||||
|
||||
|
||||
|
||||
36
kitty/options/parse.py
generated
36
kitty/options/parse.py
generated
@@ -36,6 +36,9 @@ class Parser:
|
||||
def active_border_color(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['active_border_color'] = to_color_or_none(val)
|
||||
|
||||
def active_pane_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['active_pane_title_template'] = tab_title_template(val)
|
||||
|
||||
def active_tab_background(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['active_tab_background'] = to_color(val)
|
||||
|
||||
@@ -1165,6 +1168,37 @@ class Parser:
|
||||
def open_url_with(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['open_url_with'] = to_cmdline(val)
|
||||
|
||||
def pane_title_bar(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
val = val.lower()
|
||||
if val not in self.choices_for_pane_title_bar:
|
||||
raise ValueError(f"The value {val} is not a valid choice for pane_title_bar")
|
||||
ans["pane_title_bar"] = val
|
||||
|
||||
choices_for_pane_title_bar = frozenset(('none', 'top', 'bottom'))
|
||||
|
||||
def pane_title_bar_active_bg(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['pane_title_bar_active_bg'] = to_color(val)
|
||||
|
||||
def pane_title_bar_active_fg(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['pane_title_bar_active_fg'] = to_color(val)
|
||||
|
||||
def pane_title_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
val = val.lower()
|
||||
if val not in self.choices_for_pane_title_bar_align:
|
||||
raise ValueError(f"The value {val} is not a valid choice for pane_title_bar_align")
|
||||
ans["pane_title_bar_align"] = val
|
||||
|
||||
choices_for_pane_title_bar_align = frozenset(('left', 'center', 'right'))
|
||||
|
||||
def pane_title_bar_inactive_bg(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['pane_title_bar_inactive_bg'] = to_color(val)
|
||||
|
||||
def pane_title_bar_inactive_fg(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['pane_title_bar_inactive_fg'] = to_color(val)
|
||||
|
||||
def pane_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['pane_title_template'] = tab_title_template(val)
|
||||
|
||||
def paste_actions(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['paste_actions'] = paste_actions(val)
|
||||
|
||||
@@ -1322,7 +1356,7 @@ class Parser:
|
||||
raise ValueError(f"The value {val} is not a valid choice for tab_bar_align")
|
||||
ans["tab_bar_align"] = val
|
||||
|
||||
choices_for_tab_bar_align = frozenset(('left', 'center', 'right'))
|
||||
choices_for_tab_bar_align = choices_for_pane_title_bar_align
|
||||
|
||||
def tab_bar_background(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['tab_bar_background'] = to_color_or_none(val)
|
||||
|
||||
20
kitty/options/types.py
generated
20
kitty/options/types.py
generated
@@ -25,11 +25,13 @@ choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'text', 'poi
|
||||
choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11']
|
||||
choices_for_macos_colorspace = typing.Literal['srgb', 'default', 'displayp3']
|
||||
choices_for_macos_show_window_title_in = typing.Literal['all', 'menubar', 'none', 'window']
|
||||
choices_for_pane_title_bar = typing.Literal['none', 'top', 'bottom']
|
||||
choices_for_pane_title_bar_align = typing.Literal['left', 'center', 'right']
|
||||
choices_for_placement_strategy = typing.Literal['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right']
|
||||
choices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape
|
||||
choices_for_scrollbar = typing.Literal['scrolled', 'always', 'never', 'hovered', 'scrolled-and-hovered']
|
||||
choices_for_strip_trailing_spaces = typing.Literal['always', 'never', 'smart']
|
||||
choices_for_tab_bar_align = typing.Literal['left', 'center', 'right']
|
||||
choices_for_tab_bar_align = choices_for_pane_title_bar_align
|
||||
choices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separator', 'slant', 'custom']
|
||||
choices_for_tab_powerline_style = typing.Literal['angled', 'round', 'slanted']
|
||||
choices_for_tab_switch_strategy = typing.Literal['last', 'left', 'previous', 'right']
|
||||
@@ -41,6 +43,7 @@ choices_for_window_logo_position = choices_for_placement_strategy
|
||||
option_names = (
|
||||
'action_alias',
|
||||
'active_border_color',
|
||||
'active_pane_title_template',
|
||||
'active_tab_background',
|
||||
'active_tab_font_style',
|
||||
'active_tab_foreground',
|
||||
@@ -405,6 +408,13 @@ option_names = (
|
||||
'narrow_symbols',
|
||||
'notify_on_cmd_finish',
|
||||
'open_url_with',
|
||||
'pane_title_bar',
|
||||
'pane_title_bar_active_bg',
|
||||
'pane_title_bar_active_fg',
|
||||
'pane_title_bar_align',
|
||||
'pane_title_bar_inactive_bg',
|
||||
'pane_title_bar_inactive_fg',
|
||||
'pane_title_template',
|
||||
'paste_actions',
|
||||
'pixel_scroll',
|
||||
'placement_strategy',
|
||||
@@ -502,6 +512,7 @@ option_names = (
|
||||
|
||||
class Options:
|
||||
active_border_color: kitty.fast_data_types.Color | None = Color(0, 255, 0)
|
||||
active_pane_title_template: str = 'none'
|
||||
active_tab_background: Color = Color(238, 238, 238)
|
||||
active_tab_font_style: tuple[bool, bool] = (True, True)
|
||||
active_tab_foreground: Color = Color(0, 0, 0)
|
||||
@@ -600,6 +611,13 @@ class Options:
|
||||
mouse_hide_wait: MouseHideWait = MouseHideWait(hide_wait=0.0, show_wait=0.0, show_threshold=40, scroll_show=True) if is_macos else MouseHideWait(hide_wait=3.0, show_wait=0.0, show_threshold=40, scroll_show=True)
|
||||
notify_on_cmd_finish: NotifyOnCmdFinish = NotifyOnCmdFinish(when='never', duration=5.0, action='notify', cmdline=(), clear_on=('focus', 'next'))
|
||||
open_url_with: list[str] = ['default']
|
||||
pane_title_bar: choices_for_pane_title_bar = 'none'
|
||||
pane_title_bar_active_bg: Color = Color(0, 255, 0)
|
||||
pane_title_bar_active_fg: Color = Color(0, 0, 0)
|
||||
pane_title_bar_align: choices_for_pane_title_bar_align = 'center'
|
||||
pane_title_bar_inactive_bg: Color = Color(51, 51, 51)
|
||||
pane_title_bar_inactive_fg: Color = Color(204, 204, 204)
|
||||
pane_title_template: str = '{title}'
|
||||
paste_actions: frozenset[str] = frozenset({'confirm', 'quote-urls-at-prompt'})
|
||||
pixel_scroll: bool = True
|
||||
placement_strategy: choices_for_placement_strategy = 'center'
|
||||
|
||||
Reference in New Issue
Block a user