Critical fixes: case→cond in %read-event, theme resolution, SGR mouse, scrollbox/text-input/textarea render stubs, test runner exit code, ASDF rename

CRITICAL: case b → cond in %read-event (input.lisp:280)
  case with (and ...) predicate clauses treats keys as eql-compared
  atoms — all range clauses were dead code.  Every Ctrl+letter and
  printable ASCII fell through to :unknown.  text-input/textarea
  widgets were non-functional with real terminal input.  No test
  coverage of %read-event masked this.

HIGH: Theme resolution wired (backend/modern.lisp, theme.lisp)
  sgr-fg/sgr-bg now fall back to *theme-colors* hash for semantic
  keywords (:accent, :text-muted, :background-element).  *theme-colors*
  exported from cl-tty.backend.  load-preset populates it from preset
  hex values.  Previously all themed render output was invisible.

HIGH: SGR mouse parser wired (input.lisp:210-215)
  parse-sgr-mouse was defined but never called.  Now %read-escape-sequence
  detects ESC[< prefix and routes to parse-sgr-mouse.  Mouse drags,
  releases, and scroll events now parse correctly.

MEDIUM: Rendering stubs replaced
  - scrollbox: delegates to (render child backend) with position
    offset via unwind-protect (was debug string 'child at ~D')
  - text-input: draws value/placeholder at layout position
  - textarea: draws visible lines at layout position

MEDIUM: hit-test uses component-layout-node (mouse.lisp:18-31)
  Was checking nonexistent x/y/width/height slots.  Now reads
  layout-node-x/y/w/h via component-layout-node generic.

MEDIUM: test runner exit code (run-all-tests.lisp, cl-tty.asd)
  run-all-tests.lisp exits 1 if any suite fails.
  asdf:test-system exits 1 on failure.
  Renamed :cl-tty-tests to :cl-tty/test (ASDF convention).

MEDIUM: draw-border respects x/y on simple-backend (simple.lisp:42-53)
  Was writing to cursor position only.  Now uses newlines+spaces
  to reach specified coordinates (no escape sequences needed).

LOW: TabBar truncation off-by-one fixed (tabbar.lisp:47)
  >= changed to > to avoid cutting tabs 2 chars early.

LOW: Scrollbar coordinates absolute (scrollbox.lisp:61-73)
  Scrollbar drawn at viewport-relative (0,0).  Now adds layout
  node x/y offset for correct terminal positioning.

LOW: backend-write calls finish-output (modern.lisp:169)

LOW: load-preset no longer flips theme-mode (theme.lisp:43-45)
  Mode toggle caused load-preset to load wrong variant on
  second call.

All backported to org source files (org/text-input.org,
org/scrollbox-tabbar.org) so tangling produces matching .lisp.

392 tests pass, exit code 0.
This commit is contained in:
Hermes
2026-05-12 00:48:00 +00:00
parent b50c97a0cb
commit c3c330dfff
15 changed files with 504 additions and 1575 deletions

View File

@@ -319,38 +319,36 @@ when the user manually scrolls up.
#+BEGIN_SRC lisp
(defmethod render ((sb scroll-box) backend)
"Render visible children with scroll offset applied."
"Render visible children with scroll offset applied.
Delegates to each child's `render` method, temporarily offsetting
its layout-node position for the scroll offset. Children outside
the viewport are clipped out."
(let* ((ln (scroll-box-layout-node sb))
(vx 0) (vy 0) ;; viewport origin (parent position)
(vx 0) (vy 0)
(vw (if ln (layout-node-width ln) 80))
(vh (if ln (layout-node-height ln) 24))
(sy (scroll-box-scroll-y sb))
(sx (scroll-box-scroll-x sb)))
(dolist (child (scroll-box-children sb))
(let* ((cln (component-layout-node child))
(cw (if cln (layout-node-width cln) 1))
(ch (if cln (layout-node-height cln) 1))
;; Child's position after scroll offset
(cx vx)
(cy vy))
(declare (ignore cx))
;; Only render if child intersects viewport vertically
;; Only render children that are visible in the viewport
(when (and (< (+ cy (- sy)) (+ vh vy))
(> (+ cy (- sy) ch) vy))
(let ((old-ln (component-layout-node child)))
(when old-ln
;; Temporarily adjust layout to account for scroll
(let ((new-ln (make-layout-node)))
(setf (layout-node-x new-ln) (- sx)
(layout-node-y new-ln) (- sy)
(layout-node-width new-ln) cw
(layout-node-height new-ln) ch)
;; Use a captured-backend approach or just draw-text
(draw-text backend 0 (+ vy cy (- sy))
(format nil "child at ~D" vy)
nil nil)))))
(incf vy ch))))
(draw-scrollbars sb backend vw vh))
;; Temporarily offset child's layout-node position for rendering
(let ((orig-x (if cln (layout-node-x cln) 0))
(orig-y (if cln (layout-node-y cln) 0)))
(when cln
(setf (layout-node-x cln) (- orig-x sx)
(layout-node-y cln) (- orig-y sy)))
(unwind-protect
(render child backend)
(when cln
(setf (layout-node-x cln) orig-x
(layout-node-y cln) orig-y)))))
(incf vy ch)))
(draw-scrollbars sb backend vw vh)))
#+END_SRC
** ScrollBox: sticky scroll
@@ -573,6 +571,8 @@ they are truncated with an ellipsis.
:initial-value 0))
(defmethod render ((sb scroll-box) backend)
"Render ScrollBox children within the viewport, offset by scroll position.
Children outside the viewport are skipped."
(let* ((ln (scroll-box-layout-node sb))
(vx 0) (vy 0)
(vw (if ln (layout-node-width ln) 80))
@@ -583,9 +583,20 @@ they are truncated with an ellipsis.
(let* ((cln (component-layout-node child))
(ch (if cln (layout-node-height cln) 1))
(cy vy))
(when (and (< (+ cy (- sy)) (+ vh vy)) (> (+ cy (- sy) ch) vy))
(draw-text backend (- sx) (+ vy cy (- sy))
(format nil "child at ~D" vy) nil nil))
;; Only render children that are visible in the viewport
(when (and (< (+ cy (- sy)) (+ vh vy))
(> (+ cy (- sy) ch) vy))
;; Temporarily offset child's layout-node position for rendering
(let ((orig-x (if cln (layout-node-x cln) 0))
(orig-y (if cln (layout-node-y cln) 0)))
(when cln
(setf (layout-node-x cln) (- orig-x sx)
(layout-node-y cln) (- orig-y sy)))
(unwind-protect
(render child backend)
(when cln
(setf (layout-node-x cln) orig-x
(layout-node-y cln) orig-y)))))
(incf vy ch)))
(draw-scrollbars sb backend vw vh)))

File diff suppressed because it is too large Load Diff