Documentation
¶
Overview ¶
Package button provides the Vibrant Gio button: a text or icon-only affordance carrying hover, focus, press and disabled treatments, activation by click or by Space and Enter, a screen-reader label, and a pointer target of at least 44 dp on each axis regardless of density (the drawn control is the theme Density's control height; the hit area extends beyond it when the control is smaller).
Button is the observable path — an rx.Observable[theme.Theme] and a Props in, an rx.Observable[layout.Widget] out, rebuilt whenever the theme changes — and activations leave through either Props.OnClick, which is handed the frame's layout.Context, or Props.Message, which adds an mvu.MessageOp to the frame's ops for an MVU runtime to deliver to Update. Render and RenderIcon are the pure path: resolved tokens plus an explicit RenderState in, one frame out, no event handling — that is what the golden-image tests drive and what static rendering should use.
Three things it assumes. Interaction state is allocated inside the component's rx.Defer scope, so press and focus survive the view rebuilds an MVU loop drives; pass Props.Clickable when an enclosing container such as a modal must own the focus tag instead. A button fills the width it is given and is at least the density's control height tall (36 dp Comfortable, 28 dp Compact), so a fixed-size button is laid out inside a constrained box. And Props.Shaper is not optional today — leave it nil and the button builds a Go-fonts shaper for itself, with no warning, and renders in the wrong typeface.
Index ¶
- func Button(th rx.Observable[theme.Theme], props Props) rx.Observable[layout.Widget]
- func Render(shaper *text.Shaper, label string, colors tokens.ColorTokens, ...) layout.Widget
- func RenderIcon(icon func(gtx layout.Context, sizePx int, col color.NRGBA), ...) layout.Widget
- type Props
- type RenderState
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Button ¶
func Button(th rx.Observable[theme.Theme], props Props) rx.Observable[layout.Widget]
Button returns an rx.Observable[layout.Widget] that emits a new widget whenever the theme or disabled state changes. Widget state (clickable, hover, focus, press) lives in the rx.Defer scope and persists across emissions.
Both integration paths are supported:
- FRP: set Props.OnClick; FRP consumers wrap with rx.NewSubject if needed.
- MVU: set Props.Message; the component emits mvu.MessageOp on activation.
func Render ¶
func Render( shaper *text.Shaper, label string, colors tokens.ColorTokens, sp tokens.SpacingScale, rad tokens.RadiusScale, labelStyle tokens.TextStyle, d tokens.Density, s RenderState, ) layout.Widget
Render produces a layout.Widget for a button in an explicit visual state, without any event processing or rx machinery. Intended for golden-image testing and static demonstrations; production code should use Button, which reads both of the parameters below off the theme.
labelStyle is the LabelLarge role's whole text style and d is the density the button draws at (control height and inner padding). Pass tokens.DefaultTypography.LabelLarge and tokens.Comfortable for the default desktop look.
All four properties of the style are honoured, and line height is honoured in the sense a design system means: the label box is labelStyle.LineHeight tall, leading split evenly above and below the glyphs, so the button's height derives from the type role rather than from which letters the label happens to contain. Handing the number to gioui.org/widget.Label does not achieve that — it changes nothing on a single line — so the layout goes through spectrum/typeset, which is where that discrepancy is documented.
The drawn height is therefore max(d.ControlHeight, LineHeight + 2×d.PaddingY), and the second term wins for Compact at any of the label roles: 20 + 12 = 32 against a 28 dp control height. tokens.Density.ControlHeight is a floor, not a height.
func RenderIcon ¶
func RenderIcon( icon func(gtx layout.Context, sizePx int, col color.NRGBA), colors tokens.ColorTokens, sp tokens.SpacingScale, rad tokens.RadiusScale, d tokens.Density, s RenderState, ) layout.Widget
RenderIcon produces a layout.Widget for a compact icon-only button in an explicit visual state, without event processing or rx machinery. The glyph is drawn by icon into a square d.ControlHeight on a side, inset by d.PaddingY. Pass tokens.Comfortable for the default desktop look. Intended for golden-image testing and static demonstrations; production code should use Button with Props.Icon (and, when a container drives focus, Props.Clickable).
It takes no text style: an icon-only button draws no text, so unlike Render there is nothing for a tokens.TextStyle to reach.
Types ¶
type Props ¶
type Props struct {
// Label is the text rendered inside the button.
Label string
// Description is the screen-reader label. Falls back to Label when empty.
Description string
// Icon, when non-nil and Label is empty, renders the button as a compact
// icon-only affordance: a square the density's control height on a side
// with the glyph centred, instead of a fill-width text label (the pointer
// target stays at least the 44 dp square). The painter draws into
// a sizePx×sizePx box at the current origin in colour col, via
// clip.Path / clip.Stroke, so output stays golden-deterministic (no font or
// SVG rasterisation). prism/icon is the registry for named glyphs;
// determinism-sensitive callers pass a clip.Path painter directly.
Icon func(gtx layout.Context, sizePx int, col color.NRGBA)
// Disabled, if non-nil, disables the button when it emits true.
// A nil Disabled means always enabled.
Disabled rx.Observable[bool]
// OnClick is called when the button is activated by click or Space/Enter.
// This is the FRP callback path. The gtx argument is the layout.Context
// active on the frame when the click is processed, allowing consumers to
// emit mvu.MessageOp{Message: ...}.Add(gtx.Ops) inside the callback.
OnClick func(gtx layout.Context)
// Message, if non-nil, causes the button to emit mvu.MessageOp{Message}
// into gtx.Ops on activation. This is the MVU integration path.
Message any
// Clickable, if non-nil, is used instead of an internally-allocated one.
// The caller then owns &Clickable as the button's focus tag — usable with
// key.FocusCmd, key.Filter{Focus: …} and an external Tab cycle — and may
// detect activation via Clickable.Clicked(gtx). This lets a container (e.g.
// cadence/modal) drive focus and trap Tab without a doubled focus ring.
// When nil the button allocates and owns its own clickable.
Clickable *widget.Clickable
// Shaper is an explicit per-instance override of the text shaper. Leave it
// nil in normal use: the button then shapes its label with the theme's
// shaper (Typography.Shaper()), which is built once for the process and
// shared by every component reading that typography — the cache lives
// behind the Typography value, so it survives the copy this component's
// map function makes of it (spectrum F5.1). Set it only when this button
// must shape with a different shaper than the theme provides.
//
// A shaper is not safe to use from two goroutines; Gio lays the widget
// forest out on the one goroutine that runs the event loop, which is what
// makes sharing it correct. See spectrum/tokens.Typography.Shaper.
Shaper *text.Shaper
}
Props configures a Button instance.
type RenderState ¶
RenderState holds explicit visual interaction state for static rendering. All fields default to false (normal/idle state). Intended for golden-image testing; production code obtains state from the Gio event system via Button.