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

Table

Rows in columns, with a header, a selection and horizontal scrolling.

Rows in columns, with a header, a selection and horizontal scrolling.

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

Table shows rows in columns, with a header, a selection and horizontal scrolling.

It is Focusable on the same terms as List: keys are consumed only while it has focus, and a press inside it selects a row and takes focus.

Rendered output

Table 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 12 × 4 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 × 10 rows

╭ modules ─────────────────────────────╮
│  module           size state         │
│  buffer         8.2 kB stable        │
│  geometry       3.1 kB stable        │
│› headless      11.4 kB beta          │
│  layout         6.7 kB stable        │
│  render        14.9 kB beta          │
│  virtual        5.0 kB alpha         │
│                                      │
╰──────────────────────────────────────╯

80 columns × 10 rows

╭ modules ─────────────────────────────────────────────────────────────────────╮
│  module           size state                                                 │
│  buffer         8.2 kB stable                                                │
│  geometry       3.1 kB stable                                                │
│› headless      11.4 kB beta                                                  │
│  layout         6.7 kB stable                                                │
│  render        14.9 kB beta                                                  │
│  virtual        5.0 kB alpha                                                 │
│                                                                              │
╰──────────────────────────────────────────────────────────────────────────────╯

120 columns × 10 rows

╭ modules ─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│  module           size state                                                                                         │
│  buffer         8.2 kB stable                                                                                        │
│  geometry       3.1 kB stable                                                                                        │
│› headless      11.4 kB beta                                                                                          │
│  layout         6.7 kB stable                                                                                        │
│  render        14.9 kB beta                                                                                          │
│  virtual        5.0 kB alpha                                                                                         │
│                                                                                                                      │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

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.

# Table — Rows in columns, with a header, a selection and horizontal scrolling.
# package: widgets/data
# constructor: data.NewTable(r buffer.Rect, cols ...data.Column) *data.Table
# 120 columns x 10 rows, rendered through widgettest.Capture

╭ modules ─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│  module           size state                                                                                         │
│  buffer         8.2 kB stable                                                                                        │
│  geometry       3.1 kB stable                                                                                        │
│› headless      11.4 kB beta                                                                                          │
│  layout         6.7 kB stable                                                                                        │
│  render        14.9 kB beta                                                                                          │
│  virtual        5.0 kB alpha                                                                                         │
│                                                                                                                      │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

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.

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.NewTable(r buffer.Rect, cols ...data.Column) *data.Table

NewTable takes a variadic of data.Column. A column is a header, a width and an accessor; the column definitions are where most of the thinking goes.

  • Header — bool — draw the header row. Toggling this invalidates, and ADR 0007’s amendment is about exactly this class of bug: a widget that caches column widths on Bounds() and is handed a new flag renders the old layout permanently. HeaderStyle is now really patched with ItemStyle as its documentation says, so the header text also carries ItemStyle’s foreground — Patch takes FG as well as BG, and that will show if you gave ItemStyle a distinct FG.
  • ItemStyle / SelectedStyle — buffer.Style — the unselected row’s background, and the base the header is patched with; SelectedStyle is the selected row’s. Since v0.5.1 the row style reaches the cell text as well as the fill, so on the selected row a single-span cell’s own Style and its column’s CellStyle no longer show — the row style overrides them, matching List and Tree. A cell carrying several spans keeps its own. If your selected row lost its cell colours after upgrading, that is this.
  • Marker — string — the gutter glyph beside the selected row.
  • Scrollbar — bool — draw the vertical scrollbar.

Key contract

Consumed only while focused.

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
shift+home/end first / last column
left / right scroll one column horizontally
enter, space activate the selection, calling OnActivate
wheel up/down scroll vertically WITHOUT moving the selection
wheel + shift scroll horizontally
press select the pressed row and take focus

KeyTab is NOT consumed, for the same reason List does not consume it.

What it costs

O(visible rows × visible columns) per frame, and the same on a resize. It is the horizontal axis, the header, the gutter and the scrollbar that are each O(1) or O(columns), never O(rows).

When not to use it

Do not use it for a hierarchy. It is flat. Nesting is Tree, and a Table with a manually indented first column loses the twisties, the expand keys and the lazy row rendering that make a Tree usable at depth.

Do not use it when a column’s content must be laid out, not measured. Column widths are computed from content and from the space available. A column holding a widget, a multi-line cell or anything with its own alignment is a composition problem — render that cell yourself and pass a string.

Do not use it and then fight the horizontal scrollbar. When the columns do not fit, Table scrolls horizontally and keeps both offsets — a column index and a cell position. Scrolling to the end clamps onto the right-hand edge of the content, which is generally not a column start, so the last column can be partly visible. That is deliberate and tested; it is not a bug to work around by shrinking your columns.

Do not use it for one field per row. List is the smaller widget and does not compute column widths.

Do not expect column selection. There is none as of v0.3.0. Selection is one row.

Instead: List for one field, Tree for nesting, Split plus a List for a master/detail pane.