diff options
Diffstat (limited to 'src/layout.zig')
| -rw-r--r-- | src/layout.zig | 63 |
1 files changed, 54 insertions, 9 deletions
diff --git a/src/layout.zig b/src/layout.zig index b9b24e79..4c974fb7 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -408,6 +408,46 @@ pub fn acmePlace(p: *Pardes, c: usize, origin: ?usize, id: usize) void { splitAt(p, c, id, y); } +/// The body rows a placement leaves every pane it makes or shrinks, under +/// its tag (acme's minht keeps one). +pub const min_body_rows = 2; + +fn minRows(p: *Pardes, id: usize) u16 { + return p.wantedTagRows(id) + min_body_rows; +} + +/// After placing `id`: whether it, and every pane of its column that the +/// placement shrank, still has its tag and `min_body_rows` (from `before`, +/// the rects as they were). A pane alone in its column always has room: the +/// window can be no taller. +pub fn placedRoomy(p: *Pardes, id: usize, before: *const [MAX_PANES]Rect) bool { + compute(p); + const f = findPane(p, id) orelse return true; + if (p.col_n[f.col] == 1) return true; + for (p.col_panes[f.col][0..p.col_n[f.col]]) |k| { + const pane = p.panes[k] orelse continue; + if (pane.collapsed) continue; + if (k != id and p.rects[k].h >= before[k].h) continue; + if (p.rects[k].h < minRows(p, k)) return false; + } + return true; +} + +/// Places `id` in column `c` by halving its tallest unfolded pane, when +/// that leaves both their rows; false, and `id` in no column, when not. +pub fn splitTallest(p: *Pardes, c: usize, id: usize) bool { + compute(p); + var tall: ?usize = null; + for (p.col_panes[c][0..p.col_n[c]]) |k| { + if (k == id or p.panes[k].?.collapsed) continue; + if (tall == null or p.rects[k].h > p.rects[tall.?].h) tall = k; + } + const v = tall orelse return false; + if (p.rects[v].h < minRows(p, v) + minRows(p, id)) return false; + splitAt(p, c, id, p.rects[v].y + p.rects[v].h / 2); + return true; +} + /// acme's coladd at row `y` (cols.c:66-126): the new pane goes under the /// pane that `y` falls in, which keeps its rows above `y`. fn splitAt(p: *Pardes, c: usize, id: usize, y: u16) void { @@ -419,9 +459,9 @@ fn splitAt(p: *Pardes, c: usize, id: usize, y: u16) void { snapColWeights(p, c); insert(p, c, k + 1, id); const pane = p.panes[id].?; - // Each keeps a tag and a body row, as acme's minht does. - const lo = r.y +| BOX_H + 1; - const hi = (r.y + r.h) -| (BOX_H + 1); + // Each keeps a tag and min_body_rows, where acme's minht keeps one. + const lo = r.y +| minRows(p, v); + const hi = (r.y + r.h) -| minRows(p, id); const row = splitRowForExtent(r.y, r.h, if (lo <= hi) std.math.clamp(y, lo, hi) else r.y + r.h / 2) orelse { pane.vweight = BOX_H + 1; return; @@ -550,7 +590,7 @@ pub fn absorbVWeight(p: *Pardes, id: usize, to: ?usize) ?usize { } pub fn splitParent(p: *Pardes, want: usize) usize { - const need = 2 * BOX_H + 3; + const need = 2 * (BOX_H + min_body_rows); if (p.rects[want].h >= need) return want; if (findPane(p, want)) |f| for (0..p.col_n[f.col]) |k| { if (p.rects[p.col_panes[f.col][k]].h >= need) return p.col_panes[f.col][k]; @@ -567,23 +607,28 @@ pub fn splitParent(p: *Pardes, want: usize) usize { pub fn splitBelow(p: *Pardes, src_id: usize, nw: *Pane) void { const src = p.panes[src_id] orelse return; const src_h = p.rects[src_id].h; - const body: u16 = if (src_h > BOX_H) src_h - BOX_H else 1; + // Its tag may wrap to more than one row: the rows under it are its body. + const tag_h = @max(BOX_H, src.tag_rows); + const body: u16 = if (src_h > tag_h) src_h - tag_h else 1; const cur: u16 = if (!src.isTerminal()) body / 2 else panes.Terminal.gridCursor(src).y + 1; // cap keep so a content-full source still leaves the new pane a tag + // a few body rows (an Alt-n from a full shell was born 0 rows tall) - const keep = std.math.clamp(cur, 1, @max(1, body -| (BOX_H + 3))); + // ...and the source min_body_rows of its own (exec.placeNew), when it + // has them: a boot layout splits before any row is laid out. + const least = @min(min_body_rows, body); + const keep = std.math.clamp(cur, least, @max(least, body -| (BOX_H + 3))); if (findPane(p, src_id)) |f| for (0..p.col_n[f.col]) |k| { const pid = p.col_panes[f.col][k]; if (p.panes[pid]) |pp| if (pp != nw and !pp.collapsed) { pp.vweight = @floatFromInt(@max(1, p.rects[pid].h)); }; }; - if (!src.collapsed) src.vweight = @floatFromInt(BOX_H + keep); - nw.vweight = @floatFromInt(@max(1, src_h -| (BOX_H + keep))); + if (!src.collapsed) src.vweight = @floatFromInt(tag_h + keep); + nw.vweight = @floatFromInt(@max(1, src_h -| (tag_h + keep))); if (nw.file) |f| if (!panes.Output.fileTraits(f.output).doc) { // trimmed: every row ends in a newline, and the empty line after // the last one is not a result - const want: f32 = @floatFromInt(BOX_H + panes.File.lineCount(std.mem.trimEnd(u8, f.content, "\n"))); + const want: f32 = @floatFromInt(BOX_H + @max(min_body_rows, panes.File.lineCount(std.mem.trimEnd(u8, f.content, "\n")))); if (want < nw.vweight) { src.vweight += nw.vweight - want; nw.vweight = want; |
