TermMosaic v1.0.0

v1.0.0 is the first release that makes a stability promise: the public API freezes here and Semantic Versioning applies in earnest — with one documented exception: widgets/widgettest is excluded from that promise, because it must evolve with the framework. Read the limitations before you rely on anything here.

Widgets widgets/data

List

A scrollable, selectable list with a marker gutter and a scrollbar.

A scrollable, selectable list with a marker gutter and a scrollbar.

Package widgets/data. API reference: pkg.go.dev/widgets/data.

List is a selectable, scrollable list of items.

It is Focusable: keys are consumed only while it has focus, and a press inside its rectangle takes focus as well as selecting, so a click is a complete interaction without the application writing a click handler.

Rendered output

List rendered by cmd/capture through widgettest.Capture: colour at 3 widths (40, 80, 120 columns), and the exact plain-text cell grid at the widest of them.

MinSize() returns 10 × 3 cells. That is the whole widget including its own chrome, and it is the widget's assertion about itself — below it, Draw clips rather than blanks, and what to do about that is the application's decision.

40 columns × 11 rows

╭ packages ────────────────────────────╮
│   input                              │
│   layout                             │
│>  render                            █│
│   term                              █│
│   virtual                           █│
│   widgets/basic                     █│
│   widgets/block                     █│
│   widgets/data                       │
│   widgets/form                       │
╰──────────────────────────────────────╯

80 columns × 11 rows

╭ packages ────────────────────────────────────────────────────────────────────╮
│   input                                                                      │
│   layout                                                                     │
│>  render                                                                    █│
│   term                                                                      █│
│   virtual                                                                   █│
│   widgets/basic                                                             █│
│   widgets/block                                                             █│
│   widgets/data                                                               │
│   widgets/form                                                               │
╰──────────────────────────────────────────────────────────────────────────────╯

120 columns × 11 rows

╭ packages ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│   input                                                                                                              │
│   layout                                                                                                             │
│>  render                                                                                                            █│
│   term                                                                                                              █│
│   virtual                                                                                                           █│
│   widgets/basic                                                                                                     █│
│   widgets/block                                                                                                     █│
│   widgets/data                                                                                                       │
│   widgets/form                                                                                                       │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

Plain text — the exact cell grid, trimmed per line. This is the honest form: Braille and block-element glyphs can take a different advance width in a web font than a terminal gives them, which breaks the alignment they depend on.

# List — A scrollable, selectable list with a marker gutter and a scrollbar.
# package: widgets/data
# constructor: data.NewList(r buffer.Rect, items ...data.ListItem) *data.List
# 120 columns x 11 rows, rendered through widgettest.Capture

╭ packages ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│   input                                                                                                              │
│   layout                                                                                                             │
│>  render                                                                                                            █│
│   term                                                                                                              █│
│   virtual                                                                                                           █│
│   widgets/basic                                                                                                     █│
│   widgets/block                                                                                                     █│
│   widgets/data                                                                                                       │
│   widgets/form                                                                                                       │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

What you are looking at. This is the cell grid the renderer produced, in a web page. It is not a screenshot of a terminal, and it cannot show interaction, resize or animation — a capture is one settled frame after N frames of settling. Braille and Block Elements can take a different advance width in your browser's font than in a terminal, so if a gauge or a sparkline above looks wrong, the plain-text form below it is the exact output and the colour one is the decoration. The colour frames are at 40, 80, 120 columns; the plain-text frame is at 120. More on this.

About this capture. The scrollbar thumb’s POSITION is the signal; its colour is not.

Package context

Package data provides the catalog’s scrolling data widgets: List, Table, Tree and Pager.

  • Its space is Bounds(), never buf.Size() (ADR 0007 §1 rule 1). The only place a screen dimension is read is inside a test.
  • It repaints its whole Bounds() before drawing content (rule 3). Every widget here does that by composing widgets/block.Block, whose Draw fills the rect in Background before it draws anything else — the sanctioned way to express it (ADR 0008 §2). No widget in this package draws a border, a corner or a title itself.
  • Its Draw is allocation-free (ADR 0008 §4). Anything derived from a widget’s own size — which columns are visible, which chrome survives the budget, which rows map to which items — is computed in the size-change check, cached against the rect it was computed for, and only read in Draw.
  • Draw is total. Every rect including 0x0 and 1x1 is defined, and a widget below its own MinSize draws its minimum layout clipped rather than blanking itself (ADR 0007 §4).
  • Colour is never the only signal. Selection is carried by a marker column as well as by SelectedStyle, the tree’s expansion state by a glyph, and the pager’s matches by a rendition attribute rather than a hue.

Constructing it

data.NewList(r buffer.Rect, items ...data.ListItem) *data.List

NewList takes a variadic of data.ListItem — a concrete type, which is one of the reasons the catalogue’s per-widget pages are generated from a registry rather than by reflection.

  • Marker — string — the gutter glyph beside the selected row.
  • Scrollbar — bool — draw the scrollbar. Its position is the signal; its colour is not.
  • SelectedStyle — buffer.Style — the selected row. Unset means reverse video, so selection survives NO_COLOR.

Key contract

Consumed only while focused. Every key also scrolls the selection into view, which is virtual’s ScrollIntoView rather than a per-widget re-centring rule (ADR 0007 §6 rule 2: clamp, do not recentre).

Keys Effect
up / down move the selection by one row
page up/down move it by a screen, less one row of overlap
home / end first / last row
enter, space activate the selection, calling OnActivate
wheel up/down scroll WITHOUT moving the selection
press select the pressed row and take focus

KeyTab is NOT consumed, so a form can move focus out of a list with the keyboard exactly as it moves out of a text input.

What it costs

O(visible rows) per frame, whatever the item count. The scroll arithmetic is virtual.Model’s and the row count is derived from the interior height with geometry.ClampCount; nothing in Draw or Handle touches the items slice beyond the rows it is painting. BenchmarkList100K and TestListHundredThousandItems are the evidence.

When not to use it

Do not use it for data with more than one field per row. A List item is one string. Two or more columns is Table, and bolting a fixed-width formatter onto List items is how you end up with a table that cannot scroll horizontally and misaligns on a wide glyph.

Do not use it above roughly 10,000 items if you are also updating per frame. The claim is flat cost, not free: 10,000 items render in 13,320 ns and 100,000 in 14,242 — seven percent for ten times the data, at zero allocations — and those figures are for the render. Mutating the item list invalidates; doing that every frame is your choice, not the widget’s.

Do not use it where the rows have a natural grouping or hierarchy. That is Tree, and the indentation and twisties are part of what makes a hierarchy legible.

Do not use it for a long body of text. A list scrolls; it does not wrap, and each item is truncated to the width. Prose you need to read is Pager.

Do not expect it to handle keys when it does not have focus. It is Focusable: keys are consumed only while focused, and a press inside its rect takes focus as well as selecting. Move focus deliberately.

Instead: Table for columns, Tree for hierarchy, Pager for prose.