diff options
Diffstat (limited to 'src/layout.zig')
| -rw-r--r-- | src/layout.zig | 69 |
1 files changed, 60 insertions, 9 deletions
diff --git a/src/layout.zig b/src/layout.zig index 644054ed..cbf0fd96 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -274,7 +274,11 @@ pub fn applyRowSplit(p: *Pardes, cc: usize, k: usize, cur_y: u16) void { const rb = p.rects[p.col_panes[cc][k + 1]]; const combined: f32 = @floatFromInt(ra.h + rb.h); var nt: f32 = @floatFromInt(if (p.settings.tag_bottom) cur_y -| ra.y else (cur_y + 1) -| ra.y); - nt = std.math.clamp(nt, @as(f32, BOX_H), @max(@as(f32, BOX_H), combined - BOX_H)); + // A terminal keeps its tag and two body rows (floorRows); a text pane + // gives up all but its tag, as acme's do. + const lo: f32 = @floatFromInt(@max(BOX_H, floorRows(p, p.col_panes[cc][k]))); + const hi = combined - @as(f32, @floatFromInt(@max(BOX_H, floorRows(p, p.col_panes[cc][k + 1])))); + nt = std.math.clamp(nt, @min(lo, @max(@as(f32, BOX_H), hi)), @max(@as(f32, BOX_H), hi)); if (combined <= 0) return; // Dragging a folded tag open is an explicit resize, so switch this pair // back to visible-height weights without consuming other folded weights. @@ -794,16 +798,62 @@ pub fn compute(p: *Pardes) void { if (vsum <= 0) vsum = 1; var y: u16 = p.topBarHeight() + p.columnBarHeight(); + const top = y; const avail_h = p.screen_h -| y; - for (0..p.col_n[c]) |k| { + var hs: [MAX_PANES]u16 = undefined; + const n = p.col_n[c]; + for (0..n) |k| { const id = p.col_panes[c][k]; - const pane = p.panes[id] orelse continue; - const lastk = k + 1 == p.col_n[c]; + const pane = p.panes[id] orelse { + hs[k] = 0; + continue; + }; + const lastk = k + 1 == n; const fh = @as(f32, @floatFromInt(avail_h)) * pane.vweight / vsum; const room = p.screen_h -| y; - const ch: u16 = if (lastk) room else @min(room, @max(1, @as(u16, @intFromFloat(@round(fh))))); - p.rects[id] = .{ .x = x, .y = y, .w = cw, .h = ch }; - y +|= ch; + hs[k] = if (lastk) room else @min(room, @max(1, @as(u16, @intFromFloat(@round(fh))))); + y +|= hs[k]; + } + keepTerminalFloors(p, c, hs[0..n]); + y = top; + for (0..n) |k| { + const id = p.col_panes[c][k]; + if (p.panes[id] == null) continue; + p.rects[id] = .{ .x = x, .y = y, .w = cw, .h = hs[k] }; + y +|= hs[k]; + } + } +} + +/// The fewest rows a pane is laid out with: a terminal keeps its tag and +/// `min_body_rows` (a shorter one loses its prompt and reads busy for +/// ever), a text pane may shrink to its tag, as acme's windows do. +fn floorRows(p: *Pardes, id: usize) u16 { + const pane = p.panes[id] orelse return 0; + return if (pane.isTerminal()) @max(1, pane.tag_rows) + min_body_rows else 1; +} + +/// Raises each terminal in column `c` below its floor to it, a row at a +/// time from whichever pane has the most rows above its own floor. Where +/// the column has not the rows, the terminal keeps what it could get. +// ponytail: a row per step, O(panes x rows); a column holds few of both. +fn keepTerminalFloors(p: *Pardes, c: usize, hs: []u16) void { + for (hs, 0..) |*h, k| { + const floor = floorRows(p, p.col_panes[c][k]); + while (h.* < floor) { + var giver: ?usize = null; + var spare: u16 = 0; + for (hs, 0..) |other, j| { + if (j == k) continue; + const extra = other -| floorRows(p, p.col_panes[c][j]); + if (extra > spare) { + spare = extra; + giver = j; + } + } + const g = giver orelse break; + hs[g] -= 1; + h.* += 1; } } } @@ -911,9 +961,10 @@ test "collapse local transfers remain bounded through repeated tiny window resiz test "collapse expansion is limited to space available in one neighbor" { const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 42 }); defer p.deinit(); + // Text panes: a terminal would keep its tag and two rows (floorRows). for (1..4) |id| { - _ = try p.newShell(id, ""); - insert(p, 0, id, id); + p.newScratchBelow(0); + try std.testing.expectEqual(id, p.col_panes[0][id]); } compute(p); toggleCollapse(p, 2); |
