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.
| Property | Type | Default | Behaviour |
|---|---|---|---|
children | Node[]|Bound | None | Array 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_click | fun(rect: Rect, button: "left"|"right"|"middle") | None | On release over the same button that was pressed, with the same mouse button. rect is the button’s surface-local box, before transforms |
on_drag | fun(rect: Rect, pointer: { x: number, y: number }, phase: "start"|"move"|"end") | None | Left-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_wheel | fun(rect: Rect, steps: number) | None | Vertical wheel in notches, positive away from the user, fractional on touchpads. The innermost handler or scroll container wins |
submit | boolean|Bound | false | A 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…
| Task | Answer |
|---|---|
| Toggle something on click | The example above |
| Open a menu on right click | Check button == "right" and open a popup at rect (input) |
| Make a slider | on_drag for the value, on_wheel for steps (input) |
| Show hover feedback | Bind background or shadow_* to a hover signal and tween it with animate (paint) |
| Submit a password with a button | submit = true (secure fields) |
| Change the cursor | cursor = "grab" or any cursor name |
| Make a whole row clickable | Make the button the row’s parent, width = "Fill", with a row inside |
Gotchas
| Trap | Fix |
|---|---|
| Icon and label overlap | A button stacks its children. Put a row inside |
| A click lands on the node behind the button | The 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 press | A 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 button | A text with on_link takes the click on a link run first |
A press on a textfield inside the button does not click | Presses on a field go to the field |
See also: input, rect, surfaces: popup.
Source: vocabulary, pointer dispatch, hit testing, takes_pointer.