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
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.
Related
TextInput— the same editor, one line, with a rendered selection.- Forms
- Limitations — no rendered selection here, no IME in either.
- TermMosaic limitations that apply to every widget
- Source:
widgets/formon GitHub