5.6 KiB
Container Package
- Overview
- Why a Separate Package?
- What the Container Package Provides
- Design Decisions: ScrollBox and TabBar in One Package
- Package Definition
Overview
The cl-tty.container package defines the container component types:
ScrollBox and TabBar. It uses cl-tty.backend, cl-tty.box,
cl-tty.layout, and cl-tty.input.
The package exports both ScrollBox and TabBar classes, constructors, accessors, and navigation functions.
Why a Separate Package?
The base cl-tty.box package was designed for the fundamental
renderable types — box, text, spans, dirty-tracking, the render
pipeline, and the theme engine. These are the building blocks that
virtually every component depends on. Container components —
ScrollBox and TabBar — are higher-level composite widgets with
specific behavioral contracts (viewport scrolling, tab navigation,
keyboard dispatch) that are not needed by every component user.
Separating them into cl-tty.container achieves two things:
- It keeps
cl-tty.boxlean. Users who only need basic renderables (boxes, text) do not pull in scroll-logic or tab-navigation code. This is especially important for the test suite — container tests have their own setup, backend capture, and assertion patterns that are unrelated to the base component tests. - It establishes a clean dependency boundary.
cl-tty.boxdepends only oncl-tty.backendandcl-tty.layout. Container components additionally depend oncl-tty.input, because TabBar handles key events. By putting container code in its own package, we avoid creating a circular or incidental dependency between the input system and the base component layer.
What the Container Package Provides
The package exports two full component families:
- ScrollBox: A viewport-based container that holds a list of
child components and provides vertical/horizontal scrolling with
viewport culling (only visible children are rendered), scrollbar
display, sticky-scroll (auto-scroll to bottom on new content),
and scroll-offset clamping. ScrollBox inherits
dirty-mixin, implements the component protocol (render,component-children,component-layout-node), and integrates with the layout engine. Its constructormake-scroll-boxaccepts:children,:scroll-y,:scroll-x, and:sticky-scroll-pkeyword args. - TabBar: A horizontal tab-navigation widget that manages a
list of named tabs, tracks the active tab, and dispatches
keyboard events (Left/Right for prev/next). TabBar also inherits
dirty-mixinand implementsrenderandcomponent-layout-node. It providestab-bar-addfor dynamic tab creation,tab-bar-next/tab-bar-prevfor cycling,tab-bar-selectfor direct activation, andtab-bar-handle-keyfor keyboard integration.
Both components export the generic render method, allowing the
rendering pipeline to dispatch (render instance backend) uniformly.
Design Decisions: ScrollBox and TabBar in One Package
ScrollBox and TabBar are very different widgets — one manages a
scrollable viewport, the other renders a row of selectable labels.
They are kept in the same package rather than split into
cl-tty.scroll-box and cl-tty.tab-bar for several reasons:
- Shared dependencies: Both components :use the same four
packages (
cl-tty.backend,cl-tty.box,cl-tty.layout,cl-tty.input). They both inherit fromdirty-mixinand implement the component protocol. A shared package avoids duplicating the:useand:exportboilerplate. - Co-located tests: The test suite
(
tests/scrollbox-tabbar-tests.lisp) tests both components in one file and one FiveAM suite. They share test helpers, backend-capture patterns, and the same package dependency. Keeping them in one source package means the test defpackage only needs one:useclause for the container, and symbols from both components are visible together. - Common contract: Both components are "containers" in the
architectural sense — they manage a collection of sub-items
(children or tabs) and provide navigation over them. A
TabBar is conceptually a horizontal container of selectable
entries; a ScrollBox is a vertical container with scroll.
Placing them under the same
:cl-tty.containernamespace signals to users that these are the composite widget types, as opposed to the atomic renderables in:cl-tty.box. - Practical usage patterns: In typical TUI applications, a
TabBar switches between views and a ScrollBox displays the
content of each view. They are often used together in the
same composition. Having them in one package eliminates
cross-package qualification or redundant
:import-fromdeclarations when building combined layouts.
If either component grows substantial internal logic in the future (say, ScrollBox develops virtual scrolling, infinite loading, or its own input model), it could be split into its own package at that point. The current scope favors simplicity and co-location.
Package Definition
(defpackage :cl-tty.container
(:use :cl :cl-tty.backend :cl-tty.box :cl-tty.layout :cl-tty.input)
(:export
;; ScrollBox
#:scroll-box #:make-scroll-box
#:scroll-box-scroll-y #:scroll-box-scroll-x
#:scroll-box-children
#:scroll-by #:sticky-scroll-p
#:clamp-scroll
;; TabBar
#:tab-bar #:make-tab-bar
#:tab-bar-active #:tab-bar-tabs
#:tab-bar-add #:tab-bar-next #:tab-bar-prev
#:tab-bar-select #:tab-bar-handle-key
;; Rendering
#:render))