Add optional window title bars for window splits#9450
Conversation
|
Before I find time to review the actual code, just some high level comments.
|
|
Thank you for your feedback. I have pushed a commit addressing all four points:
|
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: kovidgoyal#9448 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename all options from pane_title_* to window_title_*
- Use foreground/background instead of fg/bg in color option names
- Change color options to to_color_or_none defaulting to None,
falling back to corresponding tab bar colors
- Add bell_symbol, activity_symbol, progress_percent template vars
using existing bell_on_tab and tab_activity_symbol options
- Add custom script support via window_title_bar.py in config dir
(draw_window_title function exposed as {custom} in templates)
- Update C structs, Python references, and regenerate config files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Hi @kovidgoyal — just a friendly ping on this. I've rebased onto the latest master and all tests pass. Let me know if there's anything else you'd like me to change, or if you have any concerns with the approach. Happy to wait if you're busy — just wanted to keep it on your radar. |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
no, it hasnt fallen off my radar. Indeed, am currently working on your |
|
OK, I reviewed the actual code. It's pretty inefficient, about what one would expect from an LLM. Here's a patch against child-monitor.c to improve the render loop. diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c
index 4a03205af..01aface8f 100644
--- a/kitty/child-monitor.c
+++ b/kitty/child-monitor.c
@@ -787,7 +787,12 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
set_maximum_wait(OPT(cursor_trail) - now + WD.screen->cursor->position_changed_by_client_at);
}
}
-
+ // Prepare window title bar screen data for GPU
+ WindowRenderData *trd = &w->window_title_render_data;
+ if (trd->screen) {
+ trd->screen->cursor_render_info.is_visible = false;
+ if (send_cell_data_to_gpu(trd->vao_idx, trd->screen, os_window)) needs_render = true;
+ }
} else {
if (WD.screen->cursor_render_info.render_even_when_unfocused) {
if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;
@@ -813,12 +818,6 @@ 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 window title bar screen data for GPU
- if (w->visible && w->window_title_render_data.screen) {
- CursorRenderInfo *cri = &w->window_title_render_data.screen->cursor_render_info;
- zero_at_ptr(cri);
- if (send_cell_data_to_gpu(w->window_title_render_data.vao_idx, w->window_title_render_data.screen, os_window)) needs_render = true;
- }
}
return needs_render || was_previously_rendered_with_layers != os_window->needs_layers;
}
@@ -878,15 +877,9 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co
if (is_active_window) active_window = w;
draw_cells(&WD, os_window, is_active_window, false, num_of_visible_windows == 1, w);
if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT);
- }
- }
- // Draw window title bars
- for (unsigned int i = 0; i < tab->num_windows; i++) {
- Window *w = tab->windows + i;
- if (w->visible && w->window_title_render_data.screen &&
- w->window_title_render_data.geometry.right > w->window_title_render_data.geometry.left &&
- w->window_title_render_data.geometry.bottom > w->window_title_render_data.geometry.top) {
- draw_cells(&w->window_title_render_data, os_window, i == tab->active_window, true, false, NULL);
+ WindowRenderData *trd = &w->window_title_render_data;
+ if (trd->screen && num_visible_windows > 1 && trd->geometry.right > trd->geometry.left && trd->geometry.bottom > trd->geometry.top)
+ draw_cells(trd, os_window, i == tab->active_window, true, false, NULL);
}
}
setup_os_window_for_rendering(os_window, tab, active_window, false);Then the whole approach of managing window geometry is very flawed. Problems with it:
IMO the whole layouting approach needs to be changed. Track the screen object used to render the title bar on each Window object instead of in a central manager class. Then at the start of the layout, set a property on every window object controlling if the titlebar should be present based on the option and number of visible windows in the tab being laid out. Then in Window.set_geometry set the titlebar render geometry and call set_window_render_data() with the changed geometry but keep self.geometry unchanged so that borders etc are drawn correctly. Remember to hide the title bar if the window has height of one row. This means that the python code and the C code now have different versions of the window geometry. You need to review all the various code paths this could affect, like mouse events and see what effects it has. The whole concept makes me rather nervous, this will require very careful global review. You also need to check what happens when the title bar is re-positioned or hidden/shown by reloading the config. Finally, the title bar should be hidden if the template evaluates to empty or whitespace only string, rather than showing a blank bar. Then if you are going to enable window titlebars users will want to be able to click on them, have a close button at least. And drag them around to re-order or move to another tab/OS Window like it is possible to drag tabs around. Which will lead to requests to always have the titlebar visible not just when there are more than one visible window. Whole can of worms. |
- Eliminate double set_geometry() call: removed _apply_window_title_bars() which post-processed geometry causing expensive SIGWINCH to children - Move title bar screen ownership to Window objects instead of central manager, with show_title_bar flag set during layout before do_layout() - Window.set_geometry() now handles title bar geometry internally: self.geometry stays at layout-computed value (borders/padding correct), only C-side render data diverges via adjusted top/bottom - Hide title bar for 1-row windows (ynum <= 1) - Hide title bar when template evaluates to empty/whitespace - Optimize C render loop: merge title bar GPU prep and draw into existing per-window loops, use trd pointer and is_visible=false, use num_visible_windows > 1 guard. Eliminates separate iteration passes. - Simplify WindowTitleBarManager to thin coordinator Note on C-side GPU prep placement: the suggested patch placed send_cell_data_to_gpu for title bars inside the is_active_window branch only. This caused a segfault (NULL deref in gleRunVertexSubmitImmediate) because inactive windows' title bars had valid screen/geometry but no GPU data uploaded, yet draw_cells was called for all visible title bars. Moved to the per-window visibility block alongside the main window's send_cell_data_to_gpu call so all visible title bars get GPU data prepared. The draw loop matches the suggested patch exactly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the detailed review — pushed a rework addressing all your feedback. C render loop (
|
|
Tab dragging was added to master a few weeks ago see I suggest another config option similar to tab_bar_min_tabs While your motivation is replicating tmux bars, I am 100% certain other |
- Add window_title_bar_min_windows (0=never, 1=always, 2+=threshold) similar to tab_bar_min_tabs, to control when title bars appear - Remove 'none' choice from window_title_bar so it purely controls position (top/bottom); disabling is now via min_windows 0 - Only hide title bar for truly empty template strings, not whitespace-only, so users can have intentionally blank bars Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Pushed f2ae5d0 addressing your latest feedback.
Thank you also for the helpful context on interactive bars. If draggable tabs are already implemented, I think that gives more motivation to make window title bars draggable if you'd like. The other main interactive feature I would like to implement (for both window titles and tabs, if not already implemented) is a double click to rename. Do you have thoughts on this? Cheers. |
|
Yes draggable window title bars would be an important feature, possibly As for double click to rename, sure, I have no objections, can be |
Implements drag-to-reorder for window title bars, following up on the merged window title bar feature (kovidgoyal#9450) and the design discussion in kovidgoyal#9619. - Drag a title bar and drop on another title bar to swap positions - Drop on a window body quadrant (left/right/top/bottom) to insert as a directional split; Splits layout uses insert_window_next_to(), other layouts fall back to move_window_to_group() - Drop on a tab bar tab to move the window into that tab - Drop on another OS window to move into its active tab - Drop outside kitty to detach into a new OS window - Tab bar highlights the hovered tab during a window drag, mirroring how the destination window title bar is highlighted - toggle_window_title_bars action temporarily force-shows title bars for drag-to-reorder when they are normally hidden, auto-hiding after the drag completes - window_title_bar_drag_threshold option (default 5px) controls how far the mouse must move before a drag is initiated; 0 disables dragging MIME type follows the same convention as tab dragging: application/net.kovidgoyal.kitty-window-{PID} Ref: kovidgoyal#9619
Implements drag-to-reorder for window title bars, following up on the merged window title bar feature (kovidgoyal#9450) and the design discussion in kovidgoyal#9619. - Drag a title bar and drop on another title bar to swap positions - Drop on a window body quadrant (left/right/top/bottom) to insert as a directional split; Splits layout uses insert_window_next_to(), other layouts fall back to move_window_to_group() - Drop on a tab bar tab to move the window into that tab - Drop on another OS window to move into its active tab - Drop outside kitty to detach into a new OS window - Tab bar highlights the hovered tab during a window drag, mirroring how the destination window title bar is highlighted - toggle_window_title_bars action temporarily force-shows title bars for drag-to-reorder when they are normally hidden, auto-hiding after the drag completes - window_title_bar_drag_threshold option (default 5px) controls how far the mouse must move before a drag is initiated; 0 disables dragging MIME type follows the same convention as tab dragging: application/net.kovidgoyal.kitty-window-{PID} Ref: kovidgoyal#9619
kitty >= 0.47 (PR kovidgoyal/kitty#9450) draws an optional title bar on each split. Enable it for tabs with 2+ splits and prefix the title with the split's number via a {custom} hook (window_title_bar.py): the hook reads the window's group index from iter_windows_with_number, the same numbering nth_window uses, so the displayed number is exactly what alt+N jumps to. Assisted-by: Claude:claude-fable-5
Summary
Screenobjects (same model as the tab bar) and automatically hidden when only a single window is visibleConfiguration options
Implementation
state.h,state.c,child-monitor.c): EachWindowstruct gains awindow_title_render_datafield with its own VAO. Cell data is uploaded to the GPU inprepare_to_render_os_windowand drawn inrender_prepared_os_window.layout/base.py):_apply_window_title_bars()shrinks each visible window's geometry by one cell height to make room for the title bar.window_title_bar.py):WindowTitleBarManagermanages oneWindowTitleBarScreenper visible window, handling template evaluation, SGR formatting, alignment, and cleanup.tabs.py): Title bars update on layout changes (relayout_borders) and title changes (title_changed).window_title_bar.pyscript in their kitty config directory with adraw_window_title(data)function, exposed as{custom}in templates (same pattern as tab bar'stab_bar.py).Changes since initial submission
Addressed all reviewer feedback from @kovidgoyal:
pane→windowin all option names and internalsforeground/backgroundinstead offg/bgin color option namesto_color_or_none, default toNone, fall back to corresponding tab bar colors (active_tab_foreground, etc.) — no theme updates neededwindow_title_bar.pyin config dir (mirrorstab_bar.pypattern)bell_on_tabandtab_activity_symboloptions, matching the tab bar's default templateTest plan
Ref: #9448
🤖 Generated with Claude Code