diff options
| author | Ingmar Steen <iksteen@gmail.com> | 2026-09-06 19:59:13 +0200 |
|---|---|---|
| committer | Ingmar Steen <iksteen@gmail.com> | 2026-09-06 19:59:13 +0200 |
| commit | af82aff3cac5370cc08b2e0d7358097bfb2d9e45 (patch) | |
| tree | f13ad9272e021d039a462a96ce49f9c6b1929d68 /tests | |
| parent | graphics: state z=0 on a placement, so a mirror paints it over the text (diff) | |
| download | slosh-af82aff3cac5370cc08b2e0d7358097bfb2d9e45.tar.gz slosh-af82aff3cac5370cc08b2e0d7358097bfb2d9e45.tar.xz slosh-af82aff3cac5370cc08b2e0d7358097bfb2d9e45.zip | |
graphics: place what a float leaves of an image, not nothing
Occlusion for placements is clipping: the cell compositor gets it free
from paint order, but images are sent after the diff and drawn over it,
so what a float covers has to be taken out of the placement itself. A
clean edge came out as a smaller rectangle; anything else -- a float
across the middle, a float on a corner, a float wholly inside -- was
refused, on the grounds that the remainder is an L and a placement is a
rectangle. The image vanished, all of it, the moment a floating pane
crossed it, on the terminal and in the mirror alike, and came back when
the float moved off.
The remainder is not one rectangle, but it is always a few: the strips
above and below what is covered, and what is left beside it between
them. So rect_subtract() cuts the visible remainder into rectangles and
each is placed in its own right, with the source rectangle the same
arithmetic the pane edge already used -- generalised to a cut on any
side, and to a first piece that keeps the sub-cell offset while the
others start on a cell boundary. Eight pieces is the ceiling (two
floats' worth); past it the old refusal stands.
The pieces of one placement need an id each, and the program's own
placement id is one number that cannot be several, so the model now
hands out the placement ids the client is told and keeps the program's
as the identity it was. `graphics` reports the piece alongside it.
Checked against a float parked over an image: the picture is cut around
the float, the strips tile exactly the image minus what the float
covers, and their source rectangles land where their cells do (a float
inside a 40x10-cell natural-size image gives 320x32 above, 320x32
below, 56x96 left and 72x96 right).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRz3zwZJvBvzsR9k4cj6dd
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_graphics.py | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/tests/test_graphics.py b/tests/test_graphics.py index 50edf43..aabe87a 100644 --- a/tests/test_graphics.py +++ b/tests/test_graphics.py @@ -612,10 +612,12 @@ def test_scrolled_away_placements_are_dropped(): def test_a_float_occludes_a_placement(): """The cell compositor gets occlusion free from paint order; placements - are sent after the diff and get it from clipping (D22): a clean edge - crops -- the pane-edge arithmetic aimed at another clipper -- and a - float in the middle is a shape one placement cannot express, so that - placement is suppressed for the frame and returns when the float moves. + are sent after the diff and get it from clipping (D22): what is left of + the image once the float is taken out of it. A clean edge leaves one + rectangle, and anything else leaves several -- each placed in its own + right, because a placement is one rectangle and the remainder need not + be. Suppressing the image instead, which is what this used to do, meant + a floating pane crossing a picture erased all of it. Every pane here runs the same command, so the float shows the image too; the tiled pane's placement is told apart by the image id it had before @@ -642,10 +644,22 @@ def test_a_float_occludes_a_placement(): str(got), ) - # Across the middle: no single rect can say what remains. + # Across the middle: what is left is the columns on either side of it. s.api("float", x=img["x"] + 10, y=0, w=30, h=28) s.settle(20) - check("a middle strip suppresses it", mine() == [], str(places(s))) + got = sorted(mine(), key=lambda p: p["x"]) + check( + "a middle strip leaves the columns beside it", + len(got) == 2 + and (got[0]["x"], got[0]["cols"]) == (img["x"], 10) + and (got[1]["x"], got[1]["cols"]) == (img["x"] + 40, img["cols"] - 40), + str(got), + ) + check( + "and each piece crops to the cells it covers", + all(p["cols"] and p["rows"] == img["rows"] for p in got), + str(got), + ) # Moved clear: the placement returns whole. s.api("float", x=img["x"], y=14, w=30, h=12) @@ -657,6 +671,25 @@ def test_a_float_occludes_a_placement(): str(got), ) + # Landing inside it: what is left is the strips around the float, and + # they tile exactly the image minus what the float covers -- the + # float's rect being its frame's, not the content's. + s.api("float", x=img["x"] + 10, y=img["y"] + 1, w=20, h=3) + s.settle(20) + got = mine() + fl = [p for p in s.api("panes")["panes"] if p["floating"]][0] + ox0, oy0 = max(img["x"], fl["x"]), max(img["y"], fl["y"]) + ox1 = min(img["x"] + img["cols"], fl["x"] + fl["w"]) + oy1 = min(img["y"] + img["rows"], fl["y"] + fl["h"]) + hidden = max(0, ox1 - ox0) * max(0, oy1 - oy0) + covered = sum(p["cols"] * p["rows"] for p in got) + check( + "a float over the middle of an image leaves the strips around it", + len(got) > 1 and covered == img["cols"] * img["rows"] - hidden, + f"{len(got)} pieces covering {covered} of " + f"{img['cols'] * img['rows'] - hidden}: {got}", + ) + # Over the top rows: cropped from the top, the source origin moving. s.api("float", x=0, y=0, w=100, h=img["y"] + 3) s.settle(20) |
