summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/exec.zig13
-rw-r--r--src/layout.zig46
-rw-r--r--src/ninep/ctl.zig18
-rw-r--r--src/pardes.zig6
4 files changed, 82 insertions, 1 deletions
diff --git a/src/exec.zig b/src/exec.zig
index 79717963..60c4172d 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -977,9 +977,12 @@ pub fn placeNew(p: *Pardes, from: usize, origin: ?usize, id: usize, kind: NewPan
layout.snapColWeights(p, f.col);
if (!layout.splitTallest(p, f.col, id) or !layout.placedRoomy(p, id, &before)) {
if (layout.findPane(p, id) == null) layout.insert(p, f.col, p.col_n[f.col], id);
+ // No pane to halve, but the column holds every pane's minimum
+ // with the new one's: the rows are shared out again, so what is
+ // refused is only what the documented arithmetic cannot hold.
// +Errors goes wherever there is room, last column first; with
// none, its maker says the text some other way (appendErrors).
- if (kind == .errors) {
+ if (layout.shareColumn(p, f.col)) {} else if (kind == .errors) {
layout.removePane(p, id);
var c = p.ncol;
while (c > 0) {
@@ -999,12 +1002,20 @@ pub fn placeNew(p: *Pardes, from: usize, origin: ?usize, id: usize, kind: NewPan
return;
}
} else {
+ // Refused, it closes at the step's end holding no weight, so
+ // its column's rows come back as they were, none rounded off.
+ p.panes[id].?.vweight = 0;
p.unplaced.set(id);
p.reportError(from, "placement", error.NoPaneRoom);
return;
}
}
};
+ // Placed: no pane of its column left under its minimum by the rows
+ // rounding or an earlier placement gave it; shared out again if so.
+ if (layout.findPane(p, id)) |f| if (!layout.columnAtMinimums(p, f.col)) {
+ _ = layout.shareColumn(p, f.col);
+ };
if (kind == .command) return;
tagline.exitHeader(p);
p.active = id;
diff --git a/src/layout.zig b/src/layout.zig
index 09cf23b3..16af1ec5 100644
--- a/src/layout.zig
+++ b/src/layout.zig
@@ -441,6 +441,52 @@ pub fn minimaFit(p: *Pardes) bool {
return true;
}
+/// Whether every unfolded pane of column `c` has its `minRows` (a pane
+/// alone in its column always does).
+pub fn columnAtMinimums(p: *Pardes, c: usize) bool {
+ compute(p);
+ if (p.col_n[c] < 2) return true;
+ for (p.col_panes[c][0..p.col_n[c]]) |k| {
+ const pane = p.panes[k] orelse continue;
+ if (!pane.collapsed and p.rects[k].h < minRows(p, k)) return false;
+ }
+ return true;
+}
+
+/// Shares column `c`'s rows out again so every unfolded pane has its
+/// `minRows`, as evenly as that allows; false, changing nothing, when the
+/// column has not the rows for all of them (or holds a folded pane).
+pub fn shareColumn(p: *Pardes, c: usize) bool {
+ compute(p);
+ const n = p.col_n[c];
+ if (n == 0) return false;
+ const avail = p.screen_h -| (p.topBarHeight() + p.columnBarHeight());
+ var mins: [MAX_PANES]u16 = undefined;
+ var need: u32 = 0;
+ for (p.col_panes[c][0..n], 0..) |k, i| {
+ const pane = p.panes[k] orelse return false;
+ if (pane.collapsed) return false;
+ p.rects[k].w = p.col_w[c];
+ mins[i] = minRows(p, k);
+ need += mins[i];
+ }
+ if (need > avail) return false;
+ // Each its minimum, then what is left a row at a time to the smallest.
+ var hs: [MAX_PANES]u16 = undefined;
+ @memcpy(hs[0..n], mins[0..n]);
+ var left = avail - need;
+ while (left > 0) : (left -= 1) {
+ var low: usize = 0;
+ for (1..n) |i| if (hs[i] < hs[low]) {
+ low = i;
+ };
+ hs[low] += 1;
+ }
+ for (p.col_panes[c][0..n], 0..) |k, i| p.panes[k].?.vweight = @floatFromInt(hs[i]);
+ compute(p);
+ return true;
+}
+
/// After a resize the minima fit: each unfolded pane the resize took below
/// its `minRows` (it had them in `before`) gets them back, a row at a time
/// from whichever pane of its column has the most above its own. A pane
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index fbda6113..6631e5f9 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -2088,6 +2088,24 @@ test "a bad event record says what is wrong with it" {
_ = call(p, .{ .tag = 3, .op = .release, .node = event, .handle = h });
}
+test "a column takes panes until their documented minimums fill it, not until none can be halved" {
+ const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 50 });
+ defer p.deinit();
+ while (p.nextEffect()) |_| {}
+ const new = @intFromEnum(tree.TopFile.new);
+ while (call(p, .{ .tag = 1, .op = .open, .node = new }).reply.status == .ok) p.sync();
+ p.sync();
+ const avail = p.screen_h - (p.topBarHeight() + p.columnBarHeight());
+ for (0..p.ncol) |c| {
+ var need: u32 = 0;
+ for (p.col_panes[c][0..p.col_n[c]]) |k| need += layout.minRows(p, k);
+ // Refused only when one more pane's tag and two rows would not fit.
+ if (p.col_n[c] > 1) try testing.expect(avail - need < 3);
+ // Every pane keeps its minimum.
+ for (p.col_panes[c][0..p.col_n[c]]) |k| try testing.expect(p.rects[k].h >= layout.minRows(p, k));
+ }
+}
+
test "size is monotonic: growing is never refused, and a size once taken is taken again" {
const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 });
defer p.deinit();
diff --git a/src/pardes.zig b/src/pardes.zig
index 225dbf9e..f7aee16c 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -6630,10 +6630,16 @@ pub const Pardes = struct {
}
pub fn sync(p: *Pardes) void {
+ var refused = false;
while (p.unplaced.findFirstSet()) |id| {
p.unplaced.unset(id);
if (p.panes[id] != null) p.removePane(id, null) catch {};
+ refused = true;
}
+ // A refused pane gone, no pane it squeezed stays under its minimum.
+ if (refused) for (0..p.ncol) |c| if (!layout.columnAtMinimums(p, c)) {
+ _ = layout.shareColumn(p, c);
+ };
tagline.flattenHeaders(p);
if (p.header_column != null and p.columnBarHeight() == 0) {
tagline.exitHeader(p);