Merge branch 'pane-title-bar' of https://github.com/mcrmck/kitty

This commit is contained in:
Kovid Goyal
2026-03-05 08:31:50 +05:30
14 changed files with 881 additions and 299 deletions

View File

@@ -1466,6 +1466,81 @@ 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('window_title_bar', 'top',
choices=('top', 'bottom'),
long_text='''
Control the position of the window title bar relative to the window content.
Use :opt:`window_title_bar_min_windows` to control when title bars are shown.
'''
)
opt('window_title_bar_min_windows', '0',
option_type='positive_int',
long_text='''
The minimum number of visible windows in a tab before window title bars
are shown. A value of :code:`0` means never show. :code:`1` means always
show. :code:`2` or more means show only when at least that many windows
are visible. Similar to :opt:`tab_bar_min_tabs` for the tab bar.
'''
)
opt('window_title_template', '"{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.window}{progress_percent}{title}"',
option_type='tab_title_template',
long_text='''
A template to render the window title bar text. Uses the same template syntax as
:opt:`tab_title_template`. Available variables include: :code:`{title}`,
:code:`{bell_symbol}`, :code:`{activity_symbol}`, :code:`{progress_percent}`,
:code:`{custom}`, :code:`{fmt}`, :code:`{is_active}`.
You can also provide a custom :code:`draw_window_title(data)` function in
:file:`window_title_bar.py` in the kitty config directory, exposed as :code:`{custom}`.
'''
)
opt('active_window_title_template', 'none',
option_type='tab_title_template',
long_text='''
Template to use for the active window title bar. If not set (the value
:code:`none`), the :opt:`window_title_template` is used.
'''
)
opt('window_title_bar_active_foreground', 'none',
option_type='to_color_or_none', ctype='color_or_none_as_int',
long_text='''
Foreground color for the active window title bar. Defaults to
the corresponding tab bar color (:code:`active_tab_foreground`) when set to :code:`none`.
'''
)
opt('window_title_bar_active_background', 'none',
option_type='to_color_or_none', ctype='color_or_none_as_int',
long_text='''
Background color for the active window title bar. Defaults to
the corresponding tab bar color (:code:`active_tab_background`) when set to :code:`none`.
'''
)
opt('window_title_bar_inactive_foreground', 'none',
option_type='to_color_or_none', ctype='color_or_none_as_int',
long_text='''
Foreground color for inactive window title bars. Defaults to
the corresponding tab bar color (:code:`inactive_tab_foreground`) when set to :code:`none`.
'''
)
opt('window_title_bar_inactive_background', 'none',
option_type='to_color_or_none', ctype='color_or_none_as_int',
long_text='''
Background color for inactive window title bars. Defaults to
the corresponding tab bar color (:code:`inactive_tab_background`) when set to :code:`none`.
'''
)
opt('window_title_bar_align', 'center',
choices=('left', 'center', 'right'),
long_text='Horizontal alignment of the text in window title bars.'
)
egr() # }}}

37
kitty/options/parse.py generated
View File

