From 5f736a42876d8934d1c7d6e79f375103ce346559 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 24 Sep 2026 08:51:26 +0530 Subject: [PATCH] Fix #10522 --- docs/changelog.rst | 4 ++++ kitty/layout/splits.py | 5 +++++ kitty_tests/layout.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index aa8a10fa9..48dabe40c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -209,6 +209,10 @@ Detailed list of changes - Splits layout: Fix windows in a nested split overlapping their neighbour by a couple of pixels when the split is resized down to its minimum size. +- Splits layout: Fix the next split collapsing to a single line after + equalizing a tab with only one window, or closing a window with + :code:`equalize_on_window_close` enabled (:iss:`10522`) + - icat: Fix :code:`--transfer-mode=memory` not displaying anything and :code:`--detect-support` reporting ``files``, because the names of the POSIX shared memory objects it created were missing the leading slash mandated by diff --git a/kitty/layout/splits.py b/kitty/layout/splits.py index e43e03024..0213fe42e 100644 --- a/kitty/layout/splits.py +++ b/kitty/layout/splits.py @@ -846,6 +846,11 @@ class Splits(Layout): def equalize_biases(self) -> bool: for pair in self.pairs_root.self_and_descendants(): + if pair.is_redundant: + # A redundant pair has no geometry, but its bias is retained for + # when it is split again, so make that split an even one. + pair.bias = 0.5 + continue left = child_axis_units(pair.one, pair.horizontal) right = child_axis_units(pair.two, pair.horizontal) total = left + right diff --git a/kitty_tests/layout.py b/kitty_tests/layout.py index 81ad10101..6f32e1f52 100644 --- a/kitty_tests/layout.py +++ b/kitty_tests/layout.py @@ -651,6 +651,45 @@ class TestLayout(BaseTest): self.assertFalse(result) self.assertAlmostEqual(q2.pairs_root.bias, 0.9, places=5) + def test_splits_equalize_redundant_pair(self): + # Equalizing must leave a pair holding a single window with an even bias, + # as that bias is used when the pair is split again, see #10522 + def split_after(equalize_on_close: bool, bias: float) -> float: + q = create_layout(Splits) + q.layout_opts = SplitsLayoutOpts({}) + q.layout_opts.equalize_on_close = equalize_on_close + all_windows = create_windows(q, num=0) + q.add_window(all_windows, Window(1)) + w2 = Window(2) + q.add_window(all_windows, w2, location='vsplit') + q.pairs_root.bias = bias + q.remove_windows(all_windows.group_for_window(w2).id) + all_windows.remove_window(w2) + if equalize_on_close: + self.assertTrue(q.on_window_removed(all_windows)) + else: + self.assertTrue(q.layout_action('equalize', (), all_windows)) + q.add_window(all_windows, Window(3), location='hsplit') + return q.pairs_root.bias + + for equalize_on_close in (False, True): + with self.subTest(equalize_on_close=equalize_on_close): + self.assertAlmostEqual(split_after(equalize_on_close, 0.5), 0.5, places=5) + self.assertAlmostEqual(split_after(equalize_on_close, 0.8), 0.5, places=5) + + # Without an equalize, the bias of a closed split is preserved by design + q = create_layout(Splits) + all_windows = create_windows(q, num=0) + q.add_window(all_windows, Window(1)) + w2 = Window(2) + q.add_window(all_windows, w2, location='vsplit') + q.pairs_root.bias = 0.8 + q.remove_windows(all_windows.group_for_window(w2).id) + all_windows.remove_window(w2) + self.assertFalse(q.on_window_removed(all_windows)) + q.add_window(all_windows, Window(3), location='vsplit') + self.assertAlmostEqual(q.pairs_root.bias, 0.8, places=5) + def test_layout_dimension_no_negative_cells(self): # Regression test for issue #9946: when window padding exceeds the # available space (e.g. after maximize sets a window to minimum width),