From df5ceabd3bf465fce762d283deb959b71143c702 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 12 May 2026 14:00:59 +0000 Subject: [PATCH] fix: distribute-sizes rounding remainder, render-screen uses backend-size --- layout/layout.lisp | 30 +++++++++++++++++++++--------- src/components/render.lisp | 6 +++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/layout/layout.lisp b/layout/layout.lisp index d71f569..efcaa7c 100644 --- a/layout/layout.lisp +++ b/layout/layout.lisp @@ -76,7 +76,8 @@ "Compute child sizes given available space and gap. HORIZONTAL is non-nil when distributing width (row layout). 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)) (gap-total (* gap (max 0 (1- n)))) (base (mapcar (lambda (c) @@ -89,14 +90,25 @@ is distributed by grow ratio; overflow is reduced by shrink ratio." (remaining (- avail base-total gap-total)) (grow-total (reduce #'+ (mapcar #'layout-node-grow children))) (shrink-total (reduce #'+ (mapcar #'layout-node-shrink children)))) - (mapcar (lambda (c b) - (let ((sz b)) - (when (and (plusp remaining) (plusp grow-total)) - (incf sz (round (* remaining (/ (layout-node-grow c) grow-total))))) - (when (and (minusp remaining) (plusp shrink-total)) - (decf sz (round (* (abs remaining) (/ (layout-node-shrink c) shrink-total))))) - (max 1 sz))) - children base))) + (let ((sizes (mapcar (lambda (c b) + (let ((sz b)) + (when (and (plusp remaining) (plusp grow-total)) + (incf sz (round (* remaining (/ (layout-node-grow c) grow-total))))) + (when (and (minusp remaining) (plusp shrink-total)) + (decf sz (round (* (abs remaining) (/ (layout-node-shrink c) shrink-total))))) + (max 1 sz))) + 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) "Layout all children of ROOT within the given dimensions. diff --git a/src/components/render.lisp b/src/components/render.lisp index 9bae3e0..dadfa6a 100644 --- a/src/components/render.lisp +++ b/src/components/render.lisp @@ -32,9 +32,9 @@ (defun render-screen (root backend) "Render the component tree ROOT using BACKEND. Computes layout for dirty branches, calls render on each component, - and wraps output in synchronized updates." - (let ((w (available-width root)) - (h (available-height root))) + and wraps output in synchronized updates. Uses the actual terminal + dimensions from BACKEND rather than hardcoded defaults." + (multiple-value-bind (w h) (backend-size backend) (begin-sync backend) (render-node root backend w h) (end-sync backend)))