Fixes found while reviewing #10471:
- A window squeezed to zero size (by maximize or an extreme bias) was
indistinguishable from a removed one, so closing an unrelated window
handed its share to windows in sub-trees that lost nothing. Track
survivors separately from the numeric weight.
- Pruning a batch of windows gave a different layout than closing them
one by one, since a perpendicular sub-tree stops being perpendicular
once removals collapse it. Apply the policy one window at a time.
- Windows reconciled by do_layout(), rather than split off an existing
window, bypassed the policy entirely. Route them through it too.
Also fix two pre-existing bugs on the same code paths:
- collapse_redundant_pairs() collapsed top down, so a pair whose
children only became empty during the recursion was never
re-examined and was left in the tree, consuming its share of the
space.
- do_layout() kept using a stale pairs root when removing windows
collapsed the tree, so windows added in the same relayout were
attached to the discarded root and vanished for a frame.
save_as_session recorded only the state of the tab's active layout, so the state
of any layout the tab had been toggled away from was lost. Toggling a splits tab
to the stack layout and saving discarded the splits tree entirely, because Stack
has no layout_state() of its own, and switching back to splits after restoring
rebuilt the tree from scratch along the default axis.
The session file also carried no last_used_layout, so _last_used_layout was None
after a restore and toggle_layout <current layout> silently did nothing, since it
delegates to last_used_layout() which returns early when unset. The key that
produced the stack layout no longer undid it.
Tab now keeps the layouts it has used, by name, and writes their state plus
last_used_layout into the existing set_layout_state JSON rather than adding new
session commands. Older versions of kitty ignore unknown keys in that dict but
abort on unknown commands, so nesting keeps session files readable by them.
Restoring the state of a layout that is not being made current must not reorder
the window list, since only the current layout determines window order, hence
apply_to_window_list. Splits.set_layout_state also has to accept state for a
layout whose tree is still empty, which is the case for a layout that has not
laid out any windows yet.
Fixes#10320
Adds an `equalize` layout action that redistributes split sizes so each
window receives a proportional share of space along each axis.
Also adds an `equalize_on_close` layout option that automatically
equalizes splits whenever a window is closed, keeping the remaining
windows balanced without requiring an explicit key binding.
These two features compose well. For example, to keep splits balanced
at all times - equalizing on every open and close:
enabled_layouts splits:equalize_on_close=true
map ctrl+' combine : launch --location=hsplit --cwd=current : layout_action equalize
map ctrl+/ combine : launch --location=vsplit --cwd=current : layout_action equalize
A standalone key binding for manual rebalancing is also supported:
map ctrl+shift+e layout_action equalize
Follow PEP 0394 and use /usr/bin/env python so that the python in the
users venv is respected. Not that the kitty python files are meant to be
executed standalone anyway, but, whatever.
Fixes#6810