fix: distribute-sizes rounding remainder, render-screen uses backend-size

This commit is contained in:
Hermes Agent
2026-05-12 14:00:59 +00:00
parent 80abb23197
commit df5ceabd3b
2 changed files with 24 additions and 12 deletions

View File

@@ -76,7 +76,8 @@
"Compute child sizes given available space and gap. "Compute child sizes given available space and gap.
HORIZONTAL is non-nil when distributing width (row layout). HORIZONTAL is non-nil when distributing width (row layout).
Each child starts from its fixed size (if any). Remaining space Each child starts from its fixed size (if any). Remaining space
is distributed by grow ratio; overflow is reduced by shrink ratio." is distributed by grow ratio; overflow is reduced by shrink ratio.
Rounding errors are amortized across the first N children."
(let* ((n (length children)) (let* ((n (length children))
(gap-total (* gap (max 0 (1- n)))) (gap-total (* gap (max 0 (1- n))))
(base (mapcar (lambda (c) (base (mapcar (lambda (c)
@@ -89,7 +90,7 @@ is distributed by grow ratio; overflow is reduced by shrink ratio."
(remaining (- avail base-total gap-total)) (remaining (- avail base-total gap-total))
(grow-total (reduce #'+ (mapcar #'layout-node-grow children))) (grow-total (reduce #'+ (mapcar #'layout-node-grow children)))
(shrink-total (reduce #'+ (mapcar #'layout-node-shrink children)))) (shrink-total (reduce #'+ (mapcar #'layout-node-shrink children))))
(mapcar (lambda (c b) (let ((sizes (mapcar (lambda (c b)
(let ((sz b)) (let ((sz b))
(when (and (plusp remaining) (plusp grow-total)) (when (and (plusp remaining) (plusp grow-total))
(incf sz (round (* remaining (/ (layout-node-grow c) grow-total))))) (incf sz (round (* remaining (/ (layout-node-grow c) grow-total)))))
@@ -97,6 +98,17 @@ is distributed by grow ratio; overflow is reduced by shrink ratio."
(decf sz (round (* (abs remaining) (/ (layout-node-shrink c) shrink-total))))) (decf sz (round (* (abs remaining) (/ (layout-node-shrink c) shrink-total)))))
(max 1 sz))) (max 1 sz)))
children base))) children base)))
;; Distribute rounding remainder to first N children so that
;; the total of sizes exactly fills avail minus gap-total.
;; Only correct when grow or shrink was actually applied —
;; otherwise children keep their fixed sizes and may not fill space.
(when (or (and (plusp remaining) (plusp grow-total))
(and (minusp remaining) (plusp shrink-total)))
(let ((delta (- avail gap-total (reduce #'+ sizes))))
(when (/= delta 0)
(loop :for i :from 0 :below (min (abs delta) n)
:do (incf (nth i sizes) (signum delta))))))
sizes)))
(defun compute-layout (root available-width available-height) (defun compute-layout (root available-width available-height)
"Layout all children of ROOT within the given dimensions. "Layout all children of ROOT within the given dimensions.

View File

@@ -32,9 +32,9 @@
(defun render-screen (root backend) (defun render-screen (root backend)
"Render the component tree ROOT using BACKEND. "Render the component tree ROOT using BACKEND.
Computes layout for dirty branches, calls render on each component, Computes layout for dirty branches, calls render on each component,
and wraps output in synchronized updates." and wraps output in synchronized updates. Uses the actual terminal
(let ((w (available-width root)) dimensions from BACKEND rather than hardcoded defaults."
(h (available-height root))) (multiple-value-bind (w h) (backend-size backend)
(begin-sync backend) (begin-sync backend)
(render-node root backend w h) (render-node root backend w h)
(end-sync backend))) (end-sync backend)))