diff options
| author | user <user@clank> | 2026-08-14 20:52:17 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 20:52:17 +0200 |
| commit | a8cd32986134c2ba516a11c64dc474dd79855b0d (patch) | |
| tree | 246817144348f42badcec4158592dac65b12396b | |
| parent | sl0ppty: hold the boundaries, not a seat number (diff) | |
| download | slosh-a8cd32986134c2ba516a11c64dc474dd79855b0d.tar.gz slosh-a8cd32986134c2ba516a11c64dc474dd79855b0d.tar.xz slosh-a8cd32986134c2ba516a11c64dc474dd79855b0d.zip | |
sl0ppty: every place the boundaries meet is a crossing
Resizing one row of a two-by-two puts its column boundary out of line with the
one below, which should leave two crossings on the row boundary, one for each.
Only one was found.
find_corners took the first column boundary it saw and then accepted others
only if they occupied exactly the same columns, which is the aligned case and
nothing else: anything out of line was discarded rather than becoming a
crossing of its own. It groups by the columns a boundary occupies now, so
aligned pairs are one crossing that moves both — still the case worth having,
since moving one of a matched pair would put a step in what reads as one line —
and everything else is its own crossing moving its own boundary.
The test starts aligned, moves one row's boundary, and checks the second
crossing appears and that dragging it leaves the other row alone.
| -rw-r--r-- | src/app.c | 43 | ||||
| -rw-r--r-- | tests/test_corner.py | 37 |
2 files changed, 67 insertions, 13 deletions
@@ -2510,14 +2510,16 @@ static void find_corners(app_t *a) { struct gapinfo g[64]; size_t n = collect_gaps(cur(a)->root, g, 64, 0); - for (size_t i = 0; i < n && a->ncorners < 16; i++) { + /* Every place a column boundary runs into a row boundary is its own + * crossing. Two of them that happen to line up are one crossing with two + * boundaries to move; two that do not are two crossings, each moving its + * own. Grouping by the columns they occupy gets both, where taking the first + * and discarding anything unlike it only ever found the aligned case. */ + for (size_t i = 0; i < n; i++) { if (g[i].sp->dir != SPLIT_ROWS) continue; rect_t h = g[i].r; - corner_t c = {0}; - c.h_id = g[i].sp->id; - c.h_edge = g[i].i; - for (size_t j = 0; j < n && c.nv < 2; j++) { + for (size_t j = 0; j < n; j++) { if (g[j].sp->dir != SPLIT_COLS) continue; rect_t v = g[j].r; if ((uint16_t)(v.y + v.h) != h.y && (uint16_t)(h.y + h.h) != v.y) continue; @@ -2526,16 +2528,31 @@ static void find_corners(app_t *a) { ? (uint16_t)(v.x + v.w) : (uint16_t)(h.x + h.w); if (xe <= x0) continue; - if (!c.nv) { - c.r = (rect_t){x0, h.y, (uint16_t)(xe - x0), h.h}; - } else if (c.r.x != x0 || c.r.w != (uint16_t)(xe - x0)) { - continue; /* out of line with the first: not one corner */ + uint16_t w = (uint16_t)(xe - x0); + + corner_t *c = NULL; + for (size_t k = 0; k < a->ncorners; k++) { + corner_t *e = &a->corners[k]; + if (e->h_id == g[i].sp->id && e->h_edge == g[i].i && e->r.x == x0 && + e->r.w == w) { + c = e; + break; + } + } + if (!c) { + if (a->ncorners >= 16) break; + c = &a->corners[a->ncorners++]; + *c = (corner_t){0}; + c->r = (rect_t){x0, h.y, w, h.h}; + c->h_id = g[i].sp->id; + c->h_edge = g[i].i; + } + if (c->nv < 2) { + c->v_id[c->nv] = g[j].sp->id; + c->v_edge[c->nv] = g[j].i; + c->nv++; } - c.v_id[c.nv] = g[j].sp->id; - c.v_edge[c.nv] = g[j].i; - c.nv++; } - if (c.nv) a->corners[a->ncorners++] = c; } } diff --git a/tests/test_corner.py b/tests/test_corner.py index 83645bc..e89e0d0 100644 --- a/tests/test_corner.py +++ b/tests/test_corner.py @@ -187,6 +187,42 @@ def test_the_crossing_shows_itself_before_you_rest_on_it(): "\u250a" not in s.snapshot().screen(), "") +def test_boundaries_out_of_line_are_two_crossings(): + """Aligned, they are one crossing with two boundaries to move. Out of line, + they are two crossings each moving its own -- taking the first and + discarding anything unlike it only ever found the aligned case.""" + l = lay(GRID) + with Session(SH, cols=70, rows=22, layout=l) as l_s: + s = l_s + s.settle(30) + check("aligned, there is one", len(corners(s)) == 1, str(corners(s))) + + ids = sorted(rects(s)) + s.api("focus", id=ids[0]) + s.settle(10) + for _ in range(4): + s.send(r"\x01L") + s.settle(30) + + c = sorted(corners(s), key=lambda h: h["x"]) + check("moving one of them makes a second crossing", len(c) == 2, + str(c)) + if len(c) != 2: + return + check("at the two different columns", + c[0]["x"] != c[1]["x"], str(c)) + + w0 = [p["w"] for p in s.panes()] + s.send(rf"\e[<0;{c[0]['x'] + 1};{c[0]['y'] + 1}M") + s.send(rf"\e[<32;{c[0]['x'] + 5};{c[0]['y'] + 1}M") + s.send(rf"\e[<0;{c[0]['x'] + 5};{c[0]['y'] + 1}m") + s.settle(30) + w1 = [p["w"] for p in s.panes()] + check("and each moves only the boundary that meets it", + w1[:2] == w0[:2] and w1[2:] != w0[2:], f"{w0} -> {w1}") + os.unlink(l) + + if __name__ == "__main__": test_a_grid_has_a_corner_where_the_boundaries_cross() test_a_layout_with_no_crossing_has_no_corner() @@ -195,4 +231,5 @@ if __name__ == "__main__": test_the_hint_says_both_ways() test_a_drag_keeps_the_boundaries_it_grabbed() test_the_crossing_shows_itself_before_you_rest_on_it() + test_boundaries_out_of_line_are_two_crossings() sys.exit(report()) |
