mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-07-03 11:12:30 +08: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:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
273
kitty/pane_title_bar.py
Normal file
273
kitty/pane_title_bar.py
Normal file
@@ -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 + '"""', '<pane_title_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()
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user