Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

button

A box that takes clicks, left-button drags and the wheel; no other kind has these handlers. It stacks its children like a rect, so put a row inside for an icon beside a label. Which button wins, when a click cancels and when a drag ends are on input.

A volume chip: left click mutes, the wheel changes the level.

local muted = state("muted", false)
local volume = state("volume", 0.5)

local mute_button = button {
    padding = { left = 10, right = 10, top = 6, bottom = 6 },
    radius = 8,
    background = muted:map(function(m) return m and "#F38BA8" or "#313244" end),
    on_click = function(_, which)
        if which == "left" then muted:set(not muted:get()) end
    end,
    on_wheel = function(_, steps)
        volume:set(math.max(0, math.min(1, volume:get() + steps * 0.05)))
    end,
    children = { row { spacing = 6, align_v = "Center", children = {
        icon { name = "audio-volume-high-symbolic", size = 16, foreground = "#CDD6F4", align_v = "Center" },
        text { content = volume:map(function(v) return string.format("%d%%", math.floor(v * 100 + 0.5)) end),
               align_v = "Center" },
    } } },
}

return mute_button

Properties

button takes the common and box properties, plus the ones below. rect in the callbacks is the button’s surface-local laid-out box { x, y, width, height }, before transforms.

PropertyTypeDefaultBehaviour
childrenNode[]|BoundNoneArray of node tables, up to 10000; a nil or false entry is an error. Stacked in order: later children paint over earlier ones. Bind a signal of an array to switch views
on_clickfun(rect: Rect, button: "left"|"right"|"middle")NoneOn release over the same button that was pressed, with the same mouse button. rect is the button’s surface-local box, before transforms
on_dragfun(rect: Rect, pointer: { x: number, y: number }, phase: "start"|"move"|"end")NoneLeft-button drag. pointer is button-local and unclamped. "start" on press, "end" on release (before on_click) or when the pointer leaves the surface
on_wheelfun(rect: Rect, steps: number)NoneVertical wheel in notches, positive away from the user, fractional on touchpads. The innermost handler or scroll container wins
submitboolean|BoundfalseA click also submits the armed secure field, like Enter. Works without on_click and runs before it

A button with none of on_click, on_drag, on_wheel and submit = true ignores the pointer: clicks fall through to what is under it, and it sets no cursor.

How do I…

TaskAnswer
Toggle something on clickThe example above
Open a menu on right clickCheck button == "right" and open a popup at rect (input)
Make a slideron_drag for the value, on_wheel for steps (input)
Show hover feedbackBind background or shadow_* to a hover signal and tween it with animate (paint)
Submit a password with a buttonsubmit = true (secure fields)
Change the cursorcursor = "grab" or any cursor name
Make a whole row clickableMake the button the row’s parent, width = "Fill", with a row inside

Gotchas

TrapFix
Icon and label overlapA button stacks its children. Put a row inside
A click lands on the node behind the buttonThe button has no handler and no submit = true, so it is transparent to the pointer
A click is lost when the button moves or resizes on pressA click cancels if the laid-out box moved between press and release. Give press feedback with scale or translate, not width or margin
on_click never fires for a link inside the buttonA text with on_link takes the click on a link run first
A press on a textfield inside the button does not clickPresses on a field go to the field

See also: input, rect, surfaces: popup.

Source: vocabulary, pointer dispatch, hit testing, takes_pointer.