Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit44fd9e59bd

README.md

CanvasAppLayout

The layout module for the canvas_app scaffold. It builds a single UI screen from the engine's raw, CSS-parity widget tree: a header bar over a dark canvas holding two absolutely-positioned node cards joined by an SVG bezier connector. Clicking a node selects it — the card highlights and the header's "last click" readout updates.

This is the file you edit. Add nodes and connections to state; the tree is rebuilt from that data.

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 { nodes = {...}, connections = {...}, selected = nil, lastClick = "(none)" }
    local function build() return { --[[ widget tree ]] } end
    ui.registerScreen("canvas_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
            local nodeId = string.match(id, "^graph:click:(.+)$")
            if nodeId then
                state.selected = nodeId
                ui.updateScreen("canvas_app", build())  -- mutate, then re-render
            end
        end,
        destroy = function() ui.unregisterScreen("canvas_app") end,
    }
end
  • state is returned so the panel hands it back on a live-reload — your selection survives 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 call ui.updateScreen(name, build()) to publish the new tree.
  • destroy() unregisters the screen when the panel goes away.

The raw widget tree

A node is a table: { type = "...", props = {...}, style = {...}, children = {...} }.

  • Containers lay out their children: vertical (column), horizontal (row), spacer (flexes to push siblings apart).
  • Leaves carry content: label (text in props), svg (an inline SVG string in props.svg, drawn at display resolution).
  • props is the widget's data and behaviour — a label's text, an SVG's markup, a clickable widget's onClick callback id.
  • style is CSS: position, left, top, width, height, background, color, fontSize, fontWeight, padding, gap, borderRadius, borderWidth, borderColor, boxShadow, overflow, 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.

The node-graph layout

A node-graph canvas is built with absolute positioning:

  • The canvas is a vertical with style = { position = "relative" } and a fixed width / height, so its children position against its top-left corner.
  • Each node card is a vertical with style = { position = "absolute", left = x, top = y, ... } — its (x, y) come straight from the node's data.
  • The connection lines are one svg leaf sized to the canvas and placed position = "absolute", left = 0, top = 0 so it sits behind the cards (it is the first child). Each connection is a bezier <path> from one node's right edge to another's left edge.

Interactivity

Each node card carries a string callback id in props (onClick = "graph:click:" .. node.id). onClick works on any widget — the whole card is clickable, not just a button inside it. The node id is encoded in the callback id; onCallback parses it back out with string.match, records the selection, and re-renders so the selected card highlights.

Interaction runs in play, not edit: enter play and click a node to select it and watch the "last click" readout update.

Discover the surface

  • The gui toolbox's widgetTypes tool lists every type.
  • 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.