aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_palette.py
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-20 23:22:33 +0200
committeruser <user@clank>2026-08-20 23:22:33 +0200
commitb33e8c13ff7981effc7ec2735e6aeaaaf1705af7 (patch)
tree4933eb476b5d09e9d8131986c84bb966bca317a2 /tests/test_palette.py
parentfuzz: libFuzzer harness for all six parser surfaces (diff)
downloadslosh-b33e8c13ff7981effc7ec2735e6aeaaaf1705af7.tar.gz
slosh-b33e8c13ff7981effc7ec2735e6aeaaaf1705af7.tar.xz
slosh-b33e8c13ff7981effc7ec2735e6aeaaaf1705af7.zip
slosh: floating panes and the drop grammar
A pane can float: lifted out of the layout, drawn over the tiled panes and under the modals, freely moved and shaped. The model is a flag and a rect on the leaf, exactly like `minimized` -- the seat in the tree is kept, layout skips it, the siblings absorb its share, and un-floating returns it home. The drawn rect is the wanted one clamped to the tab every frame, never written back, so a terminal squeeze round-trips exactly; an explicit move or resize *does* write back, because the hand is the author of the intent. Floating pops the pane to the centre: the jump is what says it happened. Painted after everything tiled, so the hit list -- searched backwards -- resolves an overlap to the float you can see: the z-order and the click order are the same list. It casts a shadow (float_shadow, one number), and `floating` joins the states table above `unfocused`, so a float is never dimmed as merely-unfocused. The hands: the title moves it, any other border resizes it (a bottom corner on both axes; the grabbed edge pins at walls and floors, never sliding the window), H J K L move a focused float, the =/+ key grows it about its centre, `-` shrinks it (its split was a refusal anyway), and hovering shows which edges a grab would take, from the same derivation the press uses. Equalize moved to `0` to free the key; `C-a f` floats (finder went to `s`, where it reads as search); `C-a F` opens a new floating shell -- the throwaway terminal; the function row f1..f12 became nameable in configs along the way. Guards throughout: the last tiled pane cannot float or minimize away, closing it lands the top float, splits are refused with their own words, floats are not swap targets. Dropping got a grammar: while a pane is carried, the pane under the pointer subdivides into a swap centre and four edge bands meaning "insert me beside this pane, on this side" -- detach_leaf + place_beside, the surgery cross-tab moves already had. Bands are thirds (the first map of quarters showed the swap owning half of each axis), the hovered band fills the half the drop would hand over, the release resolves against the painted frame's hits so it cannot disagree with the fill, and a drop between siblings along the axis is a reorder that always fits -- refusing it was refusing the most ordinary drag there is. Also: rename-pane/rename-tab as palette actions (renaming was a double-click and nothing else, the one verb the palette could not reach), and C-u clears the rename editor like it clears the picker's query. Design log in PLAN-FLOATING-PANES.md; to fold into DESIGN.md as D22/M10-11.
Diffstat (limited to 'tests/test_palette.py')
-rw-r--r--tests/test_palette.py44
1 files changed, 43 insertions, 1 deletions
diff --git a/tests/test_palette.py b/tests/test_palette.py
index 9741227..fbd2e47 100644
--- a/tests/test_palette.py
+++ b/tests/test_palette.py
@@ -151,6 +151,47 @@ def test_enter_runs_the_command():
check("and the palette closed behind it", not is_open(s))
+def test_rename_is_reachable_from_the_palette():
+ """Renaming was a double-click and nothing else, which made it the one
+ verb the palette could not reach -- exactly what the palette exists to
+ prevent. The editor it opens is the same in-place one the gesture uses,
+ seeded with the current label."""
+ with Session(SH, cols=80, rows=22) as s:
+ s.settle()
+ s.send(OPEN)
+ s.send("rename this tab")
+ s.settle(80)
+ s.send(r"\r")
+ s.settle(80)
+ s.send("deploys")
+ s.settle(40)
+ s.send(r"\r")
+ s.settle(40)
+ check(
+ "rename-tab renames the tab in place",
+ [t["name"] for t in s.tabs()] == ["deploys"],
+ str(s.tabs()),
+ )
+
+ s.send(OPEN)
+ s.send("rename this pane")
+ s.settle(80)
+ s.send(r"\r")
+ s.settle(80)
+ # The editor seeds with the current title (a rename is an edit);
+ # clear it first so the name is what was typed.
+ s.send(r"\x15") # C-u: clear the field
+ s.send("agent")
+ s.settle(40)
+ s.send(r"\r")
+ s.settle(40)
+ check(
+ "rename-pane renames the pane in place",
+ [p["title"] for p in s.panes()] == ["agent"],
+ str([p["title"] for p in s.panes()]),
+ )
+
+
def test_clicking_a_row_runs_it():
with Session(SH, cols=80, rows=22) as s:
s.settle()
@@ -234,7 +275,7 @@ def test_the_finder_is_still_its_own_thing():
"""They share a box; they must not share a list."""
with Session(SH, cols=80, rows=22) as s:
s.settle()
- s.send(r"\x01f")
+ s.send(r"\x01s")
s.settle(100)
check(
"the finder lists panes, not commands",
@@ -269,6 +310,7 @@ def test_every_label_fits_the_column():
if __name__ == "__main__":
test_every_label_fits_the_column()
+ test_rename_is_reachable_from_the_palette()
test_it_opens_and_lists_commands()
test_it_lists_actions_that_have_no_key()
test_typing_filters_it()