From 03773fac517a2a26dc2f2813db9906d3bf7e91db Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 07:15:20 -0300 Subject: `size` is refused only when the panes' own minima do not fit, so it is monotonic The check read how the weights shared the rows after the resize, not whether the rows could hold every pane's tag and two rows: a pane the proportional squeeze took short made `size` refuse, growing included, and refuse sizes it took a moment before. It now asks layout.minRows, the minimum placement asks, per column; a size they fit is taken, and a pane the resize took under its minimum gets its rows back. An out of range size says `invalid size`, which 9ns reads as EINVAL, as the doc had. Co-Authored-By: Claude Opus 5.5 --- src/layout.zig | 77 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 15 deletions(-) (limited to 'src/layout.zig') diff --git a/src/layout.zig b/src/layout.zig index cbf0fd96..09cf23b3 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -416,10 +416,71 @@ pub fn acmePlace(p: *Pardes, c: usize, origin: ?usize, id: usize) void { /// its tag (acme's minht keeps one). pub const min_body_rows = 2; -fn minRows(p: *Pardes, id: usize) u16 { +/// The rows a placement, and a `size`, leave a pane: its tag at its width +/// and `min_body_rows`. The one minimum both ask. +pub fn minRows(p: *Pardes, id: usize) u16 { return p.wantedTagRows(id) + min_body_rows; } +/// Whether every column has the rows for each of its unfolded panes' +/// `minRows` (a folded one its tag row): the panes' own minima, not how +/// the weights happen to share the rows, so a size that fits keeps fitting +/// when it grows. One pane alone in its column always fits. +pub fn minimaFit(p: *Pardes) bool { + compute(p); + const avail = p.screen_h -| (p.topBarHeight() + p.columnBarHeight()); + for (0..p.ncol) |c| { + if (p.col_n[c] < 2) continue; + var need: u32 = 0; + for (p.col_panes[c][0..p.col_n[c]]) |k| { + const pane = p.panes[k] orelse continue; + need += if (pane.collapsed) BOX_H else minRows(p, k); + } + if (need > avail) return false; + } + 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 +/// dragged short before keeps what it had. +pub fn restoreMinima(p: *Pardes, before: *const [MAX_PANES]Rect) void { + compute(p); + for (0..p.ncol) |c| { + const n = p.col_n[c]; + if (n < 2) continue; + var hs: [MAX_PANES]u16 = undefined; + var mins: [MAX_PANES]u16 = undefined; + var short = false; + for (p.col_panes[c][0..n], 0..) |k, i| { + const pane = p.panes[k] orelse return; + if (pane.collapsed) break; + hs[i] = p.rects[k].h; + mins[i] = minRows(p, k); + if (hs[i] < mins[i] and before[k].h >= mins[i]) short = true; + } else { + if (!short) continue; + for (p.col_panes[c][0..n], 0..) |k, i| { + if (hs[i] >= mins[i] or before[k].h < mins[i]) continue; + while (hs[i] < mins[i]) { + var giver: ?usize = null; + var spare: u16 = 0; + for (0..n) |j| if (j != i and hs[j] -| mins[j] > spare) { + spare = hs[j] - mins[j]; + giver = j; + }; + const g = giver orelse break; + hs[g] -= 1; + hs[i] += 1; + } + } + for (p.col_panes[c][0..n], 0..) |k, i| p.panes[k].?.vweight = @floatFromInt(@max(1, hs[i])); + } + } + compute(p); +} + /// 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 @@ -437,20 +498,6 @@ pub fn placedRoomy(p: *Pardes, id: usize, before: *const [MAX_PANES]Rect) bool { return true; } -/// Whether every unfolded pane sharing its column has its tag and -/// `min_body_rows` (one alone in its column always fits). -pub fn everyPaneRoomy(p: *Pardes) bool { - compute(p); - for (0..p.ncol) |c| { - if (p.col_n[c] < 2) continue; - 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; -} - /// Places `id` last in column `c`, where acme's coladd puts a new window, /// with half the rows of the column's tallest unfolded pane, when that /// leaves both their rows; false, and `id` in no column, when not. -- cgit v1.3