summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/layout.zig69
-rw-r--r--src/mouse.zig9
-rw-r--r--src/pardes.zig6
3 files changed, 69 insertions, 15 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);
diff --git a/src/mouse.zig b/src/mouse.zig
index cb5512ad..9eefa9a1 100644
--- a/src/mouse.zig
+++ b/src/mouse.zig
@@ -109,20 +109,23 @@ test "a pane's last row of text is no resize handle; the rule over the next pane
try std.testing.expect(nudgeRightSeam(p, 2) == seam + 2);
}
-test "a pane's grip dragged up or down its own column moves its top, down to a tag alone above" {
+test "a pane's grip dragged up or down its own column moves its top, down to the floor of the pane above" {
if (platform == .web) return;
const p = try cornerFixture(std.testing.allocator);
defer p.deinit();
const upper = p.col_panes[1][0];
const lower = p.col_panes[1][1];
const grip_x = p.rects[lower].x;
- // Up, past the pane above's first row: it keeps one row, its tag.
+ // Up, past the pane above's first row: a text pane keeps its tag, a
+ // terminal its tag and two rows (layout.floorRows).
const top = p.rects[upper].y;
+ const above = p.panes[upper].?;
+ const floor: u16 = if (above.isTerminal()) above.tag_rows + layout.min_body_rows else 1;
p.update(.{ .mouse = .{ .button = .left, .kind = .press, .col = grip_x, .row = p.rects[lower].y } });
p.update(.{ .mouse = .{ .button = .left, .kind = .drag, .col = grip_x, .row = top } });
p.update(.{ .mouse = .{ .button = .left, .kind = .release, .col = grip_x, .row = top } });
p.sync();
- try std.testing.expectEqual(@as(u16, 1), p.rects[upper].h);
+ try std.testing.expectEqual(floor, p.rects[upper].h);
try std.testing.expectEqual(lower, p.col_panes[1][1]);
// Down, inside itself: its top follows, the pane above taking the rows.
const to = p.rects[lower].y + 6;
diff --git a/src/pardes.zig b/src/pardes.zig
index 8e9d3c7e..98b8819c 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -6598,9 +6598,9 @@ pub const Pardes = struct {
pane.tag_top = @intCast(@min(top, lines - pane.tag_rows));
} else pane.tag_top = 0;
const cols = @max(1, r.w -| config.GUTTER);
- // The tag steals the top rows. The pty is what the pane shows,
- // even one row dragged short: a taller pty would put its cursor
- // below the pane (only winsize keeps two, ctlfs.pty).
+ // The tag steals the top rows. The pty is what the pane shows
+ // (layout keeps a terminal two body rows where the window has
+ // them): a taller pty would put its cursor below the pane.
const rows = @max(1, r.h -| pane.tag_rows);
// A terminal's pty follows its pane down to a row, whatever the
// resize; only a folded one keeps the size it had, and one not