aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_minimize.py
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-14 20:18:23 +0200
committeruser <user@clank>2026-08-14 20:18:23 +0200
commit373931bf85fa92bc326f2ab68edf14a5bc15b260 (patch)
tree61399ade7c707800c86ef1c02ba72123fc2c3be6 /tests/test_minimize.py
parentsl0ppty: pick a minimise mark that fills its cell (diff)
downloadslosh-373931bf85fa92bc326f2ab68edf14a5bc15b260.tar.gz
slosh-373931bf85fa92bc326f2ab68edf14a5bc15b260.tar.xz
slosh-373931bf85fa92bc326f2ab68edf14a5bc15b260.zip
sl0ppty: minimised panes get one row between them, and ASCII marks
The strip was a row per minimised pane, which let putting things away cost more room than leaving them out. It is one row now, shaped like the tab strip and for the same reason: a list of names you click to go somewhere. Three panes put away leave the survivor exactly as tall as one did, and there is a test that says so, because that is the whole change. The panes on it are no longer laid out at all — the bar draws them, so they have no rect. The row the bar occupies is worked out by the layout and stored, rather than by the drawing working it out again from the same inputs; two copies of that sum is how a click lands somewhere the eye did not. The frame's marks are plain ASCII now. Spacing has been reported twice and the cells were even both times: what is uneven is the glyphs. A light character floating in a mostly empty cell hands that space to the gap beside it, and the fix of picking heavier glyphs only holds for fonts I can see. ASCII has neither that problem nor the double-width one, and "[]" and "][" say maximise and restore about as clearly as anything. That needed the button width measured rather than assumed. It was hardcoded to three cells, which was fine while every mark was one character and silently wrong the moment one was two: the mark would have been drawn over its neighbour, and the neighbour's hit would have covered a cell it did not draw. Also fixes a case that could not happen with a row each: when there is no room for the bar at all, the tab flattens rather than leaving minimised panes with no rect and nobody drawing them.
Diffstat (limited to 'tests/test_minimize.py')
-rw-r--r--tests/test_minimize.py169
1 files changed, 104 insertions, 65 deletions
diff --git a/tests/test_minimize.py b/tests/test_minimize.py
index 9582ca2..979354a 100644
--- a/tests/test_minimize.py
+++ b/tests/test_minimize.py
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
"""Putting a pane away.
-A minimised pane leaves the layout and sits in a strip along the bottom, one
-row each, still running. It is intent rather than a derived state, so the
-things worth checking are the same ones zoom needed: that it survives the
-layout being recomputed, that there is always a way back, and that it does not
-invent a state with nothing on screen.
+A minimised pane leaves the layout entirely and is listed on a single row along
+the bottom, whatever the number of them. It is intent rather than derived
+state, so what is worth checking is that it survives the layout being
+recomputed, that there is always a way back, and that it does not invent a
+state with nothing on screen.
"""
import os
import sys
@@ -29,50 +29,95 @@ def here(s):
return [p for p in s.panes() if p["tab"] == active]
-def strip(s):
- return [p for p in here(s) if p["h"] == 1]
+def open_panes(s):
+ return [p for p in here(s) if p["w"] > 1]
-def open_panes(s):
- return [p for p in here(s) if p["h"] > 1]
+def put_away(s):
+ """Minimised panes get no rect at all: the bar draws them, not the layout."""
+ return [p for p in here(s) if p["w"] <= 1]
+
+
+def bar_row(s):
+ """The bar's y, which is the row below every open pane."""
+ op = open_panes(s)
+ return max(p["y"] + p["h"] for p in op) if op else None
+
+
+def bar_entries(s):
+ y = bar_row(s)
+ if y is None:
+ return []
+ return [h for h in s.snapshot().hits
+ if h["y"] == y and h["action"].startswith("focus:")]
-def test_minimising_moves_a_pane_to_the_strip():
+def minimize_focused(s):
+ s.send(r"\x01m")
+ s.settle(20)
+
+
+def test_minimising_takes_a_pane_out_of_the_layout():
with Session(SH, cols=90, rows=20) as s:
s.settle(20)
s.api("split", dir="cols")
s.settle(20)
- before = sorted(p["w"] for p in open_panes(s))
+ before = max(p["w"] for p in open_panes(s))
- s.send(r"\x01m")
- s.settle(20)
- check("one pane is now a single row", len(strip(s)) == 1,
+ minimize_focused(s)
+ check("one pane is gone from the layout", len(put_away(s)) == 1,
str(here(s)))
- check("and the other still has the tab", len(open_panes(s)) == 1,
- str(here(s)))
- check("which is wider than it was, having taken the space back",
- open_panes(s)[0]["w"] > max(before), str(here(s)))
+ check("and the other took the space back",
+ open_panes(s)[0]["w"] > before, str(here(s)))
+ check("it is listed on the bar", len(bar_entries(s)) == 1,
+ str(bar_entries(s)))
+ check("which sits below the panes",
+ bar_entries(s)[0]["y"] == bar_row(s), str(bar_entries(s)))
- row = strip(s)[0]
- rest = open_panes(s)[0]
- check("the strip sits under the layout, not in it",
- row["y"] > rest["y"] + rest["h"] - 1, f"{row} vs {rest}")
- check("and spans the width", row["w"] == rest["w"], str(here(s)))
+def test_the_bar_is_one_row_however_many_are_put_away():
+ """A row each would let putting things away cost more room than having them
+ out, which is the opposite of the point."""
+ with Session(SH, cols=100, rows=22) as s:
+ s.settle(20)
+ for _ in range(3):
+ s.api("split", dir="cols")
+ s.settle(10)
+ check("four panes", len(open_panes(s)) == 4, str(here(s)))
-def test_clicking_the_row_brings_it_back():
- with Session(SH, cols=90, rows=20) as s:
+ minimize_focused(s)
+ one = open_panes(s)[0]["h"]
+ entries_one = len(bar_entries(s))
+ minimize_focused(s)
+ minimize_focused(s)
+ three = open_panes(s)[0]["h"]
+
+ check("three are put away", len(put_away(s)) == 3, str(here(s)))
+ check("all three are listed", len(bar_entries(s)) == 3,
+ str(bar_entries(s)))
+ check("and the survivor is no shorter than with one put away",
+ three == one, f"{one} -> {three}")
+ check("because the bar was one row either way", entries_one == 1,
+ str(entries_one))
+
+
+def test_clicking_an_entry_brings_that_pane_back():
+ with Session(SH, cols=100, rows=20) as s:
s.settle(20)
s.api("split", dir="cols")
s.settle(20)
layout_before = sorted((p["x"], p["w"]) for p in open_panes(s))
- s.send(r"\x01m")
- s.settle(20)
- row = strip(s)[0]
- s.click(row["x"] + 4, row["y"])
+ minimize_focused(s)
+ gone = put_away(s)[0]["id"]
+ e = [h for h in bar_entries(s) if h["action"] == f"focus:{gone}"]
+ check("the entry names the pane it stands for", len(e) == 1,
+ str(bar_entries(s)))
+ if not e:
+ return
+ s.click(e[0]["x"] + 1, e[0]["y"])
s.settle(20)
- check("clicking the row restores the pane", strip(s) == [], str(here(s)))
+ check("clicking it restores the pane", put_away(s) == [], str(here(s)))
check("and the layout is the one it left",
sorted((p["x"], p["w"]) for p in open_panes(s)) == layout_before,
str(here(s)))
@@ -85,35 +130,31 @@ def test_focusing_it_by_any_route_brings_it_back():
s.settle(20)
s.api("split", dir="cols")
s.settle(20)
- s.send(r"\x01m")
- s.settle(20)
- gone = strip(s)[0]["id"]
+ minimize_focused(s)
+ gone = put_away(s)[0]["id"]
s.api("focus", id=gone)
s.settle(20)
check("focusing it over the control API restores it",
- strip(s) == [], str(here(s)))
+ put_away(s) == [], str(here(s)))
def test_it_will_not_leave_the_tab_empty():
with Session(SH, cols=90, rows=20) as s:
s.settle(20)
check("one pane to start", len(here(s)) == 1, str(here(s)))
- s.send(r"\x01m")
- s.settle(20)
- check("minimising the only pane is refused", strip(s) == [],
+ minimize_focused(s)
+ check("minimising the only pane is refused", put_away(s) == [],
str(here(s)))
check("and it says why", "nothing else" in s.snapshot().screen(),
repr(s.snapshot().screen()[-200:]))
s.api("split", dir="cols")
s.settle(20)
- s.send(r"\x01m")
- s.settle(20)
- check("with two, one can go", len(strip(s)) == 1, str(here(s)))
- s.send(r"\x01m")
- s.settle(20)
- check("but the last one standing cannot", len(strip(s)) == 1,
+ minimize_focused(s)
+ check("with two, one can go", len(put_away(s)) == 1, str(here(s)))
+ minimize_focused(s)
+ check("but the last one standing cannot", len(put_away(s)) == 1,
str(here(s)))
@@ -131,59 +172,57 @@ def test_the_button_minimises_that_pane():
s.click(b[0]["x"] + 1, b[0]["y"])
s.settle(20)
check("clicking it puts that pane away",
- [p["id"] for p in strip(s)] == [target], str(here(s)))
+ [p["id"] for p in put_away(s)] == [target], str(here(s)))
def test_it_has_no_say_once_the_tab_is_a_list():
"""Down there every pane is a row already, so being minimised is not a
- different thing to be -- which is the whole reason it costs nothing."""
+ different thing to be -- which is why it costs nothing."""
with Session(SH, cols=90, rows=20) as s:
s.settle(20)
s.api("split", dir="cols")
s.settle(20)
- s.send(r"\x01m")
- s.settle(20)
- check("one minimised, one open", len(strip(s)) == 1, str(here(s)))
+ minimize_focused(s)
+ check("one put away, one open", len(put_away(s)) == 1, str(here(s)))
- s.resize(26, 20) # narrow enough that the tab flattens
+ s.resize(26, 20)
s.settle(20)
rows = here(s)
check("the tab is a list", any(p["hidden"] for p in rows), str(rows))
- check("and the minimised pane is just another row in it",
- len([p for p in rows if p["h"] == 1]) == len(rows) - 1,
- str(rows))
+ check("and every pane is in it, minimised or not",
+ all(p["w"] > 1 for p in rows), str(rows))
s.resize(90, 20)
s.settle(20)
- check("coming back, it is minimised again and not lost",
- len(strip(s)) == 1 and len(open_panes(s)) == 1, str(here(s)))
+ check("coming back, it is put away again and not lost",
+ len(put_away(s)) == 1 and len(open_panes(s)) == 1, str(here(s)))
-def test_zoom_hides_the_strip():
+def test_zoom_hides_the_bar():
with Session(SH, cols=90, rows=20) as s:
s.settle(20)
s.api("split", dir="cols")
s.api("split", dir="rows")
s.settle(20)
- s.send(r"\x01m")
- s.settle(20)
- check("a strip to start", len(strip(s)) == 1, str(here(s)))
+ minimize_focused(s)
+ check("a bar to start", len(bar_entries(s)) == 1, str(bar_entries(s)))
s.send(r"\x01z")
s.settle(20)
- check("zooming shows one pane and nothing else, strip included",
- len([p for p in here(s) if p["w"] > 1]) == 1, str(here(s)))
+ check("zooming shows one pane and nothing else, bar included",
+ bar_entries(s) == [], str(bar_entries(s)))
s.send(r"\x01z")
s.settle(20)
- check("and unzooming gives the strip back", len(strip(s)) == 1,
- str(here(s)))
+ check("and unzooming gives the bar back", len(bar_entries(s)) == 1,
+ str(bar_entries(s)))
if __name__ == "__main__":
- test_minimising_moves_a_pane_to_the_strip()
- test_clicking_the_row_brings_it_back()
+ test_minimising_takes_a_pane_out_of_the_layout()
+ test_the_bar_is_one_row_however_many_are_put_away()
+ test_clicking_an_entry_brings_that_pane_back()
test_focusing_it_by_any_route_brings_it_back()
test_it_will_not_leave_the_tab_empty()
test_the_button_minimises_that_pane()
test_it_has_no_say_once_the_tab_is_a_list()
- test_zoom_hides_the_strip()
+ test_zoom_hides_the_bar()
sys.exit(report())