v0.3.0: Rendering pipeline — render dispatch, tree walk, dirty propagation

- render generic function dispatches per component type
- render-screen entry point with sync wrapper
- render-node walks tree, computes layout, calls render
- component-layout-node generic (box/text methods)
- component-children/component-parent generics
- propagate-dirty marks component + ancestors dirty
- box and text now inherit from dirty-mixin
- 6 new tests: render dispatch, layout-node accessor, children,
  dirty propagation, available-width defaults
- 42 component tests, 100% GREEN
This commit is contained in:
Hermes
2026-05-11 15:12:38 +00:00
parent 88c576a6b9
commit b0e5c18257
6 changed files with 126 additions and 5 deletions

View File

@@ -0,0 +1,67 @@
(in-package :cl-tui.box)
;; ── Component Protocol ────────────────────────────────────────
(defgeneric component-layout-node (component)
(:documentation "Return the layout-node for COMPONENT.")
(:method ((bx box)) (box-layout-node bx))
(:method ((tx text)) (text-layout-node tx)))
(defgeneric component-children (component)
(:documentation "Return the children of COMPONENT, or nil.")
(:method ((bx box)) nil)
(:method ((tx text)) nil))
(defgeneric component-parent (component)
(:documentation "Return the parent of COMPONENT, or nil.")
(:method ((c t)) nil))
;; ── Rendering Pipeline ────────────────────────────────────────
(defgeneric render (component backend)
(:documentation "Render COMPONENT at its computed position using BACKEND.")
(:method ((c t) backend)
(declare (ignore backend))
(values)))
(defmethod render ((bx box) backend)
(render-box bx backend))
(defmethod render ((tx text) backend)
(render-text tx backend))
(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)))
(begin-sync backend)
(render-node root backend w h)
(end-sync backend)))
(defun render-node (node backend w h)
"Render a component NODE and its children."
(compute-layout (component-layout-node node) w h)
(render node backend)
(dolist (child (component-children node))
(render-node child backend w h)))
(defun available-width (component)
"Return the available width for COMPONENT (or 80 as default)."
(let ((ln (component-layout-node component)))
(if ln (layout-node-width ln) 80)))
(defun available-height (component)
"Return the available height for COMPONENT (or 24 as default)."
(let ((ln (component-layout-node component)))
(if ln (layout-node-height ln) 24)))
;; ── Dirty Propagation ─────────────────────────────────────────
(defun propagate-dirty (component)
"Mark COMPONENT and all ancestors dirty."
(mark-dirty component)
(let ((parent (component-parent component)))
(when parent
(propagate-dirty parent))))