Bare literal assignments (drag_overlay_mode = 'axis_y') cause mypy to
narrow-infer the type as Literal['axis_y'] on the parent class, making
the subclass override (Horizontal = 'axis_x', Fat = 'axis_x') an
incompatible assignment. Fix by explicitly annotating Vertical and Tall
with the full union type from the base class, so the declared type stays
wide and subclasses can freely assign any valid mode.
Also removes unused ClassVar/Literal imports from splits.py.
Horizontal extends Vertical, and Fat extends Tall. Declaring
drag_overlay_mode with a narrower Literal type in the subclass
conflicts with the parent's declared type, causing mypy error
"Incompatible types in assignment". Since the base Layout class already
declares the full union type, subclasses only need a bare assignment.
Also removes now-unused ClassVar and Literal imports from vertical.py,
tall.py, and grid.py.
Previously, body drops in all non-Splits layouts showed a full-window overlay
and performed a positional swap. This adds proper top/bottom or left/right
half-window overlays and true before/after insertion for the five layouts
Kovid identified.
Architecture:
- New `drag_overlay_mode` ClassVar on Layout ('full'|'axis_y'|'axis_x'|'free')
controls both overlay display and valid direction axis. Layout subclasses set
one line; tabs.py and boss.py dispatch on this attribute instead of hasattr.
- New `insert_window_group_next_to(target_group_id, after)` on WindowList
performs a positional insert (not swap) by popping the active group and
inserting it before or after the target.
- New base `insert_window_next_to` on Layout uses insert_window_group_next_to
for axis_x/axis_y layouts and falls back to swap for 'full' (Stack).
Splits overrides this with its existing tree-based implementation.
- `_insert_window_in_direction` in boss.py collapses from a 7-line hasattr
branch to a single layout.insert_window_next_to() call.
Direction constraints:
Vertical, Tall, Grid -> top/bottom (axis_y)
Horizontal, Fat -> left/right (axis_x)
Splits -> 4-way free (unchanged)
Stack -> full-window swap (unchanged)
splits.py: insert_window_next_to called split_and_add on self.pairs_root
instead of on the pair found by pair_for_window. split_and_add only handles
direct children, so nested dest windows fell to 'else: self.two = pair',
silently replacing an entire subtree. Lost windows were re-added by
do_layout, producing phantom panes.
tabs.py: on_window_drop returned early (window not found) before calling
_clear_force_show_title_bars, leaving the drag overlay stuck on screen.
Implements drag-to-reorder for window title bars, following up on the
merged window title bar feature (#9450) and the design discussion in #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: #9619
- 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>
- 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>
The for loops iterating over edge_border() results in layout_pair() used
top, bottom, left, right as loop variable names, which shadowed the
function's local variables tracking current layout position. This caused
sibling panes to be positioned at incorrect offsets when splitting a pane
that was itself a nested Pair (e.g. splitting the left pane horizontally
would cause the right pane to shift down half the screen).
Rename loop variables to etop, ebottom, eleft, eright to avoid shadowing.
- 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>
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>
Enable Ctrl+left-click-drag to resize window splits. Detects which
windows border the click position using neighbor information from the
layout, then resizes in cell-sized increments as the mouse moves.
Shows a move cursor during the drag operation.
Closeskovidgoyal/kitty#5959
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Useful if user builds up session to save by running programs via
the shell.
Note that the serialization format for session files has changed
slightly, becoming more robust and allowing us to add more types
of saved data in the future, without overloading user_vars and thus
risking name conflicts.
This was needed to fix various corner cases when doing blending of colors
in linear space. The new architecture has the same performance as the
old in the common case of opaque rendering with no UI layers or images.
In the case of only positive z-index images there is a performance
decrease as the OS Window is now rendered to a offscreen texture and
then blitted to screen. However, in the future when we move to Vulkan or
I can figure out how to get Wayland to accept buffers with colors in
linear space, this performance penalty can be removed. The performance
penalty was not significant on my system but this is highly GPU
dependent. Modern GPUs are supposedly optimised for rendering to
offscreen buffers, so we will see. The awrit project might be a good
test case.
Now either we have 1-shot rendering for the case of opaque with only ext
or all the various pieces are rendered in successive draw calls into an
offscreen buffer that is blitted to the output buffer after all drawing
is done.
Fixes#8869