tui
Full-screen terminal programs: widgets, layout, focus, keys, mouse and resizing, drawn into a buffer and repainted only where something changed.
import tui
tui builds full-screen terminal programs: file managers, dashboards, installers, editors, anything that takes over the window instead of printing lines. It is retained mode: you build a tree of long-lived widgets once, change their state when something happens, and the Screen turns key presses, clicks and resizes into events, and widget drawings into frames. It repaints only the cells that changed, so a large screen stays fast over a slow connection. It sits on top of term and uses os.signal to learn about window resizes.
The library started as basalam by Parsa and is now part of the standard library.
The pieces:
- Screen owns the terminal, the widget tree, the focus and the event loop.
NewTerminalScreensizes it to the terminal;NewScreengives a fixed size and never touches the terminal, which is how you test. - Widgets are structs that embed
BaseWidgetand override what they need:renderto draw,handle_eventto react,focusableto take keys. Ready-made ones:Text,Input,Button,Checkbox,List,Progress,Separator,Table,TableView,PanelandModal. - Layout containers (
Row,Column,Grid,Stack) give their children positions, so you never compute coordinates by hand. - Events go first to the focused widget and bubble up to its parents until one consumes them; whatever nobody consumes reaches the optional
Keymapand then your loop.
A complete program
A widget, a screen, and the event loop. start switches the terminal to raw mode and the alternate screen; the defer screen.close() right after it puts the terminal back on every exit path, so a crash never leaves the user's shell broken. next_event waits for the next key, click, tick or resize; dispatch hands it to the widgets; present draws what changed. A key the widgets did not consume is yours: here q quits. This program needs a real terminal, so the page only compiles it.
import term
import tui
struct Hello:
pub use tui.BaseWidget
pub presses: int = 0
pub func render(buf: tui.Buffer, bounds: tui.Rect):
buf.put_str(bounds.x + 2, bounds.y + 1, "Hello from tui!", tui.Style { bold = true, fg = tui.Cyan })
buf.put_str(bounds.x + 2, bounds.y + 3, "Enter pressed " + this.presses + " times", tui.StyleDefault)
buf.put_str(bounds.x + 2, bounds.bottom() - 1, "q quits", tui.Style { dim = true })
end
pub func handle_event(event: tui.Event): bool:
if event.is_key(term.KeyEnter):
this.presses++
ret true
end
ret false
end
end
func main:
if not term.IsTerminal():
printerrln "run me in a terminal"
ret 1
end
mut screen := tui.NewTerminalScreen(Hello {})
screen.start()
defer screen.Close()
screen.present()
mut ev := tui.Event {}
until screen.next_event(ev):
consumed := screen.dispatch(ev)
if not consumed and (ev.is_char("q") or ev.is_ctrl("c")):
screen.stop()
end
screen.present()
end
ret 0
endSee a frame without a terminal
A screen made with NewScreen never touches the terminal. You can still send it events with dispatch (or post and next_event), draw with draw, and read every row back with buffer.row_text. That is how the examples on this page produce their output, and how you write tests for your own screens.
import term
import tui
struct Hello:
pub use tui.BaseWidget
pub presses: int = 0
pub func render(buf: tui.Buffer, bounds: tui.Rect):
tui.DrawBorder(buf, bounds, tui.BorderStyle.Rounded, tui.StyleDefault, "hello")
buf.put_str(bounds.x + 2, bounds.y + 1, "Hello from tui!", tui.Style { bold = true })
buf.put_str(bounds.x + 2, bounds.y + 2, "Enter pressed " + this.presses + " times", tui.StyleDefault)
end
pub func handle_event(event: tui.Event): bool:
if event.is_key(term.KeyEnter):
this.presses++
ret true
end
ret false
end
end
func show(s: tui.Screen):
s.draw()
repeat s.buffer.height in y:
println s.buffer.row_text(y)
end
end
func main:
mut s := tui.NewScreen(Hello {}, 30, 4)
defer s.Close()
show(s)
enter := tui.Event { kind = tui.KeyEvent, key = term.Key { code = term.KeyEnter } }
_ := s.dispatch(enter)
_ := s.dispatch(enter)
show(s)
endLay out a screen
Hand widgets to a container and the container places them. A Column stacks its children; set_height with a LayoutSize chooses each share: Fixed cells, a Fill weight of what is left, or Pref for the widget's own preferred size. Make the container the root of the screen, and add each child to the screen too: the container decides where, the screen decides what is drawn and focused. On a resize the screen gives the root the new size and the layout runs again.
import tui
func fixed(n: int): tui.LayoutSize:
ret tui.LayoutSize { kind = tui.SizeKind.Fixed, cells = n }
end
func main:
title := tui.NewText("Downloads") as dyn tui.Widget
rule := tui.NewSeparator() as dyn tui.Widget
mut bar := tui.NewProgress()
bar.set_progress(40)
progress := bar as dyn tui.Widget
mut body := tui.NewText("salam-0.6.3.tar.gz is 40% done. The rest of this line wraps inside the column.")
body.set_wrap(tui.WrapMode.Word)
text := body as dyn tui.Widget
mut col := tui.NewColumn()
col.set_padding(tui.UniformInsets(1))
i := col.Add(title)
j := col.Add(rule)
k := col.Add(progress)
_ := col.Add(text)
col.set_height(i, fixed(1))
col.set_height(j, fixed(1))
col.set_height(k, fixed(1))
mut s := tui.NewScreen(col as dyn tui.Widget, 32, 8)
defer s.Close()
_ := s.Add(tui.RootId, title)
_ := s.Add(tui.RootId, rule)
_ := s.Add(tui.RootId, progress)
_ := s.Add(tui.RootId, text)
s.draw()
repeat s.buffer.height in y:
println "|" + s.buffer.row_text(y) + "|"
end
endA form: focus, typing and callbacks
Focusable widgets take turns receiving keys. The first focusable widget added gets the focus; with NewDefaultKeymap installed, Tab and the arrow keys move it. Values come back through callbacks: on_submit for an Input when Enter is pressed, on_toggle for a Checkbox, on_click for a Button. Callbacks are lambdas and cannot capture local variables, so keep the state they change in globals or in your own widget.
import term
import tui
mut name := ""
mut subscribed := false
mut saved := 0
func rect(x: int, y: int, w: int): tui.Rect:
ret tui.Rect { x = x, y = y, width = w, height = 1 }
end
func key(seq: str): tui.Event:
ret tui.Event { kind = tui.KeyEvent, key = term.Decode(seq) }
end
func typing(s &: tui.Screen, text: str):
repeat text.Len() in i:
_ := s.dispatch(key(text.Substr(i, 1)))
end
end
func main:
mut s := tui.NewScreen(tui.BaseWidget {}, 30, 4)
defer s.Close()
s.set_keymap(tui.NewDefaultKeymap())
label := s.Add(tui.RootId, tui.NewText("Name:") as dyn tui.Widget)
s.widget(label).SetBounds(rect(1, 0, 6))
mut entry := tui.NewInput()
entry.set_placeholder("your name")
entry.on_submit = (value: str):
name = value
ret true
end
field := s.Add(tui.RootId, entry as dyn tui.Widget)
s.widget(field).SetBounds(rect(7, 0, 20))
mut box := tui.NewCheckbox("Send me news", false)
box.on_toggle = (enabled: bool): subscribed = enabled end
news := s.Add(tui.RootId, box as dyn tui.Widget)
s.widget(news).SetBounds(rect(1, 1, 20))
mut button := tui.NewButton("Save")
button.on_click = (): saved++ end
save := s.Add(tui.RootId, button as dyn tui.Widget)
s.widget(save).SetBounds(rect(1, 2, 10))
println "focused first:", s.focused_id() == field
typing(s, "Sara")
_ := s.dispatch(key("\r"))
_ := s.dispatch(key("\t"))
_ := s.dispatch(key(" "))
_ := s.dispatch(key("\t"))
_ := s.dispatch(key("\r"))
println "name:", name, "news:", subscribed, "saved:", saved
s.draw()
repeat s.buffer.height in y:
println "|" + s.buffer.row_text(y) + "|"
end
endLists and tables
List shows one selectable item per row and moves with j and k (the arrow keys stay free for focus moves); Enter calls on_select. TableView is a scrolling table with a selected row; Up and Down move it and Enter calls activate with the data row. Columns take a LayoutSize width, an alignment and what to do with text that does not fit.
import term
import tui
mut opened := -1
func row(a: str, b: str): Vector<str>:
mut v := Vector {} as Vector<str>
v.Push(a)
v.Push(b)
ret v
end
func column(width: int, align: tui.Alignment): tui.TableColumn:
ret tui.TableColumn { width = tui.LayoutSize { kind = tui.SizeKind.Fixed, cells = width }, align = align }
end
func down(): tui.Event:
ret tui.Event { kind = tui.KeyEvent, key = term.Key { code = term.KeyDown } }
end
func main:
mut s := tui.NewScreen(tui.BaseWidget {}, 34, 6)
defer s.Close()
mut files := Vector {} as Vector<str>
files.Push("main.salam")
files.Push("util.salam")
files.Push("README.md")
list := s.Add(tui.RootId, tui.NewList(files) as dyn tui.Widget)
s.widget(list).SetBounds(tui.Rect { x = 0, y = 0, width = 12, height = 3 })
mut t := tui.NewTableView()
_ := t.add_column(column(8, tui.Alignment.Left))
_ := t.add_column(column(7, tui.Alignment.Right))
t.header = true
_ := t.add_row(row("Name", "Lines"))
_ := t.add_row(row("tui", "8956"))
_ := t.add_row(row("signal", "976"))
_ := t.add_row(row("term", "1944"))
t.activate = (index: int):
opened = index
ret true
end
table := s.Add(tui.RootId, t as dyn tui.Widget)
s.widget(table).SetBounds(tui.Rect { x = 13, y = 0, width = 20, height = 5 })
_ := s.focus(list)
_ := s.dispatch(tui.Event { kind = tui.KeyEvent, key = term.Decode("j") })
_ := s.focus(table)
_ := s.dispatch(down())
_ := s.dispatch(tui.Event { kind = tui.KeyEvent, key = term.Key { code = term.KeyEnter } })
names := ["tui", "signal", "term"]
println "opened:", names[opened]
s.draw()
repeat s.buffer.height in y:
println "|" + s.buffer.row_text(y) + "|"
end
end| Need | Use |
|---|---|
| Draw text, fill or a frame | Buffer.put_str, fill, DrawBorder |
| Colors and attributes | Style with fg, bg, bold... and the color constants or Color.Rgb |
| Place widgets | NewColumn, NewRow, NewGrid, NewStack |
| Move the focus | focus, focus_next, focus_left... or set_keymap(NewDefaultKeymap()) |
| Your own keys and sequences | Keymap.bind, bind_seq, NewVimKeymap |
| A dialog over the screen | Modal with push_focus and raise_subtree |
| Something on a timer | tick_ms and run_with_tick |
| Clicks | set mouse before start; Event.mouse_point |
| Test a screen | NewScreen, dispatch, draw, buffer.row_text |
| Only colors or a progress bar on plain output | term |
What to keep in mind:
- Always
defer screen.close()on the line afterstart(). - A child given to a container or a
Panelmust also be added to the screen, or it is placed but never drawn. - A
dyn Widgetcannot be turned back into its struct, so read values through callbacks or keep them in your own state. - Draw only inside
render; change state inhandle_eventor in your loop, then the nextpresentshows it. After changing state the widgets cannot see, setscreen.dirty = true. - Glyphs fall back to ASCII on terminals without UTF-8; set
SALAM_TUI_GLYPHStoasciiorunicodeto choose. Colors followNO_COLOR,COLORTERMandTERM. - On Windows resizes are noticed by polling the size about every 20 milliseconds instead of by a signal.
Types
Widget
interface Widgetdyn Widget.import term
import tui
struct Counter:
pub use tui.BaseWidget
pub count: int = 0
pub func focusable(): bool:
ret true
end
pub func handle_event(event: tui.Event): bool:
if event.is_char("+"):
this.count++
ret true
end
ret false
end
pub func render(buf: tui.Buffer, bounds: tui.Rect):
mut style := tui.StyleDefault
if buf.focused:
style = tui.Style { reverse = true }
end
buf.put_str(bounds.x, bounds.y, "count " + this.count, style)
end
end
func main:
base := tui.BaseWidget {}
println "defaults:", base.Focusable(), base.Hits(), base.Visible(), base.PreferredSize(10).width
m := base.Measure(tui.Axis.X, 10)
println "measures:", m.ok
mut tree := tui.NewTree(tui.BaseWidget {} as dyn tui.Widget)
id := tree.Add(tui.RootId, Counter {} as dyn tui.Widget)
println "tree:", tree.count(), tree.valid(id), tree.parent(id) == tui.RootId, tree.valid(tui.NoWidget)
plus := tui.Event { kind = tui.KeyEvent, key = term.Decode("+") }
println "handled by:", tree.dispatch(id, plus) == id
w := tree.Get(id)
w.SetBounds(tui.Rect { width = 10, height = 1 })
mut buf := tui.NewBuffer(10, 1)
w.Render(buf, w.Bounds())
println buf.row_text(0)
buf.Free()
tree.Free()
_ := base.rect
endBaseWidget
struct BaseWidgetpub use tui.BaseWidget and override what you need. On its own it is a widget that paints nothing, is unfocusable, reports itself visible and ignores every event, which also makes it a useful container or placeholder.| Name | Type | Description |
|---|---|---|
rect | Rect | Storage for bounds() and set_bounds(). Embedding widgets can read it directly as this.rect. |
The example for this item is shown with the ones above: tui-widget.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-widget.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-widget.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-widget.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-widget.salam
Visible
func Visible(): boolvisible
func visible(): boolThe example for this item is shown with the ones above: tui-widget.salam
Bounds
func Bounds(): Rectbounds
func bounds(): RectThe example for this item is shown with the ones above: tui-widget.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-widget.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-widget.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureThe example for this item is shown with the ones above: tui-widget.salam
Tree
struct Tree| Name | Type | Description |
|---|---|---|
widgets | Vector | The widgets, indexed by ID. Copies of a dyn value share the widget, so widgets.get(id) reaches the same widget the caller added. |
parents | Vector | parents[id] is the parent's ID; NoWidget for the root. |
The example for this item is shown with the ones above: tui-widget.salam
Count
func Count(): intcount
func count(): intThe example for this item is shown with the ones above: tui-widget.salam
Valid
func Valid(id: int): boolvalid
func valid(id: int): boolThe example for this item is shown with the ones above: tui-widget.salam
Add
func Add(parent: int, w: dyn Widget): intadd
func add(parent: int, w: dyn Widget): intThe example for this item is shown with the ones above: tui-widget.salam
Get
func Get(id: int): dyn Widgetget
func get(id: int): dyn WidgetThe example for this item is shown with the ones above: tui-widget.salam
Parent
func Parent(id: int): intparent
func parent(id: int): intThe example for this item is shown with the ones above: tui-widget.salam
Dispatch
func Dispatch(from: int, event: Event): intdispatch
func dispatch(from: int, event: Event): intfrom, then to each of its ancestors up to the root, until one consumes it. Returns the consumer's ID, or NoWidget when nobody did. An invalid from starts at the root.The example for this item is shown with the ones above: tui-widget.salam
Free
func Free()free
func free()The example for this item is shown with the ones above: tui-widget.salam
Alignment
enum Alignment| Name | Type | Description |
|---|---|---|
Left | | Lines start at the box's left edge (the default). |
Center | | Lines are centred; when the slack is odd the extra cell is on the right. |
Right | | Lines end at the box's right edge. |
import tui
func main:
lines := tui.WrapText("terminal user interfaces in Salam", 12)
each line in lines:
println "[" + line + "]"
end
size := tui.MeasureText("two\nlines", 20)
println "measured", size.width, "x", size.height
mut buf := tui.NewBuffer(20, 4)
mut title := tui.NewText("centered")
title.set_align(tui.Alignment.Center)
title.set_style(tui.Style { bold = true })
title.SetBounds(tui.Rect { x = 0, y = 0, width = 20, height = 1 })
title.Render(buf, title.Bounds())
mut rule := tui.NewSeparator()
rule.SetBounds(tui.Rect { x = 0, y = 1, width = 20, height = 1 })
rule.Render(buf, rule.Bounds())
mut bar := tui.NewProgress()
bar.set_max(200)
bar.set_progress(50)
bar.SetBounds(tui.Rect { x = 0, y = 2, width = 20, height = 1 })
bar.Render(buf, bar.Bounds())
mut long := tui.NewText("no wrap here at all")
long.set_wrap(tui.WrapMode.None)
long.SetBounds(tui.Rect { x = 0, y = 3, width = 10, height = 1 })
buf.clip = long.Bounds()
long.Render(buf, long.Bounds())
buf.clip = buf.area()
repeat buf.height in y:
println "|" + buf.row_text(y) + "|"
end
println "progress", bar.progress(), "preferred width", title.preferred_width()
buf.Free()
endWrapMode
enum WrapMode| Name | Type | Description |
|---|---|---|
Word | | Greedy word-boundary wrapping at the box width (the default). |
None | | Each explicit line is drawn as-is; the box edge clips it. |
The example for this item is shown with the ones above: tui-text.salam
Text
struct Text| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
content | str = "" | The text to draw. An explicit "\n" starts a new line (see WrapText). |
align | Alignment | Horizontal placement of each line. Defaults to Alignment.Left. |
style | Style | How the content is drawn. The background fills the whole box; the foreground and attributes apply to the glyph cells only, interior spaces included, so an underline stays continuous. StyleDefault draws with the terminal's defaults. The buffer emits it at its tier. |
wrap | WrapMode | How the content becomes lines at render time. Defaults to WrapMode.Word. |
The example for this item is shown with the ones above: tui-text.salam
SetText
func SetText(content: str)set_text
func set_text(content: str)The example for this item is shown with the ones above: tui-text.salam
SetSource
func SetSource(src: func(): str)set_source
func set_source(src: func(): str)src at render and measure time; the last setter wins. Keep its value stable within a frame. Closures capture by value, so read module state or a shared holder. Set the source before Screen.add, which copies the widget. Changes do not repaint or relayout automatically: call invalidate(id), register a tick, or call set_bounds again.The example for this item is shown with the ones above: tui-text.salam
ClearSource
func ClearSource()clear_source
func clear_source()The example for this item is shown with the ones above: tui-text.salam
SetAlign
func SetAlign(align: Alignment)set_align
func set_align(align: Alignment)The example for this item is shown with the ones above: tui-text.salam
SetStyle
func SetStyle(style: Style)set_style
func set_style(style: Style)The example for this item is shown with the ones above: tui-text.salam
SetWrap
func SetWrap(wrap: WrapMode)set_wrap
func set_wrap(wrap: WrapMode)The example for this item is shown with the ones above: tui-text.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-text.salam
PreferredWidth
func PreferredWidth(): intpreferred_width
func preferred_width(): intThe example for this item is shown with the ones above: tui-text.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-text.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolFocusable
func Focusable(): boolfocusable
func focusable(): boolHits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureCellOverflow
enum CellOverflow| Name | Type | Description |
|---|---|---|
Clip | | Text past the edge is cut off (the default). |
Ellipsis | | The last visible cell is replaced by the ellipsis character. |
import term
import tui
func row(a: str, b: str): Vector<str>:
mut v := Vector {} as Vector<str>
v.Push(a)
v.Push(b)
ret v
end
func main:
mut t := tui.NewTable()
_ := t.add_column(tui.TableColumn {})
_ := t.add_column(tui.TableColumn {})
t.set_column_width(0, tui.LayoutSize { kind = tui.SizeKind.Fixed, cells = 9 })
t.set_column_align(1, tui.Alignment.Right)
t.set_column_overflow(0, tui.CellOverflow.Ellipsis)
t.set_pad_x(1)
t.header = true
t.rule = true
t.header_style = tui.Style { bold = true }
_ := t.add_row(row("Package", "Size"))
_ := t.add_row(row("tui", "8956"))
_ := t.add_row(row("os.signal", "976"))
_ := t.add_row(row("encoding.json", "3100"))
t.set_cell_align(3, 1, tui.Alignment.Left)
pref := t.PreferredSize(40)
println "preferred height", pref.height
mut buf := tui.NewBuffer(20, pref.height)
t.SetBounds(buf.area())
t.Render(buf, buf.area())
repeat buf.height in y:
println "|" + buf.row_text(y) + "|"
end
println "columns", t.columns.Len(), "rows", t.rows.Len(), "focusable", t.Focusable()
buf.Free()
mut v := tui.NewTableView()
_ := v.add_column(tui.TableColumn { width = tui.LayoutSize { kind = tui.SizeKind.Fill, weight = 1 }, overflow = tui.CellOverflow.Clip })
repeat 10 in i:
mut one := Vector {} as Vector<str>
one.Push("line " + i)
_ := v.add_row(one)
end
v.SetBounds(tui.Rect { width = 12, height = 3 })
down := tui.Event { kind = tui.KeyEvent, key = term.Key { code = term.KeyDown } }
repeat 4:
_ := v.HandleEvent(down)
end
println "selected", v.selected, "first visible", v.offset
endTableColumn
struct TableColumn| Name | Type | Description |
|---|---|---|
width | LayoutSize | Fixed exact cells, Pref the content width, Fill a star share. |
min | int = 0 | Lower bound for a Pref column, in cells. |
max | int = -1 | Upper bound for a Pref column, in cells. -1 means unbounded. |
align | Alignment | Default alignment of this column's cells and header. |
overflow | CellOverflow | How text wider than the column is shown. |
The example for this item is shown with the ones above: tui-table.salam
Table
struct Table| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
columns | Vector | The column definitions, left to right. |
rows | Vector | The cell text by row, then column. Row 0 is the header when header is on. |
header | bool = false | When true, row 0 is the header. |
rule | bool = true | Draws a rule under the header. Only meaningful when header is on. |
pad_x | int = 1 | Blank cells on each side of every cell's text. |
style | Style | Style of the data rows. |
header_style | Style | Style of the header row. It replaces style there, rather than merging. |
The example for this item is shown with the ones above: tui-table.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-table.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-table.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-table.salam
AddColumn
func AddColumn(col: TableColumn): intadd_column
func add_column(col: TableColumn): intThe example for this item is shown with the ones above: tui-table.salam
AddRow
func AddRow(cells: Vector<str>): intadd_row
func add_row(cells: Vector<str>): intThe example for this item is shown with the ones above: tui-table.salam
SetColumnWidth
func SetColumnWidth(i: int, w: LayoutSize)set_column_width
func set_column_width(i: int, w: LayoutSize)The example for this item is shown with the ones above: tui-table.salam
SetColumnAlign
func SetColumnAlign(i: int, a: Alignment)set_column_align
func set_column_align(i: int, a: Alignment)The example for this item is shown with the ones above: tui-table.salam
SetColumnOverflow
func SetColumnOverflow(i: int, o: CellOverflow)set_column_overflow
func set_column_overflow(i: int, o: CellOverflow)The example for this item is shown with the ones above: tui-table.salam
SetCellAlign
func SetCellAlign(row: int, col: int, a: Alignment)set_cell_align
func set_cell_align(row: int, col: int, a: Alignment)The example for this item is shown with the ones above: tui-table.salam
SetPadX
func SetPadX(n: int)set_pad_x
func set_pad_x(n: int)The example for this item is shown with the ones above: tui-table.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-table.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureThe example for this item is shown with the ones above: tui-table.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-table.salam
ResolveWidths
func ResolveWidths(w: int): Vector<int>PaintRow
func PaintRow(buf: Buffer, bounds: Rect, ws: Vector<int>, r: int, y: int, style: Style)PaintRule
func PaintRule(buf: Buffer, bounds: Rect, ws: Vector<int>, y: int, style: Style)Visible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)TableView
struct TableView| Name | Type | Description |
|---|---|---|
Table | Table | The embedded Table: columns, rows, header and styles, drawn by the view with scrolling and a selected row. |
offset | int = 0 | Index of the first visible data row. |
selected | int = 0 | The selected data row, or -1 when there are no data rows. |
activate | func(int): bool | Called with the selected row when Enter is pressed on a non-empty table. Its result is the consumed flag: the default returns false, so Enter bubbles on. |
The example for this item is shown with the ones above: tui-table.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-table.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-table.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-table.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-table.salam
Visible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectTable.preferred_size, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureTable.measure, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
AddColumn
func AddColumn(col: TableColumn): intadd_column
func add_column(col: TableColumn): intTable.add_column, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
AddRow
func AddRow(cells: Vector<str>): intadd_row
func add_row(cells: Vector<str>): intTable.add_row, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
SetColumnWidth
func SetColumnWidth(i: int, w: LayoutSize)set_column_width
func set_column_width(i: int, w: LayoutSize)Table.set_column_width, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
SetColumnAlign
func SetColumnAlign(i: int, a: Alignment)set_column_align
func set_column_align(i: int, a: Alignment)Table.set_column_align, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
SetColumnOverflow
func SetColumnOverflow(i: int, o: CellOverflow)set_column_overflow
func set_column_overflow(i: int, o: CellOverflow)Table.set_column_overflow, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
SetCellAlign
func SetCellAlign(row: int, col: int, a: Alignment)set_cell_align
func set_cell_align(row: int, col: int, a: Alignment)Table.set_cell_align, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
SetPadX
func SetPadX(n: int)set_pad_x
func set_pad_x(n: int)Table.set_pad_x, on the table inside the view.The example for this item is shown with the ones above: tui-table.salam
ResolveWidths
func ResolveWidths(w: int): Vector<int>PaintRow
func PaintRow(buf: Buffer, bounds: Rect, ws: Vector<int>, r: int, y: int, style: Style)PaintRule
func PaintRule(buf: Buffer, bounds: Rect, ws: Vector<int>, y: int, style: Style)BorderStyle
enum BorderStyle| Name | Type | Description |
|---|---|---|
None | | No border; the content area is the full area. |
Single | | A one-cell ring of light lines with square corners. |
Double | | A one-cell ring of double lines. |
Rounded | | A one-cell ring of light lines with arc corners. |
import str
import tui
func show(seq: str): str:
ret str.Replace(seq, "\x1b", "ESC")
end
func main:
println show(tui.FgSeq(tui.Red)), show(tui.BgSeq(tui.Blue))
orange := tui.Color.Rgb(255, 128, 0)
println "truecolor:", show(tui.FgSeqAt(orange, tui.ColorTier.TrueColor))
println "256 colors:", show(tui.FgSeqAt(orange, tui.ColorTier.Indexed256))
println "16 colors:", show(tui.BgSeqAt(orange, tui.ColorTier.Ansi16))
println "no color:", show(tui.FgSeqAt(orange, tui.ColorTier.Off))
println "lowered:", tui.Lower(tui.Color.Indexed(196), tui.ColorTier.Ansi16) == tui.BrightRed
println "palette:", tui.Black != tui.White, tui.Green != tui.BrightGreen, tui.Yellow != tui.Magenta, tui.Cyan != tui.BrightCyan
println "bright:", tui.BrightBlack != tui.BrightYellow, tui.BrightBlue != tui.BrightMagenta, tui.BrightWhite != tui.Color.Default
println "terminal tier known:", tui.DetectTier() == tui.DetectTier()
base := tui.Style { fg = tui.Green, bold = true }
over := tui.Style { bg = tui.Black, underline = true }
both := tui.StyleMerge(base, over)
println "merged keeps bold:", both.bold, "and underline:", both.underline
println "equal to default:", tui.StyleEq(tui.StyleDefault, tui.Style {})
println "on:", show(tui.StyleSgr(both, tui.ColorTier.Ansi16)), "off:", show(tui.StyleSgrOff(both, tui.ColorTier.Ansi16))
println "frame inset:", tui.BorderInset(tui.BorderStyle.Single), tui.BorderInset(tui.BorderStyle.None)
println "styles built:", tui.SgrBuilds() > 0
_ := tui.Style { dim = true, italic = true, reverse = true }
endStyle
struct Style| Name | Type | Description |
|---|---|---|
fg | Color | Glyph foreground; Color.Default leaves the terminal's own foreground. |
bg | Color | Cell background; Color.Default leaves the terminal's own background. |
bold | bool = false | Bold weight only; never brightens the foreground slot. |
dim | bool = false | Reduced intensity. |
italic | bool = false | Slanted glyphs. |
underline | bool = false | A single underline. |
reverse | bool = false | Swaps the foreground and background the terminal draws with. |
The example for this item is shown with the ones above: tui-style.salam
Orientation
enum Orientation| Name | Type | Description |
|---|---|---|
Horizontal | | A rule across row 0 of the rect, full width. The default. |
Vertical | | A rule down column 0 of the rect, full height. |
The example for this item is shown with the ones above: tui-text.salam
Separator
struct Separator| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
orientation | Orientation | Which line is drawn. Horizontal by default. |
ch | str = "" | The rune, one cell wide. Empty means the orientation's default rune. |
style | Style | The rule's style. Its foreground colours the rune cells. A set background fills the whole rect before the rune is drawn. |
The example for this item is shown with the ones above: tui-text.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-text.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-text.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-text.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-text.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureThe example for this item is shown with the ones above: tui-text.salam
SetOrientation
func SetOrientation(o: Orientation)set_orientation
func set_orientation(o: Orientation)The example for this item is shown with the ones above: tui-text.salam
SetCh
func SetCh(ch: str): boolset_ch
func set_ch(ch: str): boolThe example for this item is shown with the ones above: tui-text.salam
SetStyle
func SetStyle(s: Style)set_style
func set_style(s: Style)The example for this item is shown with the ones above: tui-text.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-text.salam
Visible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)Screen
struct Screen| Name | Type | Description |
|---|---|---|
events | chan.Chan | The channel the input thread sends events on and next_event reads. |
root | dyn Widget | The root widget (ID 0). It always covers the whole screen. |
focused | dyn Widget | The widget that currently receives events first. |
buffer | Buffer | The back buffer widgets draw into. |
glyphs | GlyphPolicy | The glyph policy seeded into every frame's buffer copy. Unicode by default; start() replaces it with DetectGlyphPolicy() unless the app called set_glyphs. |
running | bool = false | True from start() (or the first run) until stop(). |
tree | Tree | The widget registry. Prefer add() and widget() to editing it. |
tick_ms | int = 0 | Interval of TickEvents in milliseconds; 0 (the default) sends none. Set it before start(). |
mouse | bool = false | When true, start() enables click reporting: presses and releases arrive as MouseEvents. Set it before start(). |
mouse_motion | bool = false | When true, start() enables all-motion reporting and every pointer movement, press, and release arrives as a MouseEvent. Includes clicks, so it does not need mouse, and reports are frequent. Set it before start(). |
keymap | Keymap | The opt-in key bindings, consulted as a dispatch fallback for KeyEvents no widget consumed. Empty by default, so an app that never touches it behaves exactly as before. Bind through this field to extend it in place, or install a whole map with set_keymap. Never assign a Keymap directly: it would leak the current map's buffers. |
dirty | bool = true | True when the next present() must repaint the whole frame: a resize, a tick, an added widget, raise/unraise, a keymap-consumed key, or invalidate of an unknown widget. A precise change is a region. |
focus_stack | Vector | Widget IDs; the top (last) entry is the focused widget, and an empty stack means nothing is focused. Change it through focus, push_focus, pop_focus, and the navigation methods, which keep focused in sync and queue the old and new bounds. |
import term
import tui
func at(x: int, y: int): tui.Rect:
ret tui.Rect { x = x, y = y, width = 8, height = 1 }
end
func key(code: int): tui.Event:
ret tui.Event { kind = tui.KeyEvent, key = term.Key { code = code } }
end
func main:
mut s := tui.NewScreen(tui.BaseWidget {}, 20, 3)
defer s.Close()
a := s.Add(tui.RootId, tui.NewButton("A") as dyn tui.Widget)
b := s.Add(tui.RootId, tui.NewButton("B") as dyn tui.Widget)
c := s.Add(tui.RootId, tui.NewButton("C") as dyn tui.Widget)
s.widget(a).SetBounds(at(0, 0))
s.widget(b).SetBounds(at(10, 0))
s.widget(c).SetBounds(at(0, 2))
println "nothing focused yet:", s.focused_id() == tui.NoWidget
_ := s.focus(a)
println "focused a:", s.focused_id() == a, "can focus b:", s.can_focus(b)
_ := s.focus_right()
println "right:", s.focused_id() == b
_ := s.focus_left()
_ := s.focus_down()
println "down:", s.focused_id() == c
_ := s.focus_up()
_ := s.focus_next()
_ := s.focus_prev()
println "after next and prev:", s.focused_id() == a
_ := s.push_focus(c)
_ := s.pop_focus()
println "pop goes back:", s.focused_id() == a, "stack:", s.focus_stack.Len()
println "hit at 11,0:", s.hit_test(11, 0) == b, "nothing at 19,2:", s.hit_test(19, 2) == tui.RootId
mut layer := Vector {} as Vector<int>
layer.Push(c)
s.raise(layer)
println "raised takes focus:", s.focused_id() == c
s.unraise(layer)
s.raise_subtree(b)
s.unraise_subtree(b)
s.set_keymap(tui.NewDefaultKeymap())
println "posted:", s.post(key(term.KeyTab))
mut ev := tui.Event {}
if s.next_event(ev):
println "consumed:", s.dispatch(ev)
end
s.invalidate(a)
s.invalidate_subtree(tui.RootId)
s.resize(22, 3)
s.fit_root()
s.draw()
s.present()
println "|" + s.buffer.row_text(0) + "|"
println "|" + s.buffer.row_text(2) + "|"
println "widgets:", s.tree.count(), "dirty:", s.dirty, "focused bounds width:", s.focused.Bounds().width
println "root covers", s.root.Bounds().width, "x", s.root.Bounds().height, "glyphs unicode:", s.glyphs == tui.GlyphPolicy.Unicode
_ := s.events
_ := s.keymap
s.stop()
endAdd
func Add(parent: int, w: dyn Widget): intadd
func add(parent: int, w: dyn Widget): intThe example for this item is shown with the ones above: tui-screen.salam
Raise
func Raise(ids: Vector<int>)raise
func raise(ids: Vector<int>)The example for this item is shown with the ones above: tui-screen.salam
Unraise
func Unraise(ids: Vector<int>)unraise
func unraise(ids: Vector<int>)The example for this item is shown with the ones above: tui-screen.salam
RaiseSubtree
func RaiseSubtree(id: int)raise_subtree
func raise_subtree(id: int)The example for this item is shown with the ones above: tui-screen.salam
UnraiseSubtree
func UnraiseSubtree(id: int)unraise_subtree
func unraise_subtree(id: int)The example for this item is shown with the ones above: tui-screen.salam
Widget
func Widget(id: int): dyn Widgetwidget
func widget(id: int): dyn WidgetThe example for this item is shown with the ones above: tui-screen.salam
FocusedId
func FocusedId(): intfocused_id
func focused_id(): intThe example for this item is shown with the ones above: tui-screen.salam
CanFocus
func CanFocus(id: int): boolcan_focus
func can_focus(id: int): boolThe example for this item is shown with the ones above: tui-screen.salam
Focus
func Focus(id: int): boolfocus
func focus(id: int): boolThe example for this item is shown with the ones above: tui-screen.salam
PushFocus
func PushFocus(id: int): boolpush_focus
func push_focus(id: int): boolThe example for this item is shown with the ones above: tui-screen.salam
PopFocus
func PopFocus(): boolpop_focus
func pop_focus(): boolThe example for this item is shown with the ones above: tui-screen.salam
FocusNext
func FocusNext(): boolfocus_next
func focus_next(): boolThe example for this item is shown with the ones above: tui-screen.salam
FocusPrev
func FocusPrev(): boolfocus_prev
func focus_prev(): boolThe example for this item is shown with the ones above: tui-screen.salam
FocusUp
func FocusUp(): boolfocus_up
func focus_up(): boolThe example for this item is shown with the ones above: tui-screen.salam
FocusDown
func FocusDown(): boolfocus_down
func focus_down(): boolThe example for this item is shown with the ones above: tui-screen.salam
FocusLeft
func FocusLeft(): boolfocus_left
func focus_left(): boolThe example for this item is shown with the ones above: tui-screen.salam
FocusRight
func FocusRight(): boolfocus_right
func focus_right(): boolThe example for this item is shown with the ones above: tui-screen.salam
FitRoot
func FitRoot()fit_root
func fit_root()The example for this item is shown with the ones above: tui-screen.salam
Resize
func Resize(width: int, height: int)resize
func resize(width: int, height: int)The example for this item is shown with the ones above: tui-screen.salam
HitTest
func HitTest(x: int, y: int): inthit_test
func hit_test(x: int, y: int): intThe example for this item is shown with the ones above: tui-screen.salam
Dispatch
func Dispatch(event: Event): booldispatch
func dispatch(event: Event): boolThe example for this item is shown with the ones above: tui-screen.salam
SetKeymap
func SetKeymap(km: Keymap)set_keymap
func set_keymap(km: Keymap)The example for this item is shown with the ones above: tui-screen.salam
Invalidate
func Invalidate(id: int)invalidate
func invalidate(id: int)The example for this item is shown with the ones above: tui-screen.salam
InvalidateSubtree
func InvalidateSubtree(id: int)invalidate_subtree
func invalidate_subtree(id: int)The example for this item is shown with the ones above: tui-screen.salam
Draw
func Draw()draw
func draw()dirty flag, more than RegionLimit regions, or regions covering more than half the screen repaints every widget. Otherwise only the pending regions are cleared and repainted. Does nothing when neither is set. Paint order is ascending (stamp, id). Each widget gets a buffer copy clipped to its visible bounds, with focused set if it holds the focus.The example for this item is shown with the ones above: tui-screen.salam
Present
func Present()present
func present()dirty and the pending regions.The example for this item is shown with the ones above: tui-screen.salam
Post
func Post(event: Event): boolpost
func post(event: Event): boolThe example for this item is shown with the ones above: tui-screen.salam
NextEvent
func NextEvent(event &: Event): boolnext_event
func next_event(event &: Event): boolThe example for this item is shown with the ones above: tui-screen.salam
SetGlyphs
func SetGlyphs(policy: GlyphPolicy)set_glyphs
func set_glyphs(policy: GlyphPolicy)import term
import tui
mut seconds := 0
struct Clock:
pub use tui.BaseWidget
pub func render(buf: tui.Buffer, bounds: tui.Rect):
buf.put_str(bounds.x + 1, bounds.y, "running for " + seconds + "s, q quits", tui.StyleDefault)
end
pub func handle_event(event: tui.Event): bool:
if event.is_char("q"):
ret false
end
ret event.kind == tui.MouseEvent
end
end
func main:
if not term.IsTerminal():
printerrln "needs a terminal"
ret 1
end
mut screen := tui.NewTerminalScreen(Clock {})
screen.tick_ms = 1000
screen.mouse = true
screen.mouse_motion = false
screen.set_glyphs(tui.GlyphPolicy.Unicode)
screen.start()
defer screen.Close()
screen.run_with_tick((): seconds++ end)
println "polled every", tui.PollMs, "ms, queue of", tui.EventCapacity, "running:", screen.running
ret 0
endStart
func Start()start
func start()mouse_motion is set, click reporting otherwise if mouse is set, a buffer sized to the terminal, and the input thread. Call defer screen.close() right after it.The example for this item is shown with the ones above: tui-terminal.salam
Stop
func Stop()stop
func stop()The example for this item is shown with the ones above: tui-screen.salam
Close
func Close()close
func close()defer.The example for this item is shown with the ones above: tui-screen.salam
Run
func Run()run
func run()The example for this item is shown with the ones above: tui-terminal.salam
RunWithTick
func RunWithTick(on_tick: func())run_with_tick
func run_with_tick(on_tick: func())The example for this item is shown with the ones above: tui-terminal.salam
Point
struct Point| Name | Type | Description |
|---|---|---|
x | int = 0 | Column, counted from 0 at the left edge. |
y | int = 0 | Row, counted from 0 at the top edge. |
import tui
func main:
r := tui.Rect { x = 2, y = 1, width = 10, height = 4 }
println "right", r.right(), "bottom", r.bottom(), "empty", r.IsEmpty()
println "contains 5,2:", r.Contains(tui.Point { x = 5, y = 2 })
other := tui.Rect { x = 8, y = 3, width = 6, height = 6 }
println "intersects:", r.intersects(other)
both := r.intersection(other)
println "overlap", both.x, both.y, both.width, both.height
mid := r.center()
println "center", mid.x, mid.y
mut boxes := Vector {} as Vector<tui.Rect>
boxes.Push(tui.Rect { x = 0, y = 10, width = 4, height = 1 })
boxes.Push(tui.Rect { x = 20, y = 1, width = 4, height = 1 })
println "right:", tui.Nearest(r, boxes, tui.DirRight), "down:", tui.Nearest(r, boxes, tui.DirDown)
println "up:", tui.Nearest(r, boxes, tui.DirUp), "left:", tui.Nearest(r, boxes, tui.DirLeft)
endRect
struct Rect| Name | Type | Description |
|---|---|---|
x | int = 0 | Column of the left edge. |
y | int = 0 | Row of the top edge. |
width | int = 0 | Number of columns. Zero or negative means the rect is empty. |
height | int = 0 | Number of rows. Zero or negative means the rect is empty. |
The example for this item is shown with the ones above: tui-geometry.salam
IsEmpty
func IsEmpty(): boolis_empty
func is_empty(): boolThe example for this item is shown with the ones above: tui-geometry.salam
Right
func Right(): intright
func right(): intThe example for this item is shown with the ones above: tui-geometry.salam
Bottom
func Bottom(): intbottom
func bottom(): intThe example for this item is shown with the ones above: tui-geometry.salam
Contains
func Contains(p: Point): boolcontains
func contains(p: Point): boolThe example for this item is shown with the ones above: tui-geometry.salam
Intersects
func Intersects(other: Rect): boolintersects
func intersects(other: Rect): boolThe example for this item is shown with the ones above: tui-geometry.salam
Intersection
func Intersection(other: Rect): Rectintersection
func intersection(other: Rect): RectThe example for this item is shown with the ones above: tui-geometry.salam
Center
func Center(): Pointcenter
func center(): PointThe example for this item is shown with the ones above: tui-geometry.salam
Progress
struct Progress| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
fill_style | Style | Style of the filled cells. StyleDefault uses the terminal's defaults. |
empty_style | Style | Style of the empty cells. |
text_style | Style | Style of the five-cell percent tail. |
The example for this item is shown with the ones above: tui-text.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-text.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-text.salam
Progress
func Progress(): intprogress
func progress(): intThe example for this item is shown with the ones above: tui-text.salam
SetProgress
func SetProgress(percent: int)set_progress
func set_progress(percent: int)The example for this item is shown with the ones above: tui-text.salam
SetMax
func SetMax(limit: int)set_max
func set_max(limit: int)The example for this item is shown with the ones above: tui-text.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-text.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolFocusable
func Focusable(): boolfocusable
func focusable(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasurePanel
struct Panelborder on each side and passes that rect to the child's set_bounds. A Panel with border None draws nothing and insets nothing, so it behaves like its child. Add the child with add; it must also be added to the Screen's tree to be painted, like any other child. Panel reports the child's natural size plus BorderInset of border on each side, so the ring it draws is part of that size. It is not focusable unless takes_focus is set.| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
border | BorderStyle | The ring drawn around the child. None draws nothing and insets nothing. |
border_style | Style | The frame's style. Only its foreground is used, for every ring cell and the title; attributes are ignored. |
title | str = "" | Text on the top edge. Empty or whitespace-only means no title. |
takes_focus | bool = false | When true the Panel is focusable: it takes focus and passes every event to its child. The child is not focusable and gets no focus of its own. False by default, so a Panel is never a focus stop. |
focus_style | Style | The frame's style while the Panel has focus. StyleDefault, the default, means no focus style: the frame keeps border_style. Only the foreground is used, as for border_style. |
slot | Vector | The child, held as the one layout slot. Empty until add is called. |
import term
import tui
mut closed := 0
func show(s: tui.Screen):
s.draw()
repeat s.buffer.height in y:
println "|" + s.buffer.row_text(y) + "|"
end
end
func main:
mut s := tui.NewScreen(tui.BaseWidget {}, 24, 6)
defer s.Close()
area := tui.Rect { width = 24, height = 6 }
body := tui.NewText("inside the frame") as dyn tui.Widget
mut frame := tui.Panel { border = tui.BorderStyle.Single, title = "Panel", takes_focus = true, focus_style = tui.Style { bold = true } }
frame.border_style = tui.StyleDefault
frame.Add(body)
pid := s.Add(tui.RootId, frame as dyn tui.Widget)
_ := s.Add(pid, body)
s.widget(pid).SetBounds(area)
println "panel focusable:", s.widget(pid).Focusable(), "child slot:", frame.slot.Len()
mut dialog := tui.NewModal()
dialog.set_title("Quit?")
dialog.set_size(tui.ModalSize.Fixed(14, 3))
dialog.set_closable(true)
dialog.border_style = tui.StyleDefault
dialog.on_close = (): closed++ end
answer := tui.NewText("y / n") as dyn tui.Widget
dialog.Add(answer)
dialog.SetBounds(area)
mid := s.Add(tui.RootId, dialog as dyn tui.Widget)
_ := s.Add(mid, answer)
dialog.open()
_ := s.push_focus(mid)
s.raise_subtree(mid)
show(s)
_ := s.dispatch(tui.Event { kind = tui.KeyEvent, key = term.Key { code = term.KeyEscape } })
s.unraise_subtree(mid)
_ := s.pop_focus()
println "closed:", closed, "focus back on panel:", s.focused_id() == pid
show(s)
_ := dialog.takes_focus
_ := dialog.state
dialog.Close()
_ := tui.ModalSize.Percent(50, 50)
_ := tui.Weight.Heavy
endAdd
func Add(child: dyn Widget)add
func add(child: dyn Widget)The example for this item is shown with the ones above: tui-panels.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-panels.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): Rectborder on each side. Each axis is measured at max_width minus that inset on both sides, floored at 1; a non-positive max_width is forwarded unchanged as unbounded. A child that cannot measure falls back to its preferred_size. A negative component means no preference on that axis and stays -1. An empty Panel reports no preference.The example for this item is shown with the ones above: tui-panels.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-panels.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-panels.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-panels.salam
Hits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectMeasure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureModalSize
enum ModalSize:
Percent(w: int, h: int)
Fixed(w: int, h: int)
end| Name | Type | Description |
|---|---|---|
Percent | (w: int, h: int) | A share of the bounds, in percent (0-100) on each axis. The product is floored, then clamped to at least 20 by 5 and at most the bounds. When the bounds are smaller than 20 by 5 the dialog is the bounds. |
Fixed | (w: int, h: int) | Exactly w by h cells, clamped to the bounds and nothing more: no 20 by 5 floor applies to an explicit size. |
The example for this item is shown with the ones above: tui-panels.salam
Modal
struct Modal| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
border_style | Style | The ring's colour. Only its foreground is used; attributes are ignored, as for Panel. |
takes_focus | bool = true | When true the Modal is a focus stop while open. Its one focus stop keeps Tab inside its layer. |
on_close | func() | Called after Esc closes the Modal. close does not call it. |
state | Vector<_ModalState> | The shared state holder, one element. NewModal builds it; every copy shares it. Read and write the Modal through its methods. |
The example for this item is shown with the ones above: tui-panels.salam
Add
func Add(child: dyn Widget)add
func add(child: dyn Widget)The example for this item is shown with the ones above: tui-panels.salam
Open
func Open()open
func open()The example for this item is shown with the ones above: tui-panels.salam
Close
func Close()close
func close()The example for this item is shown with the ones above: tui-panels.salam
SetTitle
func SetTitle(t: str)set_title
func set_title(t: str)The example for this item is shown with the ones above: tui-panels.salam
SetSize
func SetSize(sz: ModalSize)set_size
func set_size(sz: ModalSize)The example for this item is shown with the ones above: tui-panels.salam
SetClosable
func SetClosable(b: bool)set_closable
func set_closable(b: bool)The example for this item is shown with the ones above: tui-panels.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-panels.salam
Bounds
func Bounds(): Rectbounds
func bounds(): RectThe example for this item is shown with the ones above: tui-panels.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-panels.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-panels.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-panels.salam
Visible
func Visible(): boolvisible
func visible(): boolThe example for this item is shown with the ones above: tui-panels.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-panels.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-panels.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureList
struct List| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
style | Style | The style of every unselected row. StyleDefault draws with the terminal's defaults. |
selected_style | Style | The style of the selected row while focused; it replaces style on that row. Defaults to reverse video. |
selected_unfocused_style | Style | The style of the selected row while unfocused; it replaces style on that row. Defaults to a bright-black background. |
on_select | func(int) | Called with the selected index when enter is pressed on a non-empty list. The default does nothing. |
import str
import term
import tui
mut submitted := ""
mut picked := -1
mut pressed := 0
mut toggled := false
func key(seq: str): tui.Event:
ret tui.Event { kind = tui.KeyEvent, key = term.Decode(seq) }
end
func main:
mut buf := tui.NewBuffer(24, 5)
mut pass := tui.NewInput()
pass.masked = true
pass.mask_glyph = "*"
pass.max_length = 8
pass.filter = (candidate: str): ret not str.Contains(candidate, " ") end
pass.on_submit = (v: str):
submitted = v
ret true
end
pass.set_placeholder("password")
pass.set_value("")
pass.style = tui.StyleDefault
pass.cursor_style = tui.Style { reverse = true }
pass.placeholder_style = tui.Style { dim = true }
pass.SetBounds(tui.Rect { x = 0, y = 0, width = 12, height = 1 })
each c in ["s", " ", "e", "c"]:
_ := pass.HandleEvent(key(c))
end
_ := pass.HandleEvent(key("\r"))
println "input focusable:", pass.Focusable(), "value:", pass.Value(), "submitted:", submitted
mut ok := tui.NewButton("OK")
ok.on_click = (): pressed++ end
ok.focus_style = tui.Style { bold = true }
ok.style = tui.StyleDefault
ok.disabled = false
ok.SetBounds(tui.Rect { x = 0, y = 1, width = 8, height = 1 })
_ := ok.HandleEvent(key(" "))
println "button:", ok.label, ok.Focusable(), ok.Hits(), "pressed", pressed, "width", ok.PreferredSize(20).width
mut wrap := tui.NewCheckbox("Wrap lines", false)
wrap.on_toggle = (v: bool): toggled = v end
wrap.box_style = tui.StyleDefault
wrap.label_style = tui.StyleDefault
wrap.disabled = false
wrap.SetBounds(tui.Rect { x = 0, y = 2, width = 14, height = 1 })
_ := wrap.HandleEvent(key(" "))
wrap.set_checked(wrap.checked())
println "checkbox:", wrap.label, wrap.checked(), toggled, wrap.Focusable(), wrap.Hits(), wrap.PreferredSize(20).width
mut items := Vector {} as Vector<str>
items.Push("red")
items.Push("green")
mut colors := tui.NewList(items)
colors.on_select = (i: int): picked = i end
colors.style = tui.StyleDefault
colors.selected_style = tui.Style { reverse = true }
colors.selected_unfocused_style = tui.Style { underline = true }
colors.SetBounds(tui.Rect { x = 14, y = 0, width = 8, height = 2 })
_ := colors.HandleEvent(key("j"))
_ := colors.HandleEvent(key("\r"))
println "list:", colors.selected_index(), colors.selected_item(), picked, colors.Focusable()
pass.Render(buf, pass.Bounds())
ok.Render(buf, ok.Bounds())
wrap.Render(buf, wrap.Bounds())
colors.Render(buf, colors.Bounds())
repeat buf.height in y:
println "|" + buf.row_text(y) + "|"
end
buf.Free()
endFocusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-controls.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-controls.salam
SelectedIndex
func SelectedIndex(): intselected_index
func selected_index(): intThe example for this item is shown with the ones above: tui-controls.salam
SelectedItem
func SelectedItem(): strselected_item
func selected_item(): strThe example for this item is shown with the ones above: tui-controls.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-controls.salam
Hits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectMeasure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureAxis
enum Axis| Name | Type | Description |
|---|---|---|
X | | The horizontal axis. |
Y | | The vertical axis. |
import tui
struct Box:
pub use tui.BaseWidget
pub mark: str = "#"
pub func render(buf: tui.Buffer, bounds: tui.Rect):
buf.fill(bounds, this.mark, tui.StyleDefault)
end
end
func box(mark: str): dyn tui.Widget:
ret Box { mark = mark } as dyn tui.Widget
end
func size(kind: tui.SizeKind, cells: int, weight: int): tui.LayoutSize:
ret tui.LayoutSize { kind = kind, cells = cells, weight = weight }
end
func main:
a := box("a")
b := box("b")
c := box("c")
d := box("d")
e := box("e")
f := box("f")
mut top := tui.NewRow()
top.set_gap(1)
ia := top.Add(a)
ib := top.Add(b)
top.set_width(ia, size(tui.SizeKind.Fixed, 4, 0))
top.set_width(ib, size(tui.SizeKind.Fill, 0, 1))
toprow := top as dyn tui.Widget
mut grid := tui.NewGrid(1, 2)
ic := grid.Add(c)
id := grid.Add(d)
grid.set_cell(ic, 0, 0)
grid.set_cell(id, 0, 1)
gridw := grid as dyn tui.Widget
mut stack := tui.NewStack()
ie := stack.Add(e)
if2 := stack.Add(f)
stack.set_margin(if2, tui.NewInsets(0, 2, 0, 2))
stack.set_align(ie, tui.CrossAlign.Stretch, tui.CrossAlign.Stretch)
stackw := stack as dyn tui.Widget
mut col := tui.NewColumn()
col.set_padding(tui.UniformInsets(1))
i1 := col.Add(toprow)
i2 := col.Add(gridw)
i3 := col.Add(stackw)
col.set_height(i1, size(tui.SizeKind.Fixed, 1, 0))
col.set_height(i2, size(tui.SizeKind.Fixed, 2, 0))
col.set_height(i3, size(tui.SizeKind.Fill, 0, 1))
mut s := tui.NewScreen(col as dyn tui.Widget, 20, 8)
defer s.Close()
each w in [toprow, a, b, gridw, c, d, stackw, e, f]:
_ := s.Add(tui.RootId, w)
end
s.draw()
repeat s.buffer.height in y:
println "|" + s.buffer.row_text(y) + "|"
end
mut sizes := Vector {} as Vector<tui.LayoutSize>
sizes.Push(size(tui.SizeKind.Fixed, 3, 0))
sizes.Push(size(tui.SizeKind.Fill, 0, 2))
sizes.Push(size(tui.SizeKind.Pref, 0, 1))
split := tui.SplitMain(12, sizes, 0)
println "split 12:", split.Get(0), split.Get(1), split.Get(2)
tracks := tui.TrackSizes(10, 3)
println "tracks 10/3:", tracks.Get(0), tracks.Get(1), tracks.Get(2)
inner := tui.InsetRect(tui.Rect { width = 10, height = 4 }, tui.NewInsets(1, 2, 1, 2))
println "inset:", inner.x, inner.y, inner.width, inner.height
fit := tui.FitRect(tui.Rect { x = 8, y = 0, width = 6, height = 2 }, tui.Rect { width = 10, height = 4 })
println "fit:", fit.x, fit.width
println "place:", tui.PlaceAxisSize(10, tui.CrossAlign.Center, 4), tui.PlaceAxisOffset(10, tui.CrossAlign.End, 4)
m := tui.Measure { ok = true, extent = 3 }
println "measure:", m.ok, m.extent, tui.Axis.X != tui.Axis.Y
slot := col.items.Get(1)
println "slot span:", slot.row_span, slot.col_span, "rows/cols:", grid.rows, grid.cols, "gap:", top.gap, "pad:", col.pad.top
endMeasure
struct Measure| Name | Type | Description |
|---|---|---|
ok | bool = false | True when extent is a real measurement. False means cannot measure. |
extent | int = 0 | The measured length in cells when ok is true. The engine clamps a negative value to 0. Ignored when ok is false. |
The example for this item is shown with the ones above: tui-layout.salam
CrossAlign
enum CrossAlign| Name | Type | Description |
|---|---|---|
Start | | The child starts at the box's leading edge; free space follows it. |
Center | | The child is centred; when the free space is odd the extra cell goes right (Row) or bottom (Column). |
End | | The child ends at the box's trailing edge; free space precedes it. |
Stretch | | The child fills the whole axis (the default); any size is ignored. |
The example for this item is shown with the ones above: tui-layout.salam
SizeKind
enum SizeKind| Name | Type | Description |
|---|---|---|
Fill | | A weighted share of whatever is left after the Fixed children (R5). On a cross axis, Fill behaves like Pref and the weight is ignored (R8). |
Fixed | | Exactly cells long. A negative value clamps to 0. |
Pref | | The child's preferred size. On a main axis a measured Pref is reserved like Fixed; one that cannot measure is Fill weight 1. On a cross axis it is the measured natural size (R8). |
The example for this item is shown with the ones above: tui-layout.salam
LayoutSize
struct LayoutSizecells is read only when kind is Fixed. weight is read only when a main-axis Fill or Pref joins the share.| Name | Type | Description |
|---|---|---|
kind | SizeKind | How the size is resolved. Defaults to Fill. |
cells | int = 0 | The requested length, read only when kind is Fixed. Negative values clamp to 0 when used. |
weight | int = 1 | Share of the main-axis leftover for a Fill. Defaults to 1. A negative value clamps to 0 when used. Fixed ignores it, and so do Grid, Stack, and cross-axis alignment. SplitMain also reads it for a Pref that is still in the vector. Arrange resolves a measured Pref to Fixed first, and a Pref that cannot measure to Fill weight 1, so this value is not that slot's weight. |
The example for this item is shown with the ones above: tui-layout.salam
Insets
struct Insets| Name | Type | Description |
|---|---|---|
top | int = 0 | Cells added above the content (padding) or reserved above the child (margin). |
right | int = 0 | Cells added to the right of the content or reserved right of the child. |
bottom | int = 0 | Cells added below the content or reserved below the child. |
left | int = 0 | Cells added to the left of the content or reserved left of the child. |
The example for this item is shown with the ones above: tui-layout.salam
LayoutSlot
struct LayoutSlot| Name | Type | Description |
|---|---|---|
child | dyn Widget | The child widget. It must be registered in the Screen's tree as well, or the Screen never paints it. |
margin | Insets | Space reserved inside the slot, before alignment (R4). Zero by default. |
width | LayoutSize | The requested size on the x axis. Fill by default. |
height | LayoutSize | The requested size on the y axis. Fill by default. |
align_x | CrossAlign | Cross-axis alignment on x. Stretch by default. |
align_y | CrossAlign | Cross-axis alignment on y. Stretch by default. |
row | int = 0 | The Grid row this child occupies. 0 by default. |
col | int = 0 | The Grid column this child occupies. 0 by default. |
row_span | int = 1 | How many Grid rows this child covers from row. 1 by default. A non-positive value means 1, and Grid clamps it to the rows remaining. |
col_span | int = 1 | How many Grid columns this child covers from col. 1 by default. A non-positive value means 1, and Grid clamps it to the columns remaining. |
The example for this item is shown with the ones above: tui-layout.salam
Row
struct Row| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
pad | Insets | The inset between the container's bounds and its content box (R3). |
gap | int = 0 | Cells between adjacent slots on the main axis (R5). Zero by default. A negative value clamps to 0. |
items | Vector | The children in placement order, one slot each. |
The example for this item is shown with the ones above: tui-layout.salam
Add
func Add(child: dyn Widget): intadd
func add(child: dyn Widget): intThe example for this item is shown with the ones above: tui-layout.salam
SetMargin
func SetMargin(i: int, margin: Insets)set_margin
func set_margin(i: int, margin: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetWidth
func SetWidth(i: int, size: LayoutSize)set_width
func set_width(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetHeight
func SetHeight(i: int, size: LayoutSize)set_height
func set_height(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetAlign
func SetAlign(i: int, align_x: CrossAlign, align_y: CrossAlign)set_align
func set_align(i: int, align_x: CrossAlign, align_y: CrossAlign)The example for this item is shown with the ones above: tui-layout.salam
SetPadding
func SetPadding(pad: Insets)set_padding
func set_padding(pad: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetGap
func SetGap(gap: int)set_gap
func set_gap(gap: int)The example for this item is shown with the ones above: tui-layout.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureThe example for this item is shown with the ones above: tui-layout.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-layout.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolFocusable
func Focusable(): boolfocusable
func focusable(): boolHits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectPreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectColumn
struct Column| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
pad | Insets | The inset between the container's bounds and its content box (R3). |
gap | int = 0 | Cells between adjacent slots on the main axis (R5). Zero by default. A negative value clamps to 0. |
items | Vector | The children in placement order, one slot each. |
The example for this item is shown with the ones above: tui-layout.salam
Add
func Add(child: dyn Widget): intadd
func add(child: dyn Widget): intThe example for this item is shown with the ones above: tui-layout.salam
SetMargin
func SetMargin(i: int, margin: Insets)set_margin
func set_margin(i: int, margin: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetWidth
func SetWidth(i: int, size: LayoutSize)set_width
func set_width(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetHeight
func SetHeight(i: int, size: LayoutSize)set_height
func set_height(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetAlign
func SetAlign(i: int, align_x: CrossAlign, align_y: CrossAlign)set_align
func set_align(i: int, align_x: CrossAlign, align_y: CrossAlign)The example for this item is shown with the ones above: tui-layout.salam
SetPadding
func SetPadding(pad: Insets)set_padding
func set_padding(pad: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetGap
func SetGap(gap: int)set_gap
func set_gap(gap: int)The example for this item is shown with the ones above: tui-layout.salam
Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureThe example for this item is shown with the ones above: tui-layout.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-layout.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolFocusable
func Focusable(): boolfocusable
func focusable(): boolHits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectPreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectGrid
struct Grid| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
pad | Insets | The inset between the container's bounds and its content box (R3). |
items | Vector | The children in placement order, one slot each. |
rows | int = 0 | The number of grid rows. Set by NewGrid. |
cols | int = 0 | The number of grid columns. Set by NewGrid. |
The example for this item is shown with the ones above: tui-layout.salam
Add
func Add(child: dyn Widget): intadd
func add(child: dyn Widget): intThe example for this item is shown with the ones above: tui-layout.salam
SetMargin
func SetMargin(i: int, margin: Insets)set_margin
func set_margin(i: int, margin: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetWidth
func SetWidth(i: int, size: LayoutSize)set_width
func set_width(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetHeight
func SetHeight(i: int, size: LayoutSize)set_height
func set_height(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetAlign
func SetAlign(i: int, align_x: CrossAlign, align_y: CrossAlign)set_align
func set_align(i: int, align_x: CrossAlign, align_y: CrossAlign)The example for this item is shown with the ones above: tui-layout.salam
SetCell
func SetCell(i: int, row: int, col: int)set_cell
func set_cell(i: int, row: int, col: int)The example for this item is shown with the ones above: tui-layout.salam
SetSpan
func SetSpan(i: int, rows: int, cols: int)set_span
func set_span(i: int, rows: int, cols: int)The example for this item is shown with the ones above: tui-layout.salam
SetPadding
func SetPadding(pad: Insets)set_padding
func set_padding(pad: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-layout.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolFocusable
func Focusable(): boolfocusable
func focusable(): boolHits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectPreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectMeasure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureStack
struct Stack| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
pad | Insets | The inset between the container's bounds and its content box (R3). |
items | Vector | The children in placement order, one slot each. |
The example for this item is shown with the ones above: tui-layout.salam
Add
func Add(child: dyn Widget): intadd
func add(child: dyn Widget): intThe example for this item is shown with the ones above: tui-layout.salam
SetMargin
func SetMargin(i: int, margin: Insets)set_margin
func set_margin(i: int, margin: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetWidth
func SetWidth(i: int, size: LayoutSize)set_width
func set_width(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetHeight
func SetHeight(i: int, size: LayoutSize)set_height
func set_height(i: int, size: LayoutSize)The example for this item is shown with the ones above: tui-layout.salam
SetAlign
func SetAlign(i: int, align_x: CrossAlign, align_y: CrossAlign)set_align
func set_align(i: int, align_x: CrossAlign, align_y: CrossAlign)The example for this item is shown with the ones above: tui-layout.salam
SetPadding
func SetPadding(pad: Insets)set_padding
func set_padding(pad: Insets)The example for this item is shown with the ones above: tui-layout.salam
SetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)The example for this item is shown with the ones above: tui-layout.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolFocusable
func Focusable(): boolfocusable
func focusable(): boolHits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectPreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectMeasure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureKeymap
struct KeymapKeymap {}), fill it with bind and its variants, feed events to handle, and release it with free. Never copy a Keymap value.| Name | Type | Description |
|---|---|---|
pending_timeout_ms | int = 0 | How long a pending key sequence may wait for its next key, in milliseconds. 0 (the default) never expires a prefix, which is the behavior without a timeout. The timeout is evaluated lazily, on the next KeyEvent only; non-key events never expire a prefix. Set it directly (keymap.pending_timeout_ms = 700); there is no constructor. |
pending_since_ms | int = 0 | The clock reading, in milliseconds, when the current pending sequence started waiting; it is refreshed on every prefix step and is 0 when nothing is pending. handle maintains it; an application sets pending_timeout_ms instead. |
import term
import tui
mut quits := 0
mut tops := 0
func main:
k := tui.Event { kind = tui.KeyEvent, key = term.Decode("q") }
println "is q:", k.is_char("q"), "is Ctrl+C:", k.is_ctrl("c"), "is Enter:", k.is_key(term.KeyEnter)
click := tui.Event { kind = tui.MouseEvent, key = term.Key { raw = "\x1b[<0;5;3M" }, mouse = term.DecodeMouse("\x1b[<0;5;3M") }
p := click.mouse_point()
println "click at", p.x, p.y
resize := tui.Event { kind = tui.ResizeEvent, width = 100, height = 30 }
println "kinds:", tui.NoEvent, tui.KeyEvent, tui.MouseEvent, resize.kind, tui.TickEvent, "size", resize.width, resize.height, "bubbled", resize.bubbled
mut s := tui.NewScreen(tui.BaseWidget {}, 10, 2)
defer s.Close()
mut km := tui.NewKeymap()
km.bind(term.Decode("q"), (_s: tui.Screen*, _e: tui.Event):
quits++
ret true
end)
km.bind_seq("gg", (_s: tui.Screen*, _e: tui.Event):
tops++
ret true
end)
km.bind_for(tui.RootId, term.Decode("x"), (_s: tui.Screen*, _e: tui.Event): ret true end)
km.bind_for_seq(tui.RootId, "dd", (_s: tui.Screen*, _e: tui.Event): ret true end)
km.pending_timeout_ms = 1000
s.set_keymap(km)
_ := s.dispatch(k)
_ := s.dispatch(tui.Event { kind = tui.KeyEvent, key = term.Decode("g") })
println "sequence timeout:", s.keymap.pending_timeout_ms, "ms"
_ := s.keymap.pending_since_ms
_ := s.dispatch(tui.Event { kind = tui.KeyEvent, key = term.Decode("g") })
println "q pressed", quits, "gg pressed", tops
s.keymap.unbind(term.Decode("q"))
s.keymap.unbind_for(tui.RootId, term.Decode("x"))
s.keymap.unbind_for_seq(tui.RootId, "dd")
s.keymap.cancel_pending()
println "after unbind:", s.dispatch(k)
mut arrows := tui.NewDefaultKeymap()
arrows.Free()
mut vim := tui.NewVimKeymap((_s: tui.Screen*, _e: tui.Event): ret false end)
vim.Free()
endBind
func Bind(key: term.Key, action: func(Screen*, Event): bool)bind
func bind(key: term.Key, action: func(Screen*, Event): bool)The example for this item is shown with the ones above: tui-events.salam
BindFor
func BindFor(widget_id: int, key: term.Key, action: func(Screen*, Event): bool)bind_for
func bind_for(widget_id: int, key: term.Key, action: func(Screen*, Event): bool)The example for this item is shown with the ones above: tui-events.salam
BindSeq
func BindSeq(seq: str, action: func(Screen*, Event): bool)bind_seq
func bind_seq(seq: str, action: func(Screen*, Event): bool)The example for this item is shown with the ones above: tui-events.salam
BindForSeq
func BindForSeq(widget_id: int, seq: str, action: func(Screen*, Event): bool)bind_for_seq
func bind_for_seq(widget_id: int, seq: str, action: func(Screen*, Event): bool)The example for this item is shown with the ones above: tui-events.salam
Unbind
func Unbind(key: term.Key)unbind
func unbind(key: term.Key)The example for this item is shown with the ones above: tui-events.salam
UnbindFor
func UnbindFor(widget_id: int, key: term.Key)unbind_for
func unbind_for(widget_id: int, key: term.Key)The example for this item is shown with the ones above: tui-events.salam
UnbindForSeq
func UnbindForSeq(widget_id: int, seq: str)unbind_for_seq
func unbind_for_seq(widget_id: int, seq: str)The example for this item is shown with the ones above: tui-events.salam
Handle
func Handle(screen: Screen*, event: Event): boolhandle
func handle(screen: Screen*, event: Event): boolpending untouched. Returns true when an action ran. Reads resolve the key before any action is invoked; nothing touches the trie after an invocation.The example for this item is shown with the ones above: tui-events.salam
Free
func Free()free
func free()The example for this item is shown with the ones above: tui-events.salam
CancelPending
func CancelPending()cancel_pending
func cancel_pending()The example for this item is shown with the ones above: tui-events.salam
Input
struct Input| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
placeholder | str = "" | Text shown (dim) while the value is empty. "" means no placeholder. |
style | Style | The base style of the value and the blank background. StyleDefault draws with the terminal's defaults. |
cursor_style | Style | The style merged over style for the cursor cell. Defaults to reverse video. |
placeholder_style | Style | The style merged over style for the placeholder. Defaults to a bright-black foreground. |
max_length | int = 0 | Maximum value length in glyphs (codepoints), not display cells. 0 means unlimited. Typing at the limit is consumed and changes nothing; set_value truncates a longer value; deleting is always allowed. |
filter | func(str): bool | Called with the whole candidate value after a proposed insertion (one typed glyph), once the max_length check has passed. Returning false rejects it: the key is consumed and nothing changes. Not called for Backspace, Delete or set_value. The default accepts everything. |
masked | bool = false | When true every value glyph is painted as mask_glyph. Display only: value, caret and window are unchanged. The placeholder is never masked. |
mask_glyph | str = "*" | The glyph painted for each value glyph while masked. Only its first codepoint is used; "" means "*". Under GlyphPolicy.ASCII a non-ASCII mask glyph paints as "*". |
on_submit | func(str): bool | Called synchronously on Enter with the real value, never the mask. Returning true consumes the key; false lets it bubble to the parent. The default returns true. After it returns, the caret and window are clamped to the value, in case it changed the value. |
The example for this item is shown with the ones above: tui-controls.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-controls.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-controls.salam
Value
func Value(): strvalue
func value(): strThe example for this item is shown with the ones above: tui-controls.salam
SetValue
func SetValue(v: str)set_value
func set_value(v: str)The example for this item is shown with the ones above: tui-controls.salam
SetPlaceholder
func SetPlaceholder(p: str)set_placeholder
func set_placeholder(p: str)The example for this item is shown with the ones above: tui-controls.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)style, the window of cells that holds the caret is written (the placeholder, dim, when the value is empty), and (while the buffer reports focus) one reverse-video cell marks the caret column. The window start is kept between frames and moves only when the caret leaves it. A degenerate box draws nothing and never panics.The example for this item is shown with the ones above: tui-controls.salam
Hits
func Hits(): boolhits
func hits(): boolVisible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectMeasure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureEvent
struct Eventkind says which of the payload fields are meaningful.| Name | Type | Description |
|---|---|---|
kind | int = 0 | One of NoEvent, KeyEvent, MouseEvent, ResizeEvent, TickEvent. |
key | term.Key | The key for KeyEvent; the raw sequence (in key.raw) for MouseEvent. |
mouse | term.MouseEvent | The decoded report for MouseEvent. |
width | int = 0 | The new terminal width in columns, for ResizeEvent. |
height | int = 0 | The new terminal height in rows, for ResizeEvent. |
bubbled | bool = false | True once the event has moved from the widget it started at to an ancestor. Containers such as Panel and Modal use it so they do not hand the same event to their child a second time. |
The example for this item is shown with the ones above: tui-events.salam
MousePoint
func MousePoint(): Pointmouse_point
func mouse_point(): Pointmouse.ok is true.The example for this item is shown with the ones above: tui-events.salam
IsKey
func IsKey(code: int): boolis_key
func is_key(code: int): boolThe example for this item is shown with the ones above: tui-events.salam
IsChar
func IsChar(ch: str): boolis_char
func is_char(ch: str): boolThe example for this item is shown with the ones above: tui-events.salam
IsCtrl
func IsCtrl(ch: str): boolis_ctrl
func is_ctrl(ch: str): boolThe example for this item is shown with the ones above: tui-events.salam
Color
enum Color:
Default
Ansi(n: int)
Indexed(n: int)
Rgb(r: int, g: int, b: int)
end| Name | Type | Description |
|---|---|---|
Default | | The terminal's own default foreground or background (SGR 39 / 49), not an entry in any palette. |
Ansi | (n: int) | A 16-color palette index, 0-15: 0-7 the normal colors, 8-15 their bright variants. No validation; the documented range is 0-15. |
Indexed | (n: int) | A 256-color palette index, 0-255 (SGR 38;5;n / 48;5;n). An index in 0-15 stays Indexed on the wire, never rewritten to the ANSI form. |
Rgb | (r: int, g: int, b: int) | A 24-bit truecolor triple, each channel 0-255 (SGR 38;2;r;g;b). |
The example for this item is shown with the ones above: tui-style.salam
ColorTier
enum ColorTier| Name | Type | Description |
|---|---|---|
Off | | No color: every Color lowers to Default. |
Ansi16 | | The 16-color palette only. |
Indexed256 | | The 256-color palette. |
TrueColor | | 24-bit truecolor. |
The example for this item is shown with the ones above: tui-style.salam
Checkbox
struct Checkbox| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
label | str = "" | Text drawn after the mark. "" draws the mark alone. |
box_style | Style | Style of the three mark cells, the "[", the state glyph and the "]". |
label_style | Style | Style of the spacer, the label, and the trailing fill of the row. |
disabled | bool = false | When true the checkbox is not focusable, ignores every event and does not take hits, and draws its row dim. |
on_toggle | func(bool) | Called with the new checked value after every toggle. The default does nothing. |
The example for this item is shown with the ones above: tui-controls.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-controls.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-controls.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-controls.salam
Checked
func Checked(): boolchecked
func checked(): boolThe example for this item is shown with the ones above: tui-controls.salam
SetChecked
func SetChecked(value: bool)set_checked
func set_checked(value: bool)The example for this item is shown with the ones above: tui-controls.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-controls.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-controls.salam
Visible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureButton
struct Button| Name | Type | Description |
|---|---|---|
BaseWidget | BaseWidget | The embedded BaseWidget: bounds and the default answers for every Widget method this type does not override. |
label | str = "" | The text drawn between the brackets. Clipped to the width, never ellipsised. |
style | Style | The base style of every cell in the button. |
focus_style | Style | The style used instead of style while the button is focused and enabled. StyleDefault, the default, means bold on every cell. |
disabled | bool = false | When true the button draws dim, does not take focus or hits, and ignores every event. |
on_click | func() | Called when the button is activated. The default does nothing. |
The example for this item is shown with the ones above: tui-controls.salam
Focusable
func Focusable(): boolfocusable
func focusable(): boolThe example for this item is shown with the ones above: tui-controls.salam
Hits
func Hits(): boolhits
func hits(): boolThe example for this item is shown with the ones above: tui-controls.salam
PreferredSize
func PreferredSize(max_width: int): Rectpreferred_size
func preferred_size(max_width: int): RectThe example for this item is shown with the ones above: tui-controls.salam
HandleEvent
func HandleEvent(event: Event): boolhandle_event
func handle_event(event: Event): boolThe example for this item is shown with the ones above: tui-controls.salam
Render
func Render(buf: Buffer, bounds: Rect)render
func render(buf: Buffer, bounds: Rect)The example for this item is shown with the ones above: tui-controls.salam
Visible
func Visible(): boolvisible
func visible(): boolBounds
func Bounds(): Rectbounds
func bounds(): RectSetBounds
func SetBounds(bounds: Rect)set_bounds
func set_bounds(bounds: Rect)Measure
func Measure(axis: Axis, max_width: int): Measuremeasure
func measure(axis: Axis, max_width: int): MeasureGlyphPolicy
enum GlyphPolicy| Name | Type | Description |
|---|---|---|
Unicode | | Box-drawing glyphs. |
ASCII | | Plain ASCII glyphs only. |
import tui
func main:
mut buf := tui.NewBuffer(16, 5)
buf.glyphs = tui.GlyphPolicy.Unicode
area := buf.area()
println "area", area.width, "x", area.height
tui.DrawBorder(buf, area, tui.BorderStyle.Double, tui.StyleDefault, "box")
buf.put_str(2, 1, "hi 中文", tui.Style { bold = true })
buf.fill(tui.Rect { x = 2, y = 2, width = 5, height = 1 }, ".", tui.StyleDefault)
buf.Set(9, 2, tui.Cell { ch = "*", style = tui.StyleDefault })
tui.DrawBorderGlyphs(buf, tui.Rect { x = 10, y = 2, width = 5, height = 3 }, tui.GlyphsRounded(), tui.StyleDefault, "")
repeat buf.height in y:
println buf.row_text(y)
end
println "cell 2,1:", buf.Get(2, 1).ch, "wide head:", buf.Get(5, 1).ch
println "frame bytes:", buf.ansi().Len() > 0, "changes:", buf.diff().Len() > 0
buf.commit()
println "after commit:", buf.diff().Len()
buf.Clear()
buf.resize(4, 1)
println "resized", buf.width, buf.height, "full repaint:", buf.force_full
println "glyph sets:", tui.GlyphsSingle().h, tui.GlyphsDouble().h, tui.GlyphsHeavy().h, tui.GlyphsASCII().h, tui.GlyphsMixed().tl
println "ascii fallback:", tui.GlyphsFor(tui.BorderStyle.Rounded, tui.GlyphPolicy.ASCII).tl
println "policy from env:", tui.DetectGlyphPolicy() == tui.DetectGlyphPolicy()
_ := buf.focused
_ := buf.clip
buf.Free()
endCell
struct Cell| Name | Type | Description |
|---|---|---|
ch | str = " " | A one-cell-wide glyph, or "" for the continuation of the wide glyph in the cell to its left. A blank cell holds " ". put_str writes wide glyphs; set blanks any ch that is not one cell wide. |
style | Style | The resolved style this cell is drawn with. StyleDefault asks for the terminal's default style. The buffer, not the widget, turns it into bytes. |
The example for this item is shown with the ones above: tui-buffer.salam
Buffer
struct Bufferclip (or outside the grid) are silently ignored, so drawing is infallible.| Name | Type | Description |
|---|---|---|
width | int = 0 | Number of columns. |
height | int = 0 | Number of rows. |
cells | Vector | Row-major cell storage: the cell at (x, y) is cells[y * width + x]. Prefer get and set, which bounds-check and clip. Write through set / fill / put_str / clear. Direct writes to the backing vector are outside the contract and are not tracked. |
front | Vector | The front grid: what the terminal currently shows. Same length and layout as cells. Widgets never write it; only commit() advances it. |
clip | Rect | Writes outside this rect are dropped. NewBuffer sets it to the whole grid; the screen narrows it to each widget's bounds while that widget renders. |
focused | bool = false | Render context: true while the screen renders the focused widget, so the widget can draw a focus indicator. Always false outside a screen-driven render. |
tier | ColorTier | Render context: the colour capability of the terminal this buffer is drawn for. The buffer lowers every cell's Style to this tier when it emits a frame; widgets and DrawBorder only store styles. Screen.start() sets it to DetectTier() once; a buffer built by hand keeps TrueColor, so headless renders stay true colour whatever the environment says. resize() preserves it. |
glyphs | GlyphPolicy | Render context: the glyph policy structural glyphs follow. DrawBorder reads it. Screen seeds it on every frame copy; a buffer built by hand keeps Unicode. resize() preserves it. |
force_full | bool = true | True when the next diff() must serialize the whole frame instead of only the changed cells. NewBuffer and resize set it; commit() clears it once the front grid has caught up with the back grid. |
The example for this item is shown with the ones above: tui-buffer.salam
Area
func Area(): Rectarea
func area(): RectThe example for this item is shown with the ones above: tui-buffer.salam
Set
func Set(x: int, y: int, cell: Cell)set
func set(x: int, y: int, cell: Cell)clip. A non-empty ch that is not one cell wide becomes blank; "" is accepted only after a wide glyph.The example for this item is shown with the ones above: tui-buffer.salam
Get
func Get(x: int, y: int): Cellget
func get(x: int, y: int): Cellclip.The example for this item is shown with the ones above: tui-buffer.salam
PutStr
func PutStr(x: int, y: int, text: str, style: Style)put_str
func put_str(x: int, y: int, text: str, style: Style)The example for this item is shown with the ones above: tui-buffer.salam
Fill
func Fill(r: Rect, ch: str, style: Style)fill
func fill(r: Rect, ch: str, style: Style)ch becomes blank through set.The example for this item is shown with the ones above: tui-buffer.salam
Clear
func Clear()clear
func clear()clip. The next diff() and commit() treat every cell as changed.The example for this item is shown with the ones above: tui-buffer.salam
RowText
func RowText(y: int): strrow_text
func row_text(y: int): strThe example for this item is shown with the ones above: tui-buffer.salam
Resize
func Resize(width: int, height: int)resize
func resize(width: int, height: int)The example for this item is shown with the ones above: tui-buffer.salam
Ansi
func Ansi(): stransi
func ansi(): strThe example for this item is shown with the ones above: tui-buffer.salam
Diff
func Diff(): strdiff
func diff(): strforce_full, otherwise only the cells that differ from the front grid, row by row. Each maximal run of changed cells is prefixed by one cursor move; styles switch only where they change and are carried across runs; a non-empty diff ends by turning the last style off. Returns "" when the grids match.The example for this item is shown with the ones above: tui-buffer.salam
Commit
func Commit()commit
func commit()force_full is set or clear() touched the whole grid, and only marked cells otherwise, then clears the marks and force_full, so a later diff() reports only subsequent changes.The example for this item is shown with the ones above: tui-buffer.salam
Flush
func Flush()flush
func flush()The example for this item is shown with the ones above: tui-buffer.salam
Free
func Free()free
func free()The example for this item is shown with the ones above: tui-buffer.salam
Weight
enum Weight| Name | Type | Description |
|---|---|---|
Light | | Thin single line. |
Heavy | | Thick single line. |
Double | | Double line. |
The example for this item is shown with the ones above: tui-buffer.salam
BorderGlyphs
struct BorderGlyphs| Name | Type | Description |
|---|---|---|
h | str = "" | Horizontal run, drawn on the top and bottom edges. |
v | str = "" | Vertical run, drawn on the left and right edges. |
tl | str = "" | Top-left corner. |
tr | str = "" | Top-right corner. |
bl | str = "" | Bottom-left corner. |
br | str = "" | Bottom-right corner. |
t_down | str = "" | T junction with its stem pointing down. |
t_up | str = "" | T junction with its stem pointing up. |
t_right | str = "" | T junction with its stem pointing right. |
t_left | str = "" | T junction with its stem pointing left. |
cross | str = "" | Four-way junction. |
hw | Weight | Weight of the horizontal run. |
vw | Weight | Weight of the vertical run. |
The example for this item is shown with the ones above: tui-buffer.salam
Constants and variables
NoWidget
const NoWidget: i32 = -1The example for this item is shown with the ones above: tui-widget.salam
RootId
const RootId: i32 = 0The example for this item is shown with the ones above: tui-widget.salam
StyleDefault
const StyleDefault: tui_StyleThe example for this item is shown with the ones above: tui-style.salam
PollMs
const PollMs: i32 = 20The example for this item is shown with the ones above: tui-terminal.salam
EventCapacity
const EventCapacity: i32 = 256The example for this item is shown with the ones above: tui-terminal.salam
Directions Dir…
Nearest and the spatial focus moves (focus_up, focus_down, focus_left, focus_right) take.| Name | Value |
|---|---|
DirUp | 0 |
DirDown | 1 |
DirLeft | 2 |
DirRight | 3 |
The example for this item is shown with the ones above: tui-geometry.salam
NoEvent
const NoEvent: i32 = 0The example for this item is shown with the ones above: tui-events.salam
KeyEvent
const KeyEvent: i32 = 1key field holds the decoded key.The example for this item is shown with the ones above: tui-events.salam
MouseEvent
const MouseEvent: i32 = 2mouse field holds the decoded report and key keeps the raw escape sequence it came from.The example for this item is shown with the ones above: tui-events.salam
ResizeEvent
const ResizeEvent: i32 = 3width and height hold the new size in cells.The example for this item is shown with the ones above: tui-events.salam
TickEvent
const TickEvent: i32 = 4The example for this item is shown with the ones above: tui-events.salam
Black
const Black: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Red
const Red: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Green
const Green: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Yellow
const Yellow: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Blue
const Blue: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Magenta
const Magenta: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Cyan
const Cyan: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
White
const White: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightBlack
const BrightBlack: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightRed
const BrightRed: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightGreen
const BrightGreen: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightYellow
const BrightYellow: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightBlue
const BrightBlue: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightMagenta
const BrightMagenta: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightCyan
const BrightCyan: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
BrightWhite
const BrightWhite: Variant<tui___Color__Default, tui___Color__Ansi, tui___Color__Indexed, tui___Color__Rgb>The example for this item is shown with the ones above: tui-style.salam
Functions
NewTree
func NewTree(root: dyn Widget): TreeThe example for this item is shown with the ones above: tui-widget.salam
WrapText
func WrapText(text: str, width: int): Vector<str>The example for this item is shown with the ones above: tui-text.salam
MeasureText
func MeasureText(text: str, max_width: int): RectThe example for this item is shown with the ones above: tui-text.salam
NewText
func NewText(content: str): TextThe example for this item is shown with the ones above: tui-text.salam
NewTable
func NewTable(): TableThe example for this item is shown with the ones above: tui-table.salam
NewTableView
func NewTableView(): TableViewThe example for this item is shown with the ones above: tui-table.salam
BorderInset
func BorderInset(b: BorderStyle): intThe example for this item is shown with the ones above: tui-style.salam
StyleMerge
func StyleMerge(parent: Style, child: Style): StyleThe example for this item is shown with the ones above: tui-style.salam
StyleEq
func StyleEq(a: Style, b: Style): boolThe example for this item is shown with the ones above: tui-style.salam
StyleSgr
func StyleSgr(style: Style, tier: ColorTier): strstyle at tier: attributes first in the fixed order bold, dim, italic, underline, reverse, then the foreground, then the background. Attributes ignore the tier (NO_COLOR asks for no color, not for no bold); each color is lowered to the tier first, and one that is Default after lowering is skipped. Returns "" when nothing is set, and never emits a reset: every sequence is built from term's builders and color.salam's encoders, none is written by hand.The example for this item is shown with the ones above: tui-style.salam
StyleSgrOff
func StyleSgrOff(style: Style, tier: ColorTier): strstyle back to its default state at tier, in the fixed order a single 22 if bold or dim is set (22 clears both together), then 23 if italic, then 24 if underline, then 27 if reverse, then 39 if the foreground is non-default after lowering, then 49 if the background is. Returns "" when nothing is set and never emits a reset; the codes are built with term.Sgr.The example for this item is shown with the ones above: tui-style.salam
SgrBuilds
func SgrBuilds(): intThe example for this item is shown with the ones above: tui-style.salam
NewSeparator
func NewSeparator(): SeparatorThe example for this item is shown with the ones above: tui-text.salam
NewScreen
func NewScreen(root: dyn Widget, width: int, height: int): ScreenThe example for this item is shown with the ones above: tui-screen.salam
NewTerminalScreen
func NewTerminalScreen(root: dyn Widget): ScreenThe example for this item is shown with the ones above: tui-terminal.salam
NewProgress
func NewProgress(): ProgressThe example for this item is shown with the ones above: tui-text.salam
NewModal
func NewModal(): ModalThe example for this item is shown with the ones above: tui-panels.salam
NewList
func NewList(items: Vector<str>): ListThe example for this item is shown with the ones above: tui-controls.salam
NewInsets
func NewInsets(top: int, right: int, bottom: int, left: int): InsetsThe example for this item is shown with the ones above: tui-layout.salam
UniformInsets
func UniformInsets(v: int): InsetsThe example for this item is shown with the ones above: tui-layout.salam
FitRect
func FitRect(r: Rect, box: Rect): RectThe example for this item is shown with the ones above: tui-layout.salam
InsetRect
func InsetRect(r: Rect, i: Insets): RectThe example for this item is shown with the ones above: tui-layout.salam
SplitMain
func SplitMain(length: int, sizes: Vector<LayoutSize>, gap: int): Vector<int>length cells between sizes, in order (R5). Fixed takes max(0, cells) and ignores weight. gap is the cells between adjacent slots. A negative gap clamps to 0. The budget is gap * max(0, k - 1), where k is the slot count, including zero-size slots; k <= 1 charges nothing. Fill and Pref share the leftover R = max(0, length - sum of those Fixed sizes - budget). Arrange resolves a measured Pref to Fixed before this call, and a Pref that cannot measure to Fill weight 1. This function does not measure; a Pref still in the vector stays in the weight pool. Each pool entry's weight is max(0, weight). The share is floor(R * weight / sum of weights), then one extra cell goes to each remaining cell, given to the pool entries with the largest (R * weight) mod sum of weights. Ties go to the earliest pool slot. A weight sum of 0 gives every pool entry 0 and leaves R unused, with no division. Fixed overflow, or a budget that uses up the length, makes R 0, so every pool entry is 0 and Fixed is not shrunk. The gap is not part of the returned sizes and is not refunded. The vector has one entry per input. Weight 1 on every pool entry is an equal split, remainder to the earliest entries.The example for this item is shown with the ones above: tui-layout.salam
TrackSizes
func TrackSizes(total: int, n: int): Vector<int>The example for this item is shown with the ones above: tui-layout.salam
PlaceAxisSize
func PlaceAxisSize(avail: int, align: CrossAlign, natural: int): intThe example for this item is shown with the ones above: tui-layout.salam
PlaceAxisOffset
func PlaceAxisOffset(avail: int, align: CrossAlign, natural: int): intThe example for this item is shown with the ones above: tui-layout.salam
NewRow
func NewRow(): RowThe example for this item is shown with the ones above: tui-layout.salam
NewColumn
func NewColumn(): ColumnThe example for this item is shown with the ones above: tui-layout.salam
NewGrid
func NewGrid(rows: int, cols: int): GridThe example for this item is shown with the ones above: tui-layout.salam
NewStack
func NewStack(): StackThe example for this item is shown with the ones above: tui-layout.salam
NewKeymap
func NewKeymap(): KeymapThe example for this item is shown with the ones above: tui-events.salam
NewDefaultKeymap
func NewDefaultKeymap(): KeymapThe example for this item is shown with the ones above: tui-events.salam
NewVimKeymap
func NewVimKeymap(on_search: func(Screen*, Event): bool): KeymapThe example for this item is shown with the ones above: tui-events.salam
NewInput
func NewInput(): InputThe example for this item is shown with the ones above: tui-controls.salam
Nearest
func Nearest(from: Rect, candidates: Vector<Rect>, dir: int): intfrom in direction dir (DirUp, DirDown, DirLeft, DirRight) and returns its index in candidates, or -1 when no candidate qualifies. The rules are spelled out in the comments of the function body. O(n): every candidate is looked at once.The example for this item is shown with the ones above: tui-geometry.salam
FgSeq
func FgSeq(c: Color): strThe example for this item is shown with the ones above: tui-style.salam
BgSeq
func BgSeq(c: Color): strThe example for this item is shown with the ones above: tui-style.salam
DetectTier
func DetectTier(): ColorTierThe example for this item is shown with the ones above: tui-style.salam
Lower
func Lower(c: Color, tier: ColorTier): ColorThe example for this item is shown with the ones above: tui-style.salam
FgSeqAt
func FgSeqAt(c: Color, tier: ColorTier): strtier: FgSeq of Lower(c, tier), so the emitted escape matches the tier and no sequence is written by hand.The example for this item is shown with the ones above: tui-style.salam
BgSeqAt
func BgSeqAt(c: Color, tier: ColorTier): strtier: BgSeq of Lower(c, tier), so the emitted escape matches the tier and no sequence is written by hand.The example for this item is shown with the ones above: tui-style.salam
NewCheckbox
func NewCheckbox(label: str, checked: bool): CheckboxThe example for this item is shown with the ones above: tui-controls.salam
NewButton
func NewButton(label: str): ButtonThe example for this item is shown with the ones above: tui-controls.salam
DetectGlyphPolicy
func DetectGlyphPolicy(): GlyphPolicyThe example for this item is shown with the ones above: tui-buffer.salam
NewBuffer
func NewBuffer(width: int, height: int): BufferThe example for this item is shown with the ones above: tui-buffer.salam
GlyphsSingle
func GlyphsSingle(): BorderGlyphsThe example for this item is shown with the ones above: tui-buffer.salam
GlyphsDouble
func GlyphsDouble(): BorderGlyphsThe example for this item is shown with the ones above: tui-buffer.salam
GlyphsRounded
func GlyphsRounded(): BorderGlyphsThe example for this item is shown with the ones above: tui-buffer.salam
GlyphsASCII
func GlyphsASCII(): BorderGlyphs- and | runs and + for every corner and junction. Used for every border under GlyphPolicy.ASCII.The example for this item is shown with the ones above: tui-buffer.salam
GlyphsHeavy
func GlyphsHeavy(): BorderGlyphsThe example for this item is shown with the ones above: tui-buffer.salam
GlyphsMixed
func GlyphsMixed(): BorderGlyphsThe example for this item is shown with the ones above: tui-buffer.salam
GlyphsFor
func GlyphsFor(border: BorderStyle, policy: GlyphPolicy): BorderGlyphsborder under policy. BorderStyle.None gives the zero value (no glyphs). Under GlyphPolicy.ASCII every other style gives GlyphsASCII; otherwise Single, Double and Rounded give GlyphsSingle, GlyphsDouble and GlyphsRounded.The example for this item is shown with the ones above: tui-buffer.salam
DrawBorder
func DrawBorder(buf: Buffer, rect: Rect, border: BorderStyle, style: Style, title: str)border around rect into buf, every cell in one foreground color taken from style.fg. Rules: - BorderStyle.None, or a rect with no area, writes nothing. - A one-row rect is a horizontal rule and a one-column rect is a vertical rule; neither has corners or a title. - Otherwise the four corners are drawn, then the horizontal edges, then the vertical edges. Edges run strictly between the corners. - title sits on the top edge starting at rect.x + 2, with one space before and after it. It is trimmed; an empty title draws nothing; it is cut to rect.width - 5 display cells on the right and needs a width of at least 6. - Only foreground color is used. Attributes in style are ignored, and a Color.Default fg writes an empty style (the terminal default). Writes go through Buffer.set, which clips to the grid and the clip region, so a rect partly outside the buffer paints only its inside part.The example for this item is shown with the ones above: tui-buffer.salam
DrawBorderGlyphs
func DrawBorderGlyphs(buf: Buffer, rect: Rect, glyphs: BorderGlyphs, style: Style, title: str)glyphs around rect into buf, with every rule of DrawBorder (degenerate rects, title, one foreground color). Use it for a custom or non-default glyph set. When buf.glyphs is GlyphPolicy.ASCII the set is replaced by GlyphsASCII, so a custom set never emits a non-ASCII glyph under that policy.The example for this item is shown with the ones above: tui-buffer.salam