@@ -48,6 +48,9 @@ class Parser:
def active_tab_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['active_tab_title_template'] = active_tab_title_template(val)
def active_window_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['active_window_title_template'] = tab_title_template(val)
def allow_cloning(self, val: str, ans: dict[str, typing.Any]) -> None:
val = val.lower()
if val not in self.choices_for_allow_cloning:
@@ -1504,6 +1507,40 @@ class Parser:
def window_resize_step_lines(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_resize_step_lines'] = positive_int(val)
def window_title_bar(self, val: str, ans: dict[str, typing.Any]) -> None:
val = val.lower()
if val not in self.choices_for_window_title_bar:
raise ValueError(f"The value {val} is not a valid choice for window_title_bar")
ans["window_title_bar"] = val
choices_for_window_title_bar = frozenset(('top', 'bottom'))
def window_title_bar_min_windows(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_title_bar_min_windows'] = positive_int(val)
def window_title_bar_active_background(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_title_bar_active_background'] = to_color_or_none(val)
def window_title_bar_active_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_title_bar_active_foreground'] = to_color_or_none(val)
def window_title_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None:
val = val.lower()
if val not in self.choices_for_window_title_bar_align:
raise ValueError(f"The value {val} is not a valid choice for window_title_bar_align")
ans["window_title_bar_align"] = val
choices_for_window_title_bar_align = choices_for_tab_bar_align
def window_title_bar_inactive_background(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_title_bar_inactive_background'] = to_color_or_none(val)
def window_title_bar_inactive_foreground(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_title_bar_inactive_foreground'] = to_color_or_none(val)
def window_title_template(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['window_title_template'] = tab_title_template(val)
def x11_hide_window_decorations(self, val: str, ans: dict[str, typing.Any]) -> None:
deprecated_hide_window_decorations_aliases('x11_hide_window_decorations', val, ans)

View File

@@ -993,6 +993,58 @@ convert_from_opts_window_drag_tolerance(PyObject *py_opts, Options *opts) {
Py_DECREF(ret);
}
static void
convert_from_python_window_title_bar_active_foreground(PyObject *val, Options *opts) {
opts->window_title_bar_active_foreground = color_or_none_as_int(val);
}
static void
convert_from_opts_window_title_bar_active_foreground(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_active_foreground");
if (ret == NULL) return;
convert_from_python_window_title_bar_active_foreground(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_window_title_bar_active_background(PyObject *val, Options *opts) {
opts->window_title_bar_active_background = color_or_none_as_int(val);
}
static void
convert_from_opts_window_title_bar_active_background(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_active_background");
if (ret == NULL) return;
convert_from_python_window_title_bar_active_background(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_window_title_bar_inactive_foreground(PyObject *val, Options *opts) {
opts->window_title_bar_inactive_foreground = color_or_none_as_int(val);
}
static void
convert_from_opts_window_title_bar_inactive_foreground(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_inactive_foreground");
if (ret == NULL) return;
convert_from_python_window_title_bar_inactive_foreground(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_window_title_bar_inactive_background(PyObject *val, Options *opts) {
opts->window_title_bar_inactive_background = color_or_none_as_int(val);
}
static void
convert_from_opts_window_title_bar_inactive_background(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_inactive_background");
if (ret == NULL) return;
convert_from_python_window_title_bar_inactive_background(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_tab_bar_edge(PyObject *val, Options *opts) {
opts->tab_bar_edge = PyLong_AsLong(val);
@@ -1550,6 +1602,14 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
if (PyErr_Occurred()) return false;
convert_from_opts_window_drag_tolerance(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_window_title_bar_active_foreground(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_window_title_bar_active_background(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_window_title_bar_inactive_foreground(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_window_title_bar_inactive_background(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_tab_bar_edge(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_tab_bar_margin_height(py_opts, opts);

24
kitty/options/types.py generated
View File

@@ -37,6 +37,8 @@ choices_for_terminfo_type = typing.Literal['path', 'direct', 'none']
choices_for_undercurl_style = typing.Literal['thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense']
choices_for_underline_hyperlinks = typing.Literal['hover', 'always', 'never']
choices_for_window_logo_position = choices_for_placement_strategy
choices_for_window_title_bar = typing.Literal['top', 'bottom']
choices_for_window_title_bar_align = choices_for_tab_bar_align
option_names = (
'action_alias',
@@ -45,6 +47,7 @@ option_names = (
'active_tab_font_style',
'active_tab_foreground',
'active_tab_title_template',
'active_window_title_template',
'allow_cloning',
'allow_hyperlinks',
'allow_remote_control',
@@ -497,6 +500,14 @@ option_names = (
'window_padding_width',
'window_resize_step_cells',
'window_resize_step_lines',
'window_title_bar',
'window_title_bar_min_windows',
'window_title_bar_active_background',
'window_title_bar_active_foreground',
'window_title_bar_align',
'window_title_bar_inactive_background',
'window_title_bar_inactive_foreground',
'window_title_template',
)
@@ -506,6 +517,7 @@ class Options:
active_tab_font_style: tuple[bool, bool] = (True, True)
active_tab_foreground: Color = Color(0, 0, 0)
active_tab_title_template: str | None = None
active_window_title_template: str = 'none'
allow_cloning: choices_for_allow_cloning = 'ask'
allow_hyperlinks: int = 1
allow_remote_control: choices_for_allow_remote_control = 'no'
@@ -689,6 +701,14 @@ class Options:
window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0)
window_resize_step_cells: int = 2
window_resize_step_lines: int = 2
window_title_bar: choices_for_window_title_bar = 'top'
window_title_bar_min_windows: int = 0
window_title_bar_active_background: kitty.fast_data_types.Color | None = None
window_title_bar_active_foreground: kitty.fast_data_types.Color | None = None
window_title_bar_align: choices_for_window_title_bar_align = 'center'
window_title_bar_inactive_background: kitty.fast_data_types.Color | None = None
window_title_bar_inactive_foreground: kitty.fast_data_types.Color | None = None
window_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.window}{progress_percent}{title}'
action_alias: dict[str, str] = {}
env: dict[str, str] = {}
exe_search_path: dict[str, str] = {}
@@ -1111,6 +1131,10 @@ nullable_colors = frozenset({
'cursor_trail_color',
'visual_bell_color',
'active_border_color',
'window_title_bar_active_foreground',
'window_title_bar_active_background',
'window_title_bar_inactive_foreground',
'window_title_bar_inactive_background',
'tab_bar_background',
'tab_bar_margin_color',
'selection_foreground',