Builtin Library
README.md
DockedAppLayout
The layout module for the docked_app scaffold. It builds one UI screen from
the engine's raw, CSS-parity widget tree: a top-docked toolbar, a scrolling
body, and a bottom-docked status bar. Interactions are wired up — a "Click me"
button counts clicks, Save and Reset update the status message.
This is the file you edit. Replace the body builder with your own content and add toolbar / status widgets as needed.
The contract
A UiPanel component points its source at this module and calls mount(ctx),
where ctx = { entity, entityId, data, state? }. mount returns the handlers
the panel drives each frame:
function M.mount(ctx)
local state = ctx.state or { clicks = 0, message = "click the button" }
local function build() return { --[[ widget tree ]] } end
ui.registerScreen("docked_app", build()) -- register the tree as a screen
return {
state = state,
update = function(dt) end, -- per-frame work (no-op here)
onCallback = function(id, data) -- an interaction lands here
if id == "doc:click" then
state.clicks = state.clicks + 1
state.message = "clicked " .. tostring(state.clicks) .. " time(s)"
ui.updateScreen("docked_app", build()) -- mutate, then re-render
end
end,
destroy = function() ui.unregisterScreen("docked_app") end,
}
end
stateis returned so the panel hands it back on a live-reload — your clicks survive an edit.update(dt)runs every frame; leave it a no-op when nothing animates.onCallback(id, data)receives every widget's callback id. Mutate state, then callui.updateScreen(name, build())to publish the new tree.destroy()unregisters the screen when the panel goes away.
The docked shell
The screen root is a full-size vertical holding three regions:
topPanel— the toolbar, docked to the top edge. Holds the title, the Save / Reset actions, and a right-aligned click count.- The body — a
scrollAreawithflex = 1that fills the space between the panels and grows with the viewport. Replace this builder with your content. bottomPanel— the status bar, docked to the bottom edge. Shows a ready marker and the current message.
topPanel and bottomPanel are the screen-edge docking node types; the body
takes the remaining middle.
The raw widget tree
A node is a table: { type = "...", props = {...}, style = {...}, children = {...} }.
- Containers lay out their children:
vertical(column),horizontal(row),scrollArea(a scrolling column), and the screen-edge panels (topPanel,bottomPanel). - Leaves carry content:
label(text inprops),button(text +onClickinprops). propsis the widget's data and behaviour — a label'stext, a button'sonClickcallback id.styleis CSS:padding,gap,background,color,fontSize,fontWeight,borderRadius,borderWidth,borderColor,alignItems,justifyContent,flex,boxShadow, and the rest of the box model.
The tree is a pure function of state: to change what shows, rebuild the tree
and call ui.updateScreen. There is no retained widget object to mutate.
Interactivity
An interactive widget carries a string callback id in props. When the
user clicks, the engine delivers that id to onCallback here:
doc:click— the body's "Click me" button: incrementsclicksand updates the status message.doc:save— the toolbar's Save button: sets the message to "saved".doc:reset— the toolbar's Reset button: zeroesclicksand sets the message to "reset".
onClick works on any widget — a label, a horizontal row, an image — not
just a button.
Interaction runs in play, not edit: enter play and use the buttons to see the count and status message change.
Discover the surface
- The
guitoolbox's widgetTypes tool lists everytype. - Its widgetProps tool lists one type's
props. @builtin::examples.ui.*are complete CSS-parity screens (dashboard, sci-fi HUD, RPG inventory) in this exact model — read one before building.- The UI and styling guides cover the model and the full style surface.