Skip to content

The rules

Every part of kiln follows these. If something does not, it is a bug (see Problems). The original version is at the top of kiln/kiln.odin.

if kiln.card("Profil") {
if kiln.row() { ... }
}

All containers use the same parameter names:

namewhattype
gapspace between childrenSpace
paddingspace inside the borderSpace
fillwhich axes growFill
aligncross axis alignmentAlign
widthfixed width in unitsf32

A value widget takes the current value and returns (new_value, changed). The view never writes to the model, it turns changes into messages:

if v, ok := kiln.slider(m.volume, 0, 100); ok {append(out, Set_Volume{v})}
if kiln.button("Speichern") {append(out, Save{})} // actions return bool
if i, ok := kiln.menu("Aktionen", ITEMS[:]); ok { ... } // menus return (index, ok)
  • Selections are indices with -1 for “nothing”.
  • Choice widgets (tabs, segmented, nav, select, chips) also take an enum-indexed array of labels and then return the enum.
  • Strings returned by widgets live until the end of the frame. Clone them in update.

3. UI state lives in kiln, app state lives in the model

Section titled “3. UI state lives in kiln, app state lives in the model”

Focus, cursor, scroll offsets, open popups, hover fades, table sorting, expanded tree nodes and collapsed sections are kept by kiln. You do not store them.

Dialogs and drawers are opened by name:

if kiln.button("Löschen", .Danger) {kiln.show("confirm")}
if kiln.dialog("confirm", "Wirklich löschen?") { ... kiln.hide() ... }

4. Item queries refer to the widget declared just before them

Section titled “4. Item queries refer to the widget declared just before them”
kiln.button("Speichern")
kiln.tooltip("Strg+S")
if kiln.list_item(selected) { ... }
if kiln.clicked() { ... } // clicked, hovered, submitted
if i, ok := kiln.context_menu(ITEMS[:]); ok { ... }

clicked, hovered, submitted, tooltip and context_menu all work like this.

Widgets are identified by their label (or kind) and how often it occurred before in the current scope, so equal labels never clash.

When a loop draws items of a list that can change, wrap each item in a scope, so its UI state (hover, open, focus) follows the item and not the position:

for item in m.items {
if kiln.scope(item.id) { ... }
}

Spacing, radii, sizes, fonts and colors are named steps (.SM, .Raised, .Muted, …). The theme decides what they are. Numbers only appear as widths in units (scaled pixels).

See Theme.

screen() classifies the window as .Compact (phones), .Medium (tablets, narrow windows) or .Expanded. by_screen(a, b, c) picks a value per class. Containers also adapt to the space they actually get. See Layout.