Every function, defclass, defstruct, defgeneric, defmethod, defmacro, defvar, and defparameter in every org file now has its own #+BEGIN_SRC block with literate prose above it explaining the design reasoning. Block counts before → after: package.org: 1 → 7 container-package.org: 1 → 1 (prose expanded) dirty.org: 4 → 6 render.org: 10 → 25 theme.org: 6 → 19 box-renderable.org: 9 → 29 scrollbox.org: 8 → 26 tabbar.org: 5 → 10 backend-protocol.org: 8 → 66 modern-backend.org: 17 → 53 detection.org: 4 → 6 layout-engine.org: 9 → 36 framebuffer.org: 8 → 37 markdown-renderer.org:13 → 38 dialog.org: 17 → 23 (merged dual structure) mouse.org: 4 → 25 select.org: 12 → 30 slot.org: 4 → 12 text-input.org: 11 → 53 Total: ~153 blocks → ~502 blocks Bugs fixed during restructuring: - render.org: stray π character typo (backenπd → backend) - modern-backend.org: sgr-attr missing closing paren + #+END_SRC - detection.org: invalid #\Esc character reference - select.org: extra closing paren in select-visible-options All 13 test suites pass at 100%.
181 lines
6.8 KiB
Org Mode
181 lines
6.8 KiB
Org Mode
#+TITLE: Base Component Package
|
|
#+STARTUP: content
|
|
#+FILETAGS: :cl-tty:components:
|
|
|
|
* Overview
|
|
|
|
The ~cl-tty.box~ package is the central namespace for the component
|
|
system. It aggregates all component-related symbols — box, text,
|
|
dirty tracking, render dispatch, theme engine — under one package.
|
|
|
|
Why ~box~ as the package name? Historically the package was created
|
|
for the ~box~ and ~text~ renderables, and the name stuck as the
|
|
package grew to encompass the entire component layer. The package
|
|
~:use~s ~cl-tty.backend~ (for drawing primitives) and ~cl-tty.layout~
|
|
(for layout nodes). All component code lives in this package.
|
|
|
|
This org file is documentation-only: it explains the package design
|
|
but the code itself is just a ~defpackage~ form.
|
|
|
|
* Contract
|
|
|
|
The ~cl-tty.box~ package exports these symbol groups:
|
|
|
|
- Box: ~box~, ~make-box~, ~render-box~, border style/title accessors
|
|
- Span: ~span~, span attribute readers
|
|
- Text: ~text~, ~make-text~, ~render-text~, text accessors
|
|
- Dirty: ~dirty-mixin~, ~dirty-p~, ~mark-clean~, ~mark-dirty~
|
|
- Render: ~render~, ~render-screen~, ~render-node~, tree navigation
|
|
- Theme: ~theme~, ~make-theme~, ~theme-color~, ~load-preset~,
|
|
~define-preset~
|
|
|
|
* Implementation
|
|
|
|
~cl-tty.box~ uses ~cl-tty.backend~ for ~draw-text~, ~draw-border~,
|
|
etc., and ~cl-tty.layout~ for ~layout-node~, ~compute-layout~, and the
|
|
~vbox~/~hbox~ macros.
|
|
|
|
The only direct dependencies are these two packages — no other
|
|
application code is needed to define components.
|
|
|
|
** Box exports
|
|
|
|
The ~box~ class is the primary rectangular container: it renders a
|
|
bordered region with optional title and background color. The accessor
|
|
family (~box-border-style~, ~box-title~, ~box-title-align~,
|
|
~box-fg~, ~box-bg~) follows a consistent naming convention so that
|
|
users can infer slot names from the class name. ~render-box~ is the
|
|
specialized method that draws the border and fills the interior.
|
|
|
|
The ~box-layout-node~ accessor connects the box to its layout tree
|
|
node, which is essential for the render pipeline's coordinate
|
|
computation. We export it separately from the rendering symbols
|
|
because it is also needed by code that walks the component tree
|
|
without triggering a full render.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
(defpackage :cl-tty.box
|
|
(:use :cl :cl-tty.backend :cl-tty.layout)
|
|
(:export
|
|
;; Box
|
|
#:box #:make-box
|
|
#:box-layout-node
|
|
#:box-border-style #:box-title #:box-title-align
|
|
#:box-fg #:box-bg
|
|
#:render-box
|
|
#+END_SRC
|
|
|
|
** Span exports
|
|
|
|
Spans are lightweight inline-style records — not full classes with
|
|
layout. Each span stores a substring of the parent text along with
|
|
its visual attributes. The reader-named accessors (~span-text~,
|
|
~span-bold~, ~span-italic~, etc.) let rendering code inspect span
|
|
properties without pulling in the internal representation. We keep
|
|
the accessor list flat (no grouping macro) to make the package
|
|
surface easy to grep and to keep the API browser-friendly.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
;; Span
|
|
#:span
|
|
#:span-text #:span-bold #:span-italic #:span-underline
|
|
#:span-reverse #:span-dim #:span-fg #:span-bg
|
|
#+END_SRC
|
|
|
|
** Text exports
|
|
|
|
~text~ and ~make-text~ are the construction interface for the text
|
|
renderable. The ~text-layout-node~ accessor follows the same pattern
|
|
as ~box-layout-node~, bridging the component and layout layers.
|
|
~text-content~ and ~text-spans~ expose the raw data for rendering;
|
|
~text-fg~, ~text-bg~, and ~text-wrap-mode~ control global text
|
|
appearance. ~render-text~ is the CLOS method that walks the span list
|
|
and calls ~draw-text~ from the backend.
|
|
|
|
These symbols live in the ~cl-tty.box~ package rather than a
|
|
separate ~cl-tty.text~ package to keep inter-component references
|
|
trivial — boxes can hold text children, and text can be nested inside
|
|
other components, all without cross-package imports.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
;; Text
|
|
#:text #:make-text
|
|
#:text-layout-node #:text-content #:text-spans
|
|
#:text-fg #:text-bg #:text-wrap-mode
|
|
#:render-text
|
|
#+END_SRC
|
|
|
|
** Utility exports (for tests)
|
|
|
|
~word-wrap~ and ~split-string~ are internal text-processing utilities
|
|
used by the text renderer to break lines and tokenize input. They are
|
|
exported specifically so the test suite can unit-test them in
|
|
isolation. They are not part of the public component API and should
|
|
not be relied upon by application code outside of tests.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
;; Utilities (for tests)
|
|
#:word-wrap #:split-string
|
|
#+END_SRC
|
|
|
|
** Dirty tracking
|
|
|
|
The dirty-mixin protocol lets any component class participate in the
|
|
change-propagation system. ~dirty-mixin~ is the mixin class, and
|
|
~dirty-p~, ~mark-clean~, ~mark-dirty~ are the three operations that
|
|
the render pipeline calls to decide whether a subtree needs
|
|
re-rendering.
|
|
|
|
Having these as generic functions (rather than a single ~(setf
|
|
dirty-p)~) makes it easy for subclasses to add side effects on dirty
|
|
transitions — for example, invalidating a cached bitmap or
|
|
recomputing string metrics.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
;; Dirty tracking
|
|
#:dirty-mixin #:dirty-p #:mark-clean #:mark-dirty
|
|
#+END_SRC
|
|
|
|
** Rendering pipeline
|
|
|
|
~render~, ~render-screen~, and ~render-node~ are the three entry
|
|
points into the rendering dispatch. ~component-layout-node~,
|
|
~component-children~, and ~component-parent~ form the tree-navigation
|
|
interface that ~render-node~ uses to walk the component hierarchy.
|
|
~available-width~ and ~available-height~ are passed down the tree to
|
|
constrain layout. ~propagate-dirty~ walks upward from a changed
|
|
component to mark ancestors as dirty, ensuring the screen is
|
|
re-drawn from the correct root.
|
|
|
|
Collecting these under a single "Rendering pipeline" group signals to
|
|
readers that they form a coherent subsystem — if you override one,
|
|
you likely need to understand all of them.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
;; Rendering pipeline
|
|
#:render #:render-screen #:render-node
|
|
#:component-layout-node #:component-children #:component-parent
|
|
#:available-width #:available-height
|
|
#:propagate-dirty
|
|
#+END_SRC
|
|
|
|
** Theme engine
|
|
|
|
~theme~ and ~make-theme~ are the constructor and class for theme
|
|
objects. ~theme-mode~ selects the active color mode (light/dark).
|
|
~theme-color~ looks up a named color in the current theme.
|
|
~load-preset~ loads a theme from a file, and ~define-preset~ registers
|
|
a preset at compile time.
|
|
|
|
The theme engine is isolated from the rest of the component layer —
|
|
boxes and text reference theme colors by name at render time, and the
|
|
theme object is passed in from the application level. This separation
|
|
means themes can be swapped without touching component instances.
|
|
|
|
#+BEGIN_SRC lisp :tangle ../src/components/package.lisp
|
|
;; Theme engine
|
|
#:theme #:make-theme #:theme-mode
|
|
#:theme-color #:load-preset #:define-preset))
|
|
(in-package :cl-tty.box)
|
|
#+END_SRC
|