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/form

TextArea

A multi-line editable field with wrapping, selection and undo.

A multi-line editable field with wrapping, selection and undo.

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

TextArea is a multi-line editable text region that wraps rather than scrolls horizontally.

The wrapping is buffer’s, not this widget’s: the content is handed to buffer.Wrap and the resulting Wrapped is cached against the rect it was built for. Wrap allocates twice over, so calling it in Draw would add an allocation to every frame — the failure ADR 0008 §4 exists to prevent. The first Draw after a width change wraps; every Draw after that reads the cache.

Rendered output

TextArea 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 1 × 1 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 × 7 rows

# capture notes

The gauge dial is Braille: eight levels 
of                                      
vertical resolution per cell.           
The meter track is Block Elements.      
If a web font mis-advances either,      

80 columns × 7 rows

# capture notes

The gauge dial is Braille: eight levels of                                      
vertical resolution per cell.                                                   
The meter track is Block Elements.                                              
If a web font mis-advances either,                                              
the plain-text capture is the                                                   

120 columns × 7 rows

# capture notes

The gauge dial is Braille: eight levels of                                                                              
vertical resolution per cell.                                                                                           
The meter track is Block Elements.                                                                                      
If a web font mis-advances either,                                                                                      
the plain-text capture is the                                                                                           

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.

# TextArea — A multi-line editable field with wrapping, selection and undo.
# package: widgets/form
# constructor: form.NewTextArea(r buffer.Rect) *form.TextArea
# 120 columns x 7 rows, rendered through widgettest.Capture

# capture notes

The gauge dial is Braille: eight levels of
vertical resolution per cell.
The meter track is Block Elements.
If a web font mis-advances either,
the plain-text capture is the

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 form provides the input widgets: TextInput, TextArea, Select, Checkbox, Radio, Toggle, Tabs, Button and KeyHint.

Four rules, each of which is a consequence of something decided elsewhere, and each of which is enforced by this package’s tests rather than by convention:

  • A widget’s space is Bounds(), never buf.Size() (ADR 0007 §1 rule 1).
  • Every widget repaints its entire Bounds() before drawing content, because the renderer diffs and never clears (ADR 0007 §1 rule 3).
  • Draw is total for every rect, including 0×0, 1×1 and anything below MinSize, and it clips rather than blanking (ADR 0007 §4).
  • Nothing derived from the size or from the text is built inside Draw. Wrap, Truncate and span construction allocate, so each widget caches the result against the rect it was computed for and rebuilds when the rect, the text or the styles change (ADR 0007 §3, ADR 0008 §4).

Constructing it

form.NewTextArea(r buffer.Rect) *form.TextArea

It shares TextInput’s editor, so the field surface is the same one: Text, caret motions, selection, undo, bracketed paste.

  • TextStyle — buffer.Style — the content. An unset value resolves to the terminal’s own colours.
  • CursorStyle — buffer.Style — the caret cell.
  • Background — buffer.Style — the field background.

Key contract

Keys are consumed only while focused, and the caret moves by VISUAL line and cell column, so Up and Down follow the wrapping rather than the newline characters.

Keys Effect
insert any printable rune
newline KeyEnter inserts U+000A
backspace KeyBackspace; ModCtrl or ModAlt deletes the word before
delete KeyDelete; ModCtrl or ModAlt deletes the word after
left / right KeyLeft / KeyRight; ModShift extends the selection, ModCtrl or ModAlt moves by word
up / down KeyUp / KeyDown, one visual line
home / end KeyHome / KeyEnd within the visual line; with ModCtrl, within the whole text
page up/down KeyPageUp / KeyPageDown, one screen
select all ModCtrl+‘a’
undo ModCtrl+‘z’
paste EventPaste, inserted verbatim INCLUDING newlines

KeyTab is NOT consumed: in a form, tab moves between fields, and swallowing it here would make a text area impossible to leave with the keyboard.

When not to use it

Do not use it where the selected range must be visible. In v0.3.0 TextArea tracks and moves a caret and supports editing, but does not draw its selection. TextInput renders its selection; TextArea does not. This is a recorded gap rather than a design choice, and it is one of the reasons TextArea is not the right widget for a value the user needs to see part of.

Do not use it to show long text the user only reads. It is editable and has no read-only mode. A scrolling read-only view is Pager, and a pager with search in it is usually what someone wants when they say “show me this file”.

Do not use it for a single-line field. TextInput is the smaller widget and has the rendered selection this one lacks.

Do not use it expecting a redo stack. There is undo and there is no redo, in either field. The editor has no redo history and adding one is a widget API addition, not a patch.

Instead: Pager to read, TextInput for one line.