Widgets: forms & text editing
lib/tui.sh (basic widgets), lib/widgets/tui_widgets.sh, lib/widgets/tui_text.sh - the widget constructors and the text-editing engine shared by input, password and textarea. Guide: ../guide/widgets.md. ← API index for the full module list and a task-oriented tour with examples.
Constructors take ID PANE ROW .... ROW is 0-based inside the pane’s content area. A missing ID or PANE prints an error and skips the widget. Functions return 0 unless their entry says otherwise.
Every widget callback gets the widget id first, so one function can serve several widgets:
| Widget | Callback | Called as |
|---|---|---|
| button, list, table, select | action | FN ID |
| checkbox | action | FN ID VALUE (value 0/1) |
| input, password, textarea | submit | FN ID TEXT |
| any | tui.on_change |
FN ID |
Widgets
| Function | Summary |
|---|---|
tui.label |
Creates a static text widget. |
tui.button |
Creates a focusable button. |
tui.input |
Creates a single-line text input. |
tui.checkbox |
Creates a checkbox. |
tui.checkbox.toggle |
Flips a checkbox, redraws it and calls its action as ACTION ID VALUE, as a click would. |
tui.get |
Prints a widget’s value, without a trailing newline. |
tui.set |
Sets a widget’s value without redrawing it. |
tui.update |
Sets a widget’s value and redraws the widget. |
tui.set_label |
Changes the caption of a button or checkbox, or the text of a label, and redraws it. |
tui.get.label |
Prints the caption of a widget: button text, checkbox label, input label or label text. |
tui.on_action |
Sets or replaces the action function of a widget. Buttons, lists and tables call it as FN ID; checkboxes as FN ID VALUE. |
tui.on_submit |
Sets or replaces the submit function of an input, password or textarea, called as FN ID TEXT. |
tui.align |
Sets the horizontal alignment of one widget, overriding the pane default. |
tui.valign |
Sets the vertical anchor of one widget, overriding the pane default. |
tui.minsize |
Sets the minimum width of a widget in columns. |
tui.maxsize |
Sets the maximum width of a widget in columns. |
tui.pad |
Sets blank columns and rows around one widget. An empty value is left unchanged. |
tui.label_align |
Aligns an input label inside its label column. |
tui.label_width |
Gives an input label a fixed column width, so fields in a form line up. |
tui.input.retain |
Sets whether an input keeps focus after Enter. |
tui.input.blur_on_submit |
Old name for tui.input.retain ID false. |
tui.input.sticky |
With true (default), clicking empty space no longer takes focus away from the input; Esc and Tab still do. |
tui.focus |
Focuses a widget. Its pane becomes the keyboard pane. |
tui.label
tui.label ID PANE ROW TEXT
Creates a static text widget.
Parameters
ID: unique widget id.PANE: a leaf pane.ROW: 0-based row inside the pane’s content area, counted from the pane’s vertical anchor (seetui.pane_valign).TEXT: the text.
Returns: 1 and a message on stderr when ID or PANE is empty.
Notes
- Not focusable. Change the text with
tui.set_labelortui.update. - Create widgets before the first render, or call
tui.relayoutafterwards.
Example
tui.label lbl_name form 0 "Name"
tui.button
tui.button ID PANE ROW TEXT [ACTION]
Creates a focusable button.
Parameters
TEXT: caption, drawn as given (include brackets yourself:"[ Save ]").ACTION: function called asACTION IDon Enter or click.
Returns: 1 when ID or PANE is empty.
Notes
- Buttons are centered in their pane unless
tui.alignortui.pane_alignsays otherwise. ACTIONmay also be a built-in action such astui.action.back.
Example
tui.button btn_save form 3 "[ Save ]" on_save
on_save() { tui.notify "Saved $(tui.get inp_name)" success; }
See also: tui.on_action, tui.set_label
tui.input
tui.input ID PANE ROW [PLACEHOLDER] [LABEL] [SUBMIT]
Creates a single-line text input.
Parameters
PLACEHOLDER: grey text shown while empty.LABEL: text drawn before the field.SUBMIT: function called on Enter asSUBMIT ID TEXT: the widget id, then the current text.
Returns: 1 when ID or PANE is empty.
Notes
- The id lets one function serve several inputs:
case $1 in inp_name) ... ;; inp_mail) ... ;; esac. - Without
SUBMIT, an action set withtui.on_actionis called asACTION IDinstead. - The text is not cleared after submit. Clear it in
SUBMITwithtui.update ID "". - The input keeps focus after Enter by default; see
tui.input.retain. - Full editing keys (selection, word jumps, clipboard, undo): press
f1in the app, or seetui.action.text_keys.
Example
tui.input inp_cmd shell 0 "type a command" "> " run_cmd
run_cmd() { tui.output_append log "\$ $2"; tui.update "$1" ""; } # $1 = this input's id, $2 = the text
See also: tui.password, tui.textarea
tui.checkbox
tui.checkbox ID PANE ROW LABEL [CHECKED] [ACTION]
Creates a checkbox.
Parameters
CHECKED:1,trueoryesto start checked. Anything else starts unchecked.ACTION: function called asACTION ID VALUEafter each toggle: the checkbox id, then the new value0or1.
Returns: 1 when ID or PANE is empty.
Notes
- The id comes first, as for every widget callback, so one function can handle a whole group of checkboxes.
tui.getprints0/1;tui.get.checkedworks in conditions.
Example
tui.checkbox chk_wrap opts 0 "Wrap lines" 1 on_opt
tui.checkbox chk_times opts 1 "Show times" 0 on_opt
on_opt() { # ID VALUE
case "$1" in
chk_wrap) tui.config.set my.wrap "$2" ;;
chk_times) tui.config.set my.times "$2" ;;
esac
}
See also: tui.checkbox.toggle
tui.checkbox.toggle
tui.checkbox.toggle ID
Flips a checkbox, redraws it and calls its action as ACTION ID VALUE, as a click would.
Notes
- Does nothing for a widget that is not a checkbox.
- To set a value without calling the action, use
tui.updateID 0|1.
tui.get
tui.get ID
Prints a widget’s value, without a trailing newline.
Output: by type: input, password and textarea: the text (textarea lines joined by newlines); checkbox: 0 or 1; label: its text; select: the chosen option; progress: the percent. Buttons, lists and tables print nothing; use tui.list.item / tui.table.row.
Example
name="$(tui.get inp_name)"
tui.set
tui.set ID VALUE
Sets a widget’s value without redrawing it.
Notes
- Use it to prepare several widgets before one repaint, or before the first render. While the app runs, use
tui.updateso the change is visible. - Checkbox values must be
0or1.
tui.update
tui.update ID VALUE
Sets a widget’s value and redraws the widget.
Notes
- Does not call the widget’s action or
on_changefunction. - For a button or checkbox caption use
tui.set_label;VALUEis not the caption.
Example
tui.update inp_name "" # clear an input
tui.update chk_wrap 1 # check a checkbox without running its action
tui.set_label
tui.set_label ID TEXT
Changes the caption of a button or checkbox, or the text of a label, and redraws it.
Returns: 1 for an unknown id.
Example
tui.set_label btn_run "[ Running… ]"
tui.get.label
tui.get.label ID
Prints the caption of a widget: button text, checkbox label, input label or label text.
tui.on_action
tui.on_action ID FN
Sets or replaces the action function of a widget. Buttons, lists and tables call it as FN ID; checkboxes as FN ID VALUE.
tui.on_submit
tui.on_submit ID FN
Sets or replaces the submit function of an input, password or textarea, called as FN ID TEXT.
tui.align
tui.align ID left|center|right|fill
Sets the horizontal alignment of one widget, overriding the pane default.
Notes
fillstretches the widget to the pane width (used by tab headers).
tui.valign
tui.valign ID top|middle|bottom
Sets the vertical anchor of one widget, overriding the pane default.
Notes
- With
bottom, the widget ROW counts up from the last line.
tui.minsize
tui.minsize ID WIDTH
Sets the minimum width of a widget in columns.
tui.maxsize
tui.maxsize ID WIDTH
Sets the maximum width of a widget in columns.
tui.pad
tui.pad ID [HPAD] [VPAD]
Sets blank columns and rows around one widget. An empty value is left unchanged.
Notes
- For padding inside a pane use
tui.pane_pad.
tui.label_align
tui.label_align ID left|right
Aligns an input label inside its label column.
Notes
- Only has an effect together with
tui.label_width.
tui.label_width
tui.label_width ID N
Gives an input label a fixed column width, so fields in a form line up.
tui.input.retain
tui.input.retain ID [true|false]
Sets whether an input keeps focus after Enter.
Parameters
true(default): the cursor stays in the field, like a shell prompt or chat box.false: form style; Enter submits and leaves the field.
Notes
- Inputs without their own setting use
TUI_INPUT_RETAIN_ON_SUBMIT(1), or theinput.retainconfig key. - Any value other than
truemeansfalse.
tui.input.blur_on_submit
tui.input.blur_on_submit ID
Old name for tui.input.retain ID false.
tui.input.sticky
tui.input.sticky ID [true|false]
With true (default), clicking empty space no longer takes focus away from the input; Esc and Tab still do.
tui.focus
tui.focus ID
Focuses a widget. Its pane becomes the keyboard pane.
Notes
- Redraws only the old and new widget and their pane borders.
- Focusing a text widget places the cursor for editing.
See also: tui.get.focused, tui.action.unfocus
Richer widgets
Key tables and mouse behaviour: ../guide/widgets.md. Markup tags: <password> <textarea> <list> <table> <select> <progress> (attributes in share/tui.xsd). Theme classes (all optional): .list_sel .table_head .table_sel .progress .progress_fill .select .selection.
| Function | Summary |
|---|---|
tui.password |
Creates a single-line input that shows • for each character. |
tui.textarea |
Creates a multi-line text editor. |
tui.list |
Creates a scrollable single-choice list. |
tui.table |
Creates a scrollable table: a header row plus rows of \|-separated cells, one selectable row at a time. |
tui.select |
Creates a one-row dropdown. Enter, Space, Down or a click opens a picker dialog. |
tui.progress |
Creates a progress bar showing 0–100 %. Not focusable. |
tui.list.set |
Replaces all items and selects the first one (none when empty). |
tui.list.add |
Appends items. Selects the first item when nothing was selected. |
tui.list.clear |
Removes all items and clears the selection. |
tui.list.select |
Moves the selection to INDEX (0-based), clamped to the list. A negative index clears it. |
tui.list.selected |
Prints the selected index, or -1 when nothing is selected. |
tui.list.item |
Prints the item at INDEX, default the selected one. Prints nothing for an index out of range. |
tui.list.count |
Prints the number of items. |
tui.table.set |
Replaces the header and all rows, and selects the first row. |
tui.table.add |
Appends rows (\|-separated cells). Selects the first row when nothing was selected. |
tui.table.clear |
Removes all rows and clears the selection. The header is kept. |
tui.table.count |
Prints the number of rows (the header not counted). |
tui.table.select |
Moves the selection to row INDEX (0-based), clamped. Does not call on_change. |
tui.table.selected |
Prints the selected row index, or -1. |
tui.table.row |
Prints a row as a\|b\|c, default the selected one. |
tui.select.set |
Replaces the options. The current value stays selected when it is among the new options. |
tui.select.index |
Prints the index of the current value among the options, or -1. |
tui.select.pick |
Chooses option INDEX (0-based) as if the user picked it: sets the value, redraws, then calls the on_change function and the action. |
tui.progress.set |
Sets a progress bar to VALUE out of MAX and redraws it. |
tui.on_change |
Sets a function called as FN ID whenever the widget changes through the user. |
tui.password
tui.password ID PANE ROW [PLACEHOLDER] [LABEL] [SUBMIT]
Creates a single-line input that shows • for each character.
Notes
- Same parameters and
SUBMIT ID TEXTcall astui.input. Read the value withtui.get. - Copy and cut are disabled, so the text never reaches the clipboard.
tui.textarea
tui.textarea ID PANE ROW [PLACEHOLDER] [ROWS] [SUBMIT]
Creates a multi-line text editor.
Parameters
ROWS: height in rows. Omitted or0: fill the rest of the pane.SUBMIT: called asSUBMIT ID TEXTonalt+enterorctrl+enter. Enter inserts a newline.
Notes
- Supports mouse placement, drag-select, word jumps, cut/copy/paste and undo. Up/Down at the first/last line move focus out of the widget.
- Tabs are converted to four spaces, and non-printable characters are dropped on insert.
Example
tui.textarea notes editor 0 "notes..." 0 save_notes
tui.on_change notes mark_dirty
tui.list
tui.list ID PANE ROW [ACTION] [ROWS]
Creates a scrollable single-choice list.
Parameters
ACTION: called asACTION IDon Enter or double-click.ROWS: height. Omitted or0: fill the pane.
Notes
- Keys: Up/Down, PgUp/PgDn, Home/End. At the first/last row Up/Down move focus out.
- Selection moves call the
tui.on_changefunction.
Example
tui.list files side 0 open_file
tui.list.set files *.txt
open_file() { tui.output view "$(<"$(tui.list.item "$1")")"; }
tui.table
tui.table ID PANE ROW [ACTION] [ROWS]
Creates a scrollable table: a header row plus rows of |-separated cells, one selectable row at a time.
Parameters
- As
tui.list.
Example
tui.table procs main 0 show_proc
tui.table.set procs "PID|Name|CPU" "1|init|0.0" "42|bash|1.2"
tui.select
tui.select ID PANE ROW LABEL [ACTION]
Creates a one-row dropdown. Enter, Space, Down or a click opens a picker dialog.
Parameters
LABEL: text before the value, also the picker title.ACTION: called asACTION IDafter a new value is picked.
Notes
- Read the value with
tui.get, the index withtui.select.index.
Example
tui.select mode form 3 "Mode:" mode_changed
tui.select.set mode fast balanced careful
tui.progress
tui.progress ID PANE ROW [LABEL]
Creates a progress bar showing 0–100 %. Not focusable.
See also: tui.progress.set
tui.list.set
tui.list.set ID ITEM...
Replaces all items and selects the first one (none when empty).
Notes
- Does not call the
on_changefunction.
tui.list.add
tui.list.add ID ITEM...
Appends items. Selects the first item when nothing was selected.
tui.list.clear
tui.list.clear ID
Removes all items and clears the selection.
tui.list.select
tui.list.select ID INDEX
Moves the selection to INDEX (0-based), clamped to the list. A negative index clears it.
Notes
- Does not call the
on_changefunction.
tui.list.selected
tui.list.selected ID
Prints the selected index, or -1 when nothing is selected.
tui.list.item
tui.list.item ID [INDEX]
Prints the item at INDEX, default the selected one. Prints nothing for an index out of range.
tui.list.count
tui.list.count ID
Prints the number of items.
tui.table.set
tui.table.set ID HEADER ROW...
Replaces the header and all rows, and selects the first row.
Parameters
HEADER,ROW: cells separated by|, e.g."Name|Size"and"a.txt|1k".
Notes
- Each column is as wide as its widest cell; when the table is too wide, the widest columns shrink first (to at least 3). A cell cannot contain
|.
tui.table.add
tui.table.add ID ROW...
Appends rows (|-separated cells). Selects the first row when nothing was selected.
tui.table.clear
tui.table.clear ID
Removes all rows and clears the selection. The header is kept.
tui.table.count
tui.table.count ID
Prints the number of rows (the header not counted).
tui.table.select
tui.table.select ID INDEX
Moves the selection to row INDEX (0-based), clamped. Does not call on_change.
tui.table.selected
tui.table.selected ID
Prints the selected row index, or -1.
tui.table.row
tui.table.row ID [INDEX]
Prints a row as a|b|c, default the selected one.
Notes
- Split it with
IFS="|" read -r name size <<<"$(tui.table.row procs)".
tui.select.set
tui.select.set ID ITEM...
Replaces the options. The current value stays selected when it is among the new options.
Notes
- A new select has no value; call
tui.select.pickto preselect one.
tui.select.index
tui.select.index ID
Prints the index of the current value among the options, or -1.
tui.select.pick
tui.select.pick ID INDEX
Chooses option INDEX (0-based) as if the user picked it: sets the value, redraws, then calls the on_change function and the action.
Returns: 1 for an index out of range.
tui.progress.set
tui.progress.set ID VALUE [MAX]
Sets a progress bar to VALUE out of MAX and redraws it.
Parameters
VALUE: integer.MAX: integer. Default:100.
Notes
- Stored as a whole percent, clamped to 0–100.
Example
tui.progress.set job "$done" "$total"
tui.on_change
tui.on_change ID FN
Sets a function called as FN ID whenever the widget changes through the user.
Notes
- Fires after every edit (input, password, textarea), selection move (list, table) or picked value (select).
- Not called for programmatic changes such as
tui.updateortui.list.set.
Example
tui.on_change notes mark_dirty
mark_dirty() { tui.set_label btn_save "[ Save* ]"; }
Text editing
For input, password and textarea widgets. Offsets are 0-based character positions in the whole text. TUI_CLIPBOARD holds the text of the last copy or cut inside a text widget; every copy is also sent to the terminal clipboard (tui.clipboard.copy).
| Function | Summary |
|---|---|
tui.text.selection |
Prints the selected text of a text widget (nothing when nothing is selected). |
tui.text.select |
Selects characters START to END (0-based offsets into the whole text) and redraws. Offsets beyond the end are clamped. |
tui.text.select_all |
Selects the whole text and redraws. |
tui.text.cursor |
Prints the cursor position as ROW COL (1-based). |
tui.text.set_cursor |
Moves the cursor to character OFFSET (0-based, clamped) and clears the selection. |
tui.text.insert |
Inserts TEXT at the cursor, replacing the selection, as typing would. The change can be undone and calls the on_change function. |
tui.text.delete_selection |
Deletes the selected text. Returns 1 when nothing is selected. |
tui.text.undo |
Undoes the last edit (ctrl+z/alt+z). Returns 1 when there is nothing to undo. |
tui.text.redo |
Redoes the last undone edit (ctrl+y/alt+y). Returns 1 when there is nothing to redo. |
tui.text.line_count |
Prints the number of lines in a text widget (1 for an input). |
tui.action.text_keys |
Opens a scrollable list of every text-editing key. Bound to f1 by default. |
tui.text.selection
tui.text.selection ID
Prints the selected text of a text widget (nothing when nothing is selected).
tui.text.select
tui.text.select ID START END
Selects characters START to END (0-based offsets into the whole text) and redraws. Offsets beyond the end are clamped.
tui.text.select_all
tui.text.select_all ID
Selects the whole text and redraws.
tui.text.cursor
tui.text.cursor ID
Prints the cursor position as ROW COL (1-based).
tui.text.set_cursor
tui.text.set_cursor ID OFFSET
Moves the cursor to character OFFSET (0-based, clamped) and clears the selection.
tui.text.insert
tui.text.insert ID TEXT
Inserts TEXT at the cursor, replacing the selection, as typing would. The change can be undone and calls the on_change function.
Notes
- In single-line widgets newlines become spaces and control characters are removed. Tabs become four spaces.
Example
tui.text.insert notes "$(date +%F) "
tui.text.delete_selection
tui.text.delete_selection ID
Deletes the selected text. Returns 1 when nothing is selected.
tui.text.undo
tui.text.undo ID
Undoes the last edit (ctrl+z/alt+z). Returns 1 when there is nothing to undo.
tui.text.redo
tui.text.redo ID
Redoes the last undone edit (ctrl+y/alt+y). Returns 1 when there is nothing to redo.
tui.text.line_count
tui.text.line_count ID
Prints the number of lines in a text widget (1 for an input).
tui.action.text_keys
tui.action.text_keys
Opens a scrollable list of every text-editing key. Bound to f1 by default.
