diff options
| -rw-r--r-- | src/pardes.zig | 43 | ||||
| -rw-r--r-- | test/snapshots/splitparent.golden | 99 | ||||
| -rw-r--r-- | test/snapshots/splitparent.snap | 27 |
3 files changed, 161 insertions, 8 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index c14154e5..0219d474 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1203,9 +1203,10 @@ pub const Pardes = struct { const free = p.freeSlot() orelse return; const nt = p.newShell(free, "") catch return; nt.greet = true; - const f = p.layoutFindTerm(p.active).?; + const src = p.splitParent(p.active); + const f = p.layoutFindTerm(src).?; p.layoutInsert(f.col, f.idx + 1, free); - p.splitBelow(p.active, nt); + p.splitBelow(src, nt); p.active = free; return; } @@ -3973,6 +3974,26 @@ pub const Pardes = struct { if (p.panes[sib]) |s| s.vweight += @as(f32, @floatFromInt(@max(1, p.rects[id].h))); } + /// The parent a new pane splits from must be tall enough that splitBelow + /// leaves the NEW pane at least 2 body rows (the parent keeps tag+1 row, + /// the new pane needs tag+2). A too-short choice is swapped for a + /// qualifying pane (same column first, keeping the split local), else the + /// tallest pane anywhere. + fn splitParent(p: *Pardes, want: usize) usize { + const need = 2 * BOX_H + 3; + if (p.rects[want].h >= need) return want; + if (p.layoutFindTerm(want)) |f| for (0..p.col_n[f.col]) |k| { + if (p.rects[p.col_terms[f.col][k]].h >= need) return p.col_terms[f.col][k]; + }; + var tallest = want; + for (0..p.ncol) |c| for (0..p.col_n[c]) |k| { + const pid = p.col_terms[c][k]; + if (p.rects[pid].h >= need) return pid; + if (p.rects[pid].h > p.rects[tallest].h) tallest = pid; + }; + return tallest; + } + /// Open a window BELOW `src` (acme-style) without rebalancing the column: /// shrink ONLY src to its content height (cursor row kept visible) and hand /// the freed rows to `nw` — together they fill src's old slot and the other @@ -4039,8 +4060,12 @@ pub const Pardes = struct { const nt = p.newShell(free, dir) catch return null; nt.greet = false; const rc = if (p.ncol > 0) p.ncol - 1 else 0; - if (p.col_n[rc] > 0) p.splitBelow(p.col_terms[rc][p.col_n[rc] - 1], nt); - p.layoutInsert(rc, p.col_n[rc], free); + if (p.col_n[rc] > 0) { + const src = p.splitParent(p.col_terms[rc][p.col_n[rc] - 1]); + const f = p.layoutFindTerm(src).?; + p.splitBelow(src, nt); + p.layoutInsert(f.col, f.idx + 1, free); + } else p.layoutInsert(rc, p.col_n[rc], free); return free; } @@ -4225,9 +4250,10 @@ pub const Pardes = struct { const free = p.freeSlot() orelse return; const nt = p.newShell(free, dir) catch return; nt.greet = true; - const f = p.layoutFindTerm(id).?; + const src = p.splitParent(id); + const f = p.layoutFindTerm(src).?; p.layoutInsert(f.col, f.idx + 1, free); - p.splitBelow(id, nt); + p.splitBelow(src, nt); p.active = free; }, .file => |target| { @@ -4359,9 +4385,10 @@ pub const Pardes = struct { }; }; if (src_id) |sid| { - const sf = p.layoutFindTerm(sid).?; + const src = p.splitParent(sid); + const sf = p.layoutFindTerm(src).?; p.layoutInsert(sf.col, sf.idx + 1, free); - p.splitBelow(sid, nt); + p.splitBelow(src, nt); p.active = free; } else { p.stackDocLeft(free, nt); diff --git a/test/snapshots/splitparent.golden b/test/snapshots/splitparent.golden new file mode 100644 index 00000000..b838cdad --- /dev/null +++ b/test/snapshots/splitparent.golden @@ -0,0 +1,99 @@ +== snap shrunk grid=100x30 cursor=4,3 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/splitparent/cwd Del NOR /tmp/pardes-snap/splitparent/cwd Del +| NOR /tmp/pardes-snap/splitparent/cwd Del +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +== snap swap-parent grid=100x30 cursor=4,7 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/splitparent/cwd Del NOR /tmp/pardes-snap/splitparent/cwd Del +| NOR /tmp/pardes-snap/splitparent/cwd Del +| +| +| NOR /tmp/pardes-snap/splitparent/cwd Del +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +== snap tallest grid=100x5 cursor=54,4 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/splitparent/cwd Del NOR /tmp/pardes-snap/splitparent/cwd Del +| NOR /tmp/pardes-snap/splitparent/cwd Del +| NOR /tmp/pardes-snap/splitparent/cwd Del NOR /tmp/pardes-snap/splitparent/cwd Del +| +== snap regrown grid=100x30 cursor=54,18 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/splitparent/cwd Del NOR /tmp/pardes-snap/splitparent/cwd Del +| NOR /tmp/pardes-snap/splitparent/cwd Del +| +| +| NOR /tmp/pardes-snap/splitparent/cwd Del +| +| +| +| +| +| +| +| +| +| +| NOR /tmp/pardes-snap/splitparent/cwd Del +| +| +| +| +| +| +| +| +| +| +| +| +| diff --git a/test/snapshots/splitparent.snap b/test/snapshots/splitparent.snap new file mode 100644 index 00000000..77a1fba1 --- /dev/null +++ b/test/snapshots/splitparent.snap @@ -0,0 +1,27 @@ +# a new pane never opens under a too-short parent: Alt-n from a tag-only pane +# splits the tall sibling in its column instead; when NO pane could leave the +# newcomer 2 body rows, the tallest one anywhere is split +start 30 100 -n 3 +stable 700 20000 +# shrink pane 0 (top-left) to just its tagline +press left 20 16 +drag left 20 2 +release left 20 2 +motion 40 25 +stable 500 8000 +snap shrunk +# Alt-n from the tag-only pane: the new shell opens below the tall sibling +# underneath it, not as another 1-row sliver +key a-n +stable 700 15000 +snap swap-parent +# squeeze the grid until no pane is 5 rows tall, then Alt-n again: the +# tallest pane (right column) is split even though the active one is left +resize 5 100 +stable 700 15000 +key a-n +stable 700 15000 +snap tallest +resize 30 100 +stable 700 15000 +snap regrown |
