From ab3a8ca56ad33a2f34306648dd57e1bbaa13d0d9 Mon Sep 17 00:00:00 2001 From: mcrmck Date: Sun, 1 Feb 2026 00:37:40 -0500 Subject: [PATCH] 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 --- kitty/child-monitor.c | 15 + kitty/fast_data_types.pyi | 7 + kitty/layout/base.py | 20 ++ kitty/options/definition.py | 53 ++++ kitty/options/parse.py | 36 ++- kitty/options/types.py | 20 +- kitty/pane_title_bar.py | 273 +++++++++++++++++ kitty/state.c | 16 + kitty/state.h | 1 + kitty/tabs.py | 4 + tools/themes/collection.go | 572 ++++++++++++++++++------------------ 11 files changed, 731 insertions(+), 286 deletions(-) create mode 100644 kitty/pane_title_bar.py diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 504a7bd7f..6d52d970f 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -807,6 +807,12 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * if (send_cell_data_to_gpu(WD.vao_idx, WD.screen, os_window)) needs_render = true; if (WD.screen->start_visual_bell_at != 0) needs_render = true; } + // Prepare pane title bar screen data for GPU + if (w->visible && w->pane_title_render_data.screen) { + CursorRenderInfo *cri = &w->pane_title_render_data.screen->cursor_render_info; + zero_at_ptr(cri); + if (send_cell_data_to_gpu(w->pane_title_render_data.vao_idx, w->pane_title_render_data.screen, os_window)) needs_render = true; + } } return needs_render || was_previously_rendered_with_layers != os_window->needs_layers; } @@ -868,6 +874,15 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT); } } + // Draw pane title bars + for (unsigned int i = 0; i < tab->num_windows; i++) { + Window *w = tab->windows + i; + if (w->visible && w->pane_title_render_data.screen && + w->pane_title_render_data.geometry.right > w->pane_title_render_data.geometry.left && + w->pane_title_render_data.geometry.bottom > w->pane_title_render_data.geometry.top) { + draw_cells(&w->pane_title_render_data, os_window, i == tab->active_window, true, false, NULL); + } + } setup_os_window_for_rendering(os_window, tab, active_window, false); if (global_state.thumbnail_callback.os_window == os_window->id) { thumbnail_callback(os_window); diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index d826597e7..c3a0eec68 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1404,6 +1404,13 @@ def set_tab_bar_render_data( pass +def set_pane_title_bar_render_data( + os_window_id: int, tab_id: int, window_id: int, screen: Screen, + left: int, top: int, right: int, bottom: int +) -> None: + pass + + def set_window_render_data( os_window_id: int, tab_id: int, window_id: int, screen: Screen, left: int, top: int, right: int, bottom: int, diff --git a/kitty/layout/base.py b/kitty/layout/base.py index 62d0098dd..667fdfdf9 100644 --- a/kitty/layout/base.py +++ b/kitty/layout/base.py @@ -368,6 +368,26 @@ class Layout: self.update_visibility(all_windows) self.blank_rects = [] self.do_layout(all_windows) + self._apply_pane_title_bars(all_windows) + + def _apply_pane_title_bars(self, all_windows: WindowList) -> None: + opts = get_options() + position = opts.pane_title_bar + if position == 'none': + return + visible_groups = list(all_windows.iter_all_layoutable_groups(only_visible=True)) + if len(visible_groups) < 2: + return + ch = lgd.cell_height + for wg in visible_groups: + geom = wg.geometry + if geom is None: + continue + if position == 'top': + new_geom = geom._replace(top=geom.top + ch, ynum=max(1, geom.ynum - 1)) + else: + new_geom = geom._replace(bottom=geom.bottom - ch, ynum=max(1, geom.ynum - 1)) + wg.set_geometry(new_geom) def layout_single_window_group(self, wg: WindowGroup, add_blank_rects: bool = True) -> None: bw = 1 if self.must_draw_borders else 0 diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 0c24ca30a..409e619de 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -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() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index ff7e3f0aa..8f8249ba3 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -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) diff --git a/kitty/options/types.py b/kitty/options/types.py index 8272f0c70..a6711a79a 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -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' diff --git a/kitty/pane_title_bar.py b/kitty/pane_title_bar.py new file mode 100644 index 000000000..c3953af62 --- /dev/null +++ b/kitty/pane_title_bar.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +# License: GPL v3 Copyright: 2024, kitty contributors + +from functools import lru_cache +from typing import Any, NamedTuple + +from .fast_data_types import ( + DECAWM, + Screen, + cell_size_for_window, + get_options, + set_pane_title_bar_render_data, +) +from .rgb import color_as_sgr, color_from_int, to_color +from .types import WindowGeometry +from .utils import color_as_int, log_error, sgr_sanitizer_pat +from .window_list import WindowList + + +@lru_cache +def _report_template_failure(template: str, e: str) -> None: + log_error(f'Invalid pane title template: "{template}" with error: {e}') + + +@lru_cache +def _compile_template(template: str) -> Any: + try: + return compile('f"""' + template + '"""', '', 'eval') + except Exception as e: + _report_template_failure(template, str(e)) + + +safe_builtins = { + 'max': max, 'min': min, 'str': str, 'repr': repr, 'abs': abs, + 'len': len, 'chr': chr, 'ord': ord, +} + + +class PaneTitleColorFormatter: + is_active: bool = False + + def __init__(self, which: str): + self.which = which + + def __getattr__(self, name: str) -> str: + q = name + if q == 'default': + ans = '9' + elif q == 'pane': + opts = get_options() + if self.is_active: + col = color_from_int(color_as_int(opts.pane_title_bar_active_fg if self.which == '3' else opts.pane_title_bar_active_bg)) + else: + col = color_from_int(color_as_int(opts.pane_title_bar_inactive_fg if self.which == '3' else opts.pane_title_bar_inactive_bg)) + ans = f'8{color_as_sgr(col)}' + elif q.startswith('color'): + ans = f'8:5:{int(q[5:])}' + else: + if name.startswith('_'): + q = f'#{name[1:]}' + c = to_color(q) + if c is None: + raise AttributeError(f'{name} is not a valid color') + ans = f'8{color_as_sgr(c)}' + return f'\x1b[{self.which}{ans}m' + + +class PaneTitleFormatter: + reset = '\x1b[0m' + fg = PaneTitleColorFormatter('3') + bg = PaneTitleColorFormatter('4') + bold = '\x1b[1m' + nobold = '\x1b[22m' + italic = '\x1b[3m' + noitalic = '\x1b[23m' + + +def _draw_attributed_string(title: str, screen: Screen) -> None: + if '\x1b' in title: + for x in sgr_sanitizer_pat(for_splitting=True).split(title): + if x.startswith('\x1b') and x.endswith('m'): + screen.apply_sgr(x[2:-1]) + else: + screen.draw(x) + else: + screen.draw(title) + + +class PaneTitleData(NamedTuple): + title: str + is_active: bool + window_id: int + tab_id: int + + +class PaneTitleBarScreen: + def __init__(self, os_window_id: int, cell_width: int, cell_height: int): + self.os_window_id = os_window_id + self.cell_width = cell_width + self.screen = Screen(None, 1, 10, 0, cell_width, cell_height) + self.screen.reset_mode(DECAWM) + + def layout(self, geometry: WindowGeometry) -> None: + ncells = max(4, (geometry.right - geometry.left) // self.cell_width) + self.screen.resize(1, ncells) + self.geometry = geometry + + def render(self, data: PaneTitleData) -> None: + opts = get_options() + s = self.screen + s.cursor.x = 0 + s.erase_in_line(2, False) + + is_active = data.is_active + if is_active: + s.color_profile.default_fg = opts.pane_title_bar_active_fg + s.color_profile.default_bg = opts.pane_title_bar_active_bg + fg = (color_as_int(opts.pane_title_bar_active_fg) << 8) | 2 + bg = (color_as_int(opts.pane_title_bar_active_bg) << 8) | 2 + else: + s.color_profile.default_fg = opts.pane_title_bar_inactive_fg + s.color_profile.default_bg = opts.pane_title_bar_inactive_bg + fg = (color_as_int(opts.pane_title_bar_inactive_fg) << 8) | 2 + bg = (color_as_int(opts.pane_title_bar_inactive_bg) << 8) | 2 + + s.cursor.fg = fg + s.cursor.bg = bg + + template = opts.pane_title_template + if is_active and opts.active_pane_title_template and opts.active_pane_title_template != 'none': + template = opts.active_pane_title_template + + PaneTitleColorFormatter.is_active = is_active + eval_locals = { + 'title': data.title, + 'is_active': is_active, + 'fmt': PaneTitleFormatter, + } + try: + title = eval(_compile_template(template), {'__builtins__': safe_builtins}, eval_locals) + except Exception as e: + _report_template_failure(template, str(e)) + title = data.title + + title_str = str(title) + align = opts.pane_title_bar_align + + if align == 'left': + _draw_attributed_string(title_str, s) + else: + # Measure the title length by drawing to cursor position 0 + # and checking where the cursor ends up + _draw_attributed_string(title_str, s) + title_len = s.cursor.x + s.cursor.x = 0 + s.erase_in_line(2, False) + s.cursor.fg = fg + s.cursor.bg = bg + + if align == 'center': + pad = max(0, (s.columns - title_len) // 2) + else: # right + pad = max(0, s.columns - title_len) + + for _ in range(pad): + s.draw(' ') + _draw_attributed_string(title_str, s) + + # Fill remaining cells with background + while s.cursor.x < s.columns: + s.draw(' ') + + +class PaneTitleBarManager: + + def __init__(self, os_window_id: int, tab_id: int): + self.os_window_id = os_window_id + self.tab_id = tab_id + self._screens: dict[int, PaneTitleBarScreen] = {} + + def _clear_all(self) -> None: + for wid, pts in self._screens.items(): + # Zero geometry so the C render loop skips drawing + set_pane_title_bar_render_data( + self.os_window_id, self.tab_id, wid, pts.screen, + 0, 0, 0, 0, + ) + self._screens.clear() + + def update(self, all_windows: WindowList) -> None: + opts = get_options() + position = opts.pane_title_bar + if position == 'none': + if self._screens: + self._clear_all() + return + + visible_groups = list(all_windows.iter_all_layoutable_groups(only_visible=True)) + if len(visible_groups) < 2: + if self._screens: + self._clear_all() + return + + cell_width, cell_height = cell_size_for_window(self.os_window_id) + active_group = all_windows.active_group + seen_window_ids: set[int] = set() + + for wg in visible_groups: + geom = wg.geometry + if geom is None: + continue + + window = wg.windows[-1] if wg.windows else None + if window is None: + continue + + # Validate geometry has enough space for a title bar + if geom.right <= geom.left or geom.bottom <= geom.top: + continue + if position == 'top' and geom.top < cell_height: + continue + if position == 'bottom' and geom.bottom + cell_height < geom.bottom: # overflow check + continue + + wid = window.id + seen_window_ids.add(wid) + + if wid not in self._screens: + self._screens[wid] = PaneTitleBarScreen(self.os_window_id, cell_width, cell_height) + + pts = self._screens[wid] + + # Calculate title bar geometry + if position == 'top': + title_geom = WindowGeometry( + left=geom.left, + top=geom.top - cell_height, + right=geom.right, + bottom=geom.top, + xnum=0, ynum=1, + ) + else: + title_geom = WindowGeometry( + left=geom.left, + top=geom.bottom, + right=geom.right, + bottom=geom.bottom + cell_height, + xnum=0, ynum=1, + ) + + pts.layout(title_geom) + + is_active = wg is active_group + data = PaneTitleData( + title=window.title or '', + is_active=is_active, + window_id=wid, + tab_id=self.tab_id, + ) + pts.render(data) + + set_pane_title_bar_render_data( + self.os_window_id, self.tab_id, wid, pts.screen, + title_geom.left, title_geom.top, title_geom.right, title_geom.bottom, + ) + + # Clean up screens for windows that are no longer visible + stale = set(self._screens) - seen_window_ids + for wid in stale: + del self._screens[wid] + + def destroy(self) -> None: + self._screens.clear() diff --git a/kitty/state.c b/kitty/state.c index e0e5fc0cb..e37512e4c 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -255,12 +255,16 @@ add_tab(id_type os_window_id) { static void create_gpu_resources_for_window(Window *w) { w->render_data.vao_idx = create_cell_vao(); + w->pane_title_render_data.vao_idx = create_cell_vao(); } static void release_gpu_resources_for_window(Window *w) { if (w->render_data.vao_idx > -1) remove_vao(w->render_data.vao_idx); w->render_data.vao_idx = -1; + if (w->pane_title_render_data.vao_idx > -1) remove_vao(w->pane_title_render_data.vao_idx); + w->pane_title_render_data.vao_idx = -1; + Py_CLEAR(w->pane_title_render_data.screen); } static bool @@ -855,6 +859,17 @@ PYWRAP1(set_tab_bar_render_data) { Py_RETURN_NONE; } +PYWRAP1(set_pane_title_bar_render_data) { + WindowGeometry g; + id_type os_window_id, tab_id, window_id; + Screen *screen; + PA("KKKOIIII", &os_window_id, &tab_id, &window_id, &screen, &g.left, &g.top, &g.right, &g.bottom); + WITH_WINDOW(os_window_id, tab_id, window_id) + init_window_render_data(&window->pane_title_render_data, g, screen); + END_WITH_WINDOW + Py_RETURN_NONE; +} + static PyTypeObject RegionType; static PyStructSequence_Field region_fields[] = { {"left", ""}, {"top", ""}, {"right", ""}, {"bottom", ""}, {"width", ""}, {"height", ""}, {NULL, NULL} @@ -1592,6 +1607,7 @@ static PyMethodDef module_methods[] = { MW(reorder_tabs, METH_VARARGS), MW(set_borders_rects, METH_VARARGS), MW(set_tab_bar_render_data, METH_VARARGS), + MW(set_pane_title_bar_render_data, METH_VARARGS), MW(set_window_render_data, METH_VARARGS), MW(set_window_padding, METH_VARARGS), MW(viewport_for_window, METH_VARARGS), diff --git a/kitty/state.h b/kitty/state.h index 38c8a3cc9..05aec3b44 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -208,6 +208,7 @@ typedef struct Window { bool visible; PyObject *title; WindowRenderData render_data; + WindowRenderData pane_title_render_data; WindowLogoRenderData window_logo; MousePosition mouse_pos; struct { diff --git a/kitty/tabs.py b/kitty/tabs.py index 7ba37c54a..217f73cd2 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -15,6 +15,7 @@ from gettext import gettext as _ from typing import Any, Concatenate, Deque, NamedTuple, Optional, ParamSpec, TypeVar, cast from .borders import Border, Borders +from .pane_title_bar import PaneTitleBarManager from .child import Child from .cli_stub import CLIOptions, SaveAsSessionOptions from .constants import appname @@ -170,6 +171,7 @@ class Tab: # {{{ self.name = getattr(session_tab, 'name', '') self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or get_options().enabled_layouts] self.borders = Borders(self.os_window_id, self.id) + self.pane_title_bar_manager = PaneTitleBarManager(self.os_window_id, self.id) self.windows: WindowList = WindowList(self) self._last_used_layout: str | None = None self._current_layout_name: str | None = None @@ -440,6 +442,7 @@ class Tab: # {{{ self.mark_tab_bar_dirty() def title_changed(self, window: Window) -> None: + self.pane_title_bar_manager.update(self.windows) if window is self.active_window: tm = self.tab_manager_ref() if tm is not None: @@ -468,6 +471,7 @@ class Tab: # {{{ current_layout=ly, tab_bar_rects=tm.tab_bar_rects, draw_window_borders=draw_borders ) + self.pane_title_bar_manager.update(self.windows) def create_layout_object(self, name: str) -> Layout: return create_layout_object_for(name, self.os_window_id, self.id) diff --git a/tools/themes/collection.go b/tools/themes/collection.go index 6191c92e6..5ba3fb4d1 100644 --- a/tools/themes/collection.go +++ b/tools/themes/collection.go @@ -35,290 +35,294 @@ var _ = fmt.Print var AllColorSettingNames = map[string]bool{ // {{{ // generated by gen-config.py do not edit // ALL_COLORS_START - "active_border_color": true, - "active_tab_background": true, - "active_tab_foreground": true, - "background": true, - "bell_border_color": true, - "color0": true, - "color1": true, - "color10": true, - "color100": true, - "color101": true, - "color102": true, - "color103": true, - "color104": true, - "color105": true, - "color106": true, - "color107": true, - "color108": true, - "color109": true, - "color11": true, - "color110": true, - "color111": true, - "color112": true, - "color113": true, - "color114": true, - "color115": true, - "color116": true, - "color117": true, - "color118": true, - "color119": true, - "color12": true, - "color120": true, - "color121": true, - "color122": true, - "color123": true, - "color124": true, - "color125": true, - "color126": true, - "color127": true, - "color128": true, - "color129": true, - "color13": true, - "color130": true, - "color131": true, - "color132": true, - "color133": true, - "color134": true, - "color135": true, - "color136": true, - "color137": true, - "color138": true, - "color139": true, - "color14": true, - "color140": true, - "color141": true, - "color142": true, - "color143": true, - "color144": true, - "color145": true, - "color146": true, - "color147": true, - "color148": true, - "color149": true, - "color15": true, - "color150": true, - "color151": true, - "color152": true, - "color153": true, - "color154": true, - "color155": true, - "color156": true, - "color157": true, - "color158": true, - "color159": true, - "color16": true, - "color160": true, - "color161": true, - "color162": true, - "color163": true, - "color164": true, - "color165": true, - "color166": true, - "color167": true, - "color168": true, - "color169": true, - "color17": true, - "color170": true, - "color171": true, - "color172": true, - "color173": true, - "color174": true, - "color175": true, - "color176": true, - "color177": true, - "color178": true, - "color179": true, - "color18": true, - "color180": true, - "color181": true, - "color182": true, - "color183": true, - "color184": true, - "color185": true, - "color186": true, - "color187": true, - "color188": true, - "color189": true, - "color19": true, - "color190": true, - "color191": true, - "color192": true, - "color193": true, - "color194": true, - "color195": true, - "color196": true, - "color197": true, - "color198": true, - "color199": true, - "color2": true, - "color20": true, - "color200": true, - "color201": true, - "color202": true, - "color203": true, - "color204": true, - "color205": true, - "color206": true, - "color207": true, - "color208": true, - "color209": true, - "color21": true, - "color210": true, - "color211": true, - "color212": true, - "color213": true, - "color214": true, - "color215": true, - "color216": true, - "color217": true, - "color218": true, - "color219": true, - "color22": true, - "color220": true, - "color221": true, - "color222": true, - "color223": true, - "color224": true, - "color225": true, - "color226": true, - "color227": true, - "color228": true, - "color229": true, - "color23": true, - "color230": true, - "color231": true, - "color232": true, - "color233": true, - "color234": true, - "color235": true, - "color236": true, - "color237": true, - "color238": true, - "color239": true, - "color24": true, - "color240": true, - "color241": true, - "color242": true, - "color243": true, - "color244": true, - "color245": true, - "color246": true, - "color247": true, - "color248": true, - "color249": true, - "color25": true, - "color250": true, - "color251": true, - "color252": true, - "color253": true, - "color254": true, - "color255": true, - "color26": true, - "color27": true, - "color28": true, - "color29": true, - "color3": true, - "color30": true, - "color31": true, - "color32": true, - "color33": true, - "color34": true, - "color35": true, - "color36": true, - "color37": true, - "color38": true, - "color39": true, - "color4": true, - "color40": true, - "color41": true, - "color42": true, - "color43": true, - "color44": true, - "color45": true, - "color46": true, - "color47": true, - "color48": true, - "color49": true, - "color5": true, - "color50": true, - "color51": true, - "color52": true, - "color53": true, - "color54": true, - "color55": true, - "color56": true, - "color57": true, - "color58": true, - "color59": true, - "color6": true, - "color60": true, - "color61": true, - "color62": true, - "color63": true, - "color64": true, - "color65": true, - "color66": true, - "color67": true, - "color68": true, - "color69": true, - "color7": true, - "color70": true, - "color71": true, - "color72": true, - "color73": true, - "color74": true, - "color75": true, - "color76": true, - "color77": true, - "color78": true, - "color79": true, - "color8": true, - "color80": true, - "color81": true, - "color82": true, - "color83": true, - "color84": true, - "color85": true, - "color86": true, - "color87": true, - "color88": true, - "color89": true, - "color9": true, - "color90": true, - "color91": true, - "color92": true, - "color93": true, - "color94": true, - "color95": true, - "color96": true, - "color97": true, - "color98": true, - "color99": true, - "cursor": true, - "cursor_text_color": true, - "cursor_trail_color": true, - "foreground": true, - "inactive_border_color": true, - "inactive_tab_background": true, - "inactive_tab_foreground": true, - "macos_titlebar_color": true, - "mark1_background": true, - "mark1_foreground": true, - "mark2_background": true, - "mark2_foreground": true, - "mark3_background": true, - "mark3_foreground": true, - "scrollbar_handle_color": true, - "scrollbar_track_color": true, - "selection_background": true, - "selection_foreground": true, - "tab_bar_background": true, - "tab_bar_margin_color": true, - "url_color": true, - "visual_bell_color": true, - "wayland_titlebar_color": true, // ALL_COLORS_END + "active_border_color": true, + "active_tab_background": true, + "active_tab_foreground": true, + "background": true, + "bell_border_color": true, + "color0": true, + "color1": true, + "color10": true, + "color100": true, + "color101": true, + "color102": true, + "color103": true, + "color104": true, + "color105": true, + "color106": true, + "color107": true, + "color108": true, + "color109": true, + "color11": true, + "color110": true, + "color111": true, + "color112": true, + "color113": true, + "color114": true, + "color115": true, + "color116": true, + "color117": true, + "color118": true, + "color119": true, + "color12": true, + "color120": true, + "color121": true, + "color122": true, + "color123": true, + "color124": true, + "color125": true, + "color126": true, + "color127": true, + "color128": true, + "color129": true, + "color13": true, + "color130": true, + "color131": true, + "color132": true, + "color133": true, + "color134": true, + "color135": true, + "color136": true, + "color137": true, + "color138": true, + "color139": true, + "color14": true, + "color140": true, + "color141": true, + "color142": true, + "color143": true, + "color144": true, + "color145": true, + "color146": true, + "color147": true, + "color148": true, + "color149": true, + "color15": true, + "color150": true, + "color151": true, + "color152": true, + "color153": true, + "color154": true, + "color155": true, + "color156": true, + "color157": true, + "color158": true, + "color159": true, + "color16": true, + "color160": true, + "color161": true, + "color162": true, + "color163": true, + "color164": true, + "color165": true, + "color166": true, + "color167": true, + "color168": true, + "color169": true, + "color17": true, + "color170": true, + "color171": true, + "color172": true, + "color173": true, + "color174": true, + "color175": true, + "color176": true, + "color177": true, + "color178": true, + "color179": true, + "color18": true, + "color180": true, + "color181": true, + "color182": true, + "color183": true, + "color184": true, + "color185": true, + "color186": true, + "color187": true, + "color188": true, + "color189": true, + "color19": true, + "color190": true, + "color191": true, + "color192": true, + "color193": true, + "color194": true, + "color195": true, + "color196": true, + "color197": true, + "color198": true, + "color199": true, + "color2": true, + "color20": true, + "color200": true, + "color201": true, + "color202": true, + "color203": true, + "color204": true, + "color205": true, + "color206": true, + "color207": true, + "color208": true, + "color209": true, + "color21": true, + "color210": true, + "color211": true, + "color212": true, + "color213": true, + "color214": true, + "color215": true, + "color216": true, + "color217": true, + "color218": true, + "color219": true, + "color22": true, + "color220": true, + "color221": true, + "color222": true, + "color223": true, + "color224": true, + "color225": true, + "color226": true, + "color227": true, + "color228": true, + "color229": true, + "color23": true, + "color230": true, + "color231": true, + "color232": true, + "color233": true, + "color234": true, + "color235": true, + "color236": true, + "color237": true, + "color238": true, + "color239": true, + "color24": true, + "color240": true, + "color241": true, + "color242": true, + "color243": true, + "color244": true, + "color245": true, + "color246": true, + "color247": true, + "color248": true, + "color249": true, + "color25": true, + "color250": true, + "color251": true, + "color252": true, + "color253": true, + "color254": true, + "color255": true, + "color26": true, + "color27": true, + "color28": true, + "color29": true, + "color3": true, + "color30": true, + "color31": true, + "color32": true, + "color33": true, + "color34": true, + "color35": true, + "color36": true, + "color37": true, + "color38": true, + "color39": true, + "color4": true, + "color40": true, + "color41": true, + "color42": true, + "color43": true, + "color44": true, + "color45": true, + "color46": true, + "color47": true, + "color48": true, + "color49": true, + "color5": true, + "color50": true, + "color51": true, + "color52": true, + "color53": true, + "color54": true, + "color55": true, + "color56": true, + "color57": true, + "color58": true, + "color59": true, + "color6": true, + "color60": true, + "color61": true, + "color62": true, + "color63": true, + "color64": true, + "color65": true, + "color66": true, + "color67": true, + "color68": true, + "color69": true, + "color7": true, + "color70": true, + "color71": true, + "color72": true, + "color73": true, + "color74": true, + "color75": true, + "color76": true, + "color77": true, + "color78": true, + "color79": true, + "color8": true, + "color80": true, + "color81": true, + "color82": true, + "color83": true, + "color84": true, + "color85": true, + "color86": true, + "color87": true, + "color88": true, + "color89": true, + "color9": true, + "color90": true, + "color91": true, + "color92": true, + "color93": true, + "color94": true, + "color95": true, + "color96": true, + "color97": true, + "color98": true, + "color99": true, + "cursor": true, + "cursor_text_color": true, + "cursor_trail_color": true, + "foreground": true, + "inactive_border_color": true, + "inactive_tab_background": true, + "inactive_tab_foreground": true, + "macos_titlebar_color": true, + "mark1_background": true, + "mark1_foreground": true, + "mark2_background": true, + "mark2_foreground": true, + "mark3_background": true, + "mark3_foreground": true, + "pane_title_bar_active_bg": true, + "pane_title_bar_active_fg": true, + "pane_title_bar_inactive_bg": true, + "pane_title_bar_inactive_fg": true, + "scrollbar_handle_color": true, + "scrollbar_track_color": true, + "selection_background": true, + "selection_foreground": true, + "tab_bar_background": true, + "tab_bar_margin_color": true, + "url_color": true, + "visual_bell_color": true, + "wayland_titlebar_color": true, // ALL_COLORS_END } // }}} type JSONMetadata struct {