# ui

`import "github.com/matjam/bunyip/ui"`

Package ui is an immediate-mode interface toolkit drawn with gfx. To build the interface, call widget methods inside Begin's closure every frame, nesting containers (Panel, Window, Row, Columns, ScrollArea, Tabs, Table, Tree, MenuBar, Modal) the same way. Widgets return what happened (a click, a changed value). Values live in the game's variables and are passed by pointer; Context retains interaction state such as focus, caret, selection, undo history and scrolling. Games using engine.Context.NewUI get a default theme and shared font plus clipboard and input-method placement wiring. Use New directly when supplying those dependencies yourself.

The widgets are Label and RichLabel, Button and IconButton, Checkbox, Radio and RadioGroup, Slider, IntSlider and Spinner, Progress, Dropdown, ListBox and ReorderableList, TextField and TextArea with selection, clipboard and undo, ColorPicker, Image, Tooltip and Separator. Anchored, Stretched and Split place rectangles from the view's size. DragSource and DropTarget carry a payload from one widget to another and draw a ghost under the pointer.

A widget's identity comes from its label and its enclosing containers. Widgets with the same label in one container are told apart by the order they are called in, so a list of identical buttons works as long as the order is stable. Focus moves with Tab and the d-pad and activates with Enter, Space or a gamepad's A. A list, a row of tabs, a table's rows, a tree, a radio group and an open dropdown are each one Tab stop, and the arrows, Home, End, PageUp and PageDown move between the items inside. Sliders and spinners step with the left and right arrows. WantsMouse reports panel hover and dragging; WantsKeyboard reports text focus. Neither consumes input or gates the game's own controls. Accessible lists the last frame's widgets with roles and values for screen readers and tests.

Colours, spacing and the font live in a Theme. To restyle the toolkit, swap that one value. The built-in themes come from NamedTheme. A Skin of nine-slice textures inside the theme replaces the flat rectangles with drawn art.

## Variables

<a id="Palettes"></a>

```go
var Palettes = map[string]Palette{ /* … */ }
```

Palettes are the built-in colour schemes by name; see ThemeNames.

## Functions

<a id="Move"></a>

### Move

```go
func Move[T any](s []T, from, to int)
```

Move shifts the element at from so it ends up at index to, keeping the order of the rest: what a caller does with ReorderableList's result.

<a id="ThemeNames"></a>

### ThemeNames

```go
func ThemeNames() []string
```

ThemeNames returns the built-in theme names in menu order.

## Types

<a id="AccessibleNode"></a>

<a id="AccessibleNode.Role"></a>

<a id="AccessibleNode.Label"></a>

<a id="AccessibleNode.Value"></a>

<a id="AccessibleNode.Rect"></a>

<a id="AccessibleNode.State"></a>

<a id="AccessibleNode.Focused"></a>

### AccessibleNode

```go
type AccessibleNode struct {
	// Role is one of button, checkbox, slider, textfield, textarea, tab,
	// tree, menu, menuitem, radio, spinner, listbox, colorpicker, image,
	// label, window, dropdown, progress, row (of a Table), list and
	// listitem (a ReorderableList and its rows), draggable and droptarget.
	Role    string
	Label   string // human-readable widget label
	Value   string // the current value where there is one
	Rect    Rect   // bounds in view units
	State   bool   // checked, selected, open, dragged or ready for a drop, as the role implies
	Focused bool   // has keyboard focus
}
```

AccessibleNode describes one widget of the last frame for assistive technology: what it is, what it says, where it is and its state. A game or platform layer can read the list with Accessible and hand it to a screen reader, log it, or drive the interface from it.

<a id="Anchor"></a>

### Anchor

```go
type Anchor uint8
```

Anchor names a point of an area a rectangle hangs from.

<a id="TopLeft"></a>

<a id="Top"></a>

<a id="TopRight"></a>

<a id="Left"></a>

<a id="Center"></a>

<a id="Right"></a>

<a id="BottomLeft"></a>

<a id="Bottom"></a>

<a id="BottomRight"></a>

```go
const (
	TopLeft Anchor = iota
	Top
	TopRight
	Left
	Center
	Right
	BottomLeft
	Bottom
	BottomRight
)
```

<a id="Clipboard"></a>

<a id="Clipboard.Clipboard"></a>

<a id="Clipboard.SetClipboard"></a>

### Clipboard

```go
type Clipboard interface {
	Clipboard() (string, error)
	SetClipboard(string) error
}
```

Clipboard is what text fields cut, copy and paste through; the engine's Context satisfies it, so a game sets ui.Clipboard = ctx.

<a id="Context"></a>

<a id="Context.Theme"></a>

<a id="Context.OnTextInputRect"></a>

<a id="Context.DragGhost"></a>

<a id="Context.Clipboard"></a>

### Context

```go
type Context struct {
	Theme Theme // live widget styling; referenced resources are managed separately

	// OnTextInputRect, when set, is told where the focused text field is
	// so the platform can place input-method candidate windows. Wire it to
	// engine.Context.SetTextInputRect.
	OnTextInputRect func(x, y, w, h float32)

	// DragGhost, when set, draws what follows the pointer during a drag
	// in place of the source's label.
	DragGhost func(label string, payload any, x, y float32)

	// Clipboard, when set, is what text fields cut, copy and paste
	// through; the engine's Context satisfies it.
	Clipboard Clipboard
	// contains filtered or unexported fields
}
```

Context drives one interface. Create one and reuse it every frame. Build it during the game's Draw on the rendering goroutine; it is not safe for concurrent use and does not own fonts or skin textures.

<a id="New"></a>

#### New

```go
func New(g *gfx.Graphics, theme Theme) *Context
```

New makes a context drawing with g under theme.

<a id="Context.Accessible"></a>

#### Context.Accessible

```go
func (c *Context) Accessible() []AccessibleNode
```

Accessible returns the widgets of the last finished frame in reading order. The slice is borrowed and reused by later frames. Do not modify it; copy it if it must outlive the next Begin. No operating-system accessibility bridge is installed by this method.

<a id="Context.Begin"></a>

#### Context.Begin

```go
func (c *Context) Begin(in *input.State, body func())
```

Begin runs one frame of interface: body calls the widget methods, and the frame is finished (overlays drawn, state settled) when it returns.

	ui.Begin(ctx.Input, func() {
		ui.Panel("Menu", rect, func() { ... })
	})

Example:

```go
package main

import (
	"github.com/matjam/bunyip/gfx"
	"github.com/matjam/bunyip/input"
	"github.com/matjam/bunyip/ui"
)

// In a game these come from engine.Context: ctx.Gfx and ctx.Input. The
// font is created once in Init.
var (
	g    *gfx.Graphics
	in   *input.State
	font *gfx.Font
)

// Options a menu edits.
var (
	volume     float32 = 0.8
	fullscreen bool
	name       string
	quality    int
)

func main() {
	u := ui.New(g, ui.DarkTheme(font))
	// Every frame, inside Draw:
	u.Begin(in, func() {
		u.Panel("Options", ui.Rect{X: 20, Y: 20, W: 300, H: 260}, func() {
			u.Slider("Volume", &volume, 0, 1)
			u.Checkbox("Fullscreen", &fullscreen)
			u.Dropdown("Quality", &quality, []string{"Low", "High"})
			u.TextField("Player name", &name)
			u.Row(2, func() {
				if u.Button("Apply") {
					// ...
				}
				u.Button("Cancel")
			})
		})
	})
}
```

<a id="Context.Button"></a>

#### Context.Button

```go
func (c *Context) Button(label string) bool
```

Button draws a push button and reports a click.

<a id="Context.Cell"></a>

#### Context.Cell

```go
func (c *Context) Cell(text string)
```

Cell draws a plain text cell, for tables of values.

<a id="Context.Checkbox"></a>

#### Context.Checkbox

```go
func (c *Context) Checkbox(label string, value *bool) bool
```

Checkbox toggles \*value on click and reports a change.

<a id="Context.ColorPicker"></a>

#### Context.ColorPicker

```go
func (c *Context) ColorPicker(label string, col *gfx.Color) bool
```

ColorPicker edits a colour with a hue bar and a saturation-value square, showing a swatch and the hex value; it reports a change.

<a id="Context.Columns"></a>

#### Context.Columns

```go
func (c *Context) Columns(weights []float32, body func())
```

Columns lays the widgets body creates side by side, one per weight, with widths in proportion to the weights:

	ui.Columns([]float32{2, 1}, func() { ui.Dropdown(...); ui.Checkbox(...) })

<a id="Context.CurveEditor"></a>

#### Context.CurveEditor

```go
func (c *Context) CurveEditor(label string, points *[]lin.Vec2, lo, hi, height float32) bool
```

CurveEditor edits a curve of points drawn as a graph: drag a point to move it, click an empty part of the graph to add one, right-click a point to remove it. It reports whether the curve changed this frame.

The points are (x, y) pairs kept in increasing x. X runs from 0 to 1 across the graph and y from lo to hi up it, so a particle curve over a lifetime passes lo and hi as the range its values may take. The first and last points keep their x, so a curve always spans the whole range; the rest move freely between their neighbours. A curve of fewer than two points is drawn but only its points move.

height is the graph's height in view units; zero is 80.

<a id="Context.DragSource"></a>

#### Context.DragSource

```go
func (c *Context) DragSource(label string, payload any, body func()) bool
```

DragSource makes the widgets body creates draggable: pressing on them and moving the pointer a few units starts a drag carrying payload, with a ghost of label (or what DragGhost draws) following the pointer until it is released on a DropTarget or Escape cancels it. It reports whether its payload is being dragged this frame.

<a id="Context.Dragging"></a>

#### Context.Dragging

```go
func (c *Context) Dragging() (payload any, ok bool)
```

Dragging returns the payload of the drag in progress, so a target can show itself ready while something it accepts is over it.

<a id="Context.DropTarget"></a>

#### Context.DropTarget

```go
func (c *Context) DropTarget(label string, accept func(payload any) bool) (payload any, dropped bool)
```

DropTarget makes the previous widget a place a drag can end. While an accepted payload hovers it is outlined in the accent colour; on the frame the pointer is released there it reports the payload dropped. accept may be nil to take anything.

<a id="Context.DropTargetRect"></a>

#### Context.DropTargetRect

```go
func (c *Context) DropTargetRect(label string, r Rect, accept func(payload any) bool) (payload any, dropped bool)
```

DropTargetRect is DropTarget for an explicit rectangle, such as a cell of an inventory grid drawn without widgets.

<a id="Context.Dropdown"></a>

#### Context.Dropdown

```go
func (c *Context) Dropdown(label string, selected *int, options []string) bool
```

Dropdown shows the selected option and opens a list on click; it reports a change to \*selected. While the list is open the arrows move through it, Enter chooses and Escape closes it.

<a id="Context.IconButton"></a>

#### Context.IconButton

```go
func (c *Context) IconButton(icon gfx.Region, label string) bool
```

IconButton is a Button with an icon before its label.

<a id="Context.Image"></a>

#### Context.Image

```go
func (c *Context) Image(tex *gfx.Texture, w, h float32)
```

Image shows a texture at a size; zero size means the texture's own, fitted to the width available.

<a id="Context.ImageRegion"></a>

#### Context.ImageRegion

```go
func (c *Context) ImageRegion(reg gfx.Region, w, h float32)
```

ImageRegion shows a region of a texture at a size, keeping its aspect ratio when only one of w and h is given.

<a id="Context.IntSlider"></a>

#### Context.IntSlider

```go
func (c *Context) IntSlider(label string, value *int, lo, hi int) bool
```

IntSlider drags \*value across \[lo, hi] in whole steps; while focused the left and right arrows (or the d-pad) step it by one.

<a id="Context.Label"></a>

#### Context.Label

```go
func (c *Context) Label(text string)
```

Label draws text, wrapped to the width available to it.

<a id="Context.ListBox"></a>

#### Context.ListBox

```go
func (c *Context) ListBox(label string, height float32, items []string, selected *int) bool
```

ListBox shows items in a scrolling box of the given height with one selected; clicking selects and reports a change. selected may be -1. The list is one Tab stop: the arrows, Home, End, PageUp and PageDown move through it, Enter selects, and the focused row scrolls into view.

<a id="Context.Menu"></a>

#### Context.Menu

```go
func (c *Context) Menu(label string, body func())
```

Menu is one heading in a MenuBar; clicking it opens a list beneath in which body's MenuItem calls appear. The list closes when an item is chosen or the pointer clicks elsewhere.

<a id="Context.MenuBar"></a>

#### Context.MenuBar

```go
func (c *Context) MenuBar(r Rect, body func())
```

MenuBar lays a row of menus across r; body calls Menu for each.

<a id="Context.MenuItem"></a>

#### Context.MenuItem

```go
func (c *Context) MenuItem(label string) bool
```

MenuItem is a choice in an open Menu; it reports a click and closes the menu.

<a id="Context.Modal"></a>

#### Context.Modal

```go
func (c *Context) Modal(title string, r Rect, open *bool, body func())
```

Modal dims everything and draws a panel above it that alone takes input while open; body builds its contents. Pass the same open flag each frame and clear it to close. Its body runs later in Begin, after ordinary widgets. Once submitted, it blocks subsequent background widgets; on following frames it owns input from the first widget. Submit a newly opened modal before background controls to block them in that opening frame too. The game must separately suppress its own input while the modal is open.

<a id="Context.Panel"></a>

#### Context.Panel

```go
func (c *Context) Panel(title string, r Rect, body func())
```

Panel draws a titled box at r and lays out the widgets body creates inside it, top to bottom.

<a id="Context.Progress"></a>

#### Context.Progress

```go
func (c *Context) Progress(label string, t float32)
```

Progress draws a bar filled to t in \[0,1].

<a id="Context.Radio"></a>

#### Context.Radio

```go
func (c *Context) Radio(label string, value *int, option int) bool
```

Radio is one button of a group: it sets \*value to option when clicked and shows filled while they match. RadioGroup stacks several.

<a id="Context.RadioGroup"></a>

#### Context.RadioGroup

```go
func (c *Context) RadioGroup(value *int, options []string) bool
```

RadioGroup stacks a radio button per option and reports a change. The group is one Tab stop; the arrows move between its buttons.

<a id="Context.ReorderableList"></a>

#### Context.ReorderableList

```go
func (c *Context) ReorderableList(label string, items []string, height float32) (from, to int, moved bool)
```

ReorderableList shows items in a scrolling box of the given height where a row can be dragged to a new place, with a marker showing where it will land. It reports that the item at from should move to index to; the caller applies the move, for instance with Move. With a row focused, Ctrl or Cmd with Up or Down moves it a step.

<a id="Context.RichLabel"></a>

#### Context.RichLabel

```go
func (c *Context) RichLabel(markup string) string
```

RichLabel draws markup (see gfx.ParseRich) wrapped to the width available, in the theme's regular, bold and italic fonts, and returns the name of a link clicked this frame, or "". The markup is parsed and laid out once and kept for as long as the interface keeps drawing it, so the same label every frame costs a map lookup.

<a id="Context.Row"></a>

#### Context.Row

```go
func (c *Context) Row(n int, body func())
```

Row lays the widgets body creates side by side, n of equal width.

<a id="Context.ScrollArea"></a>

#### Context.ScrollArea

```go
func (c *Context) ScrollArea(label string, r Rect, contentHeight float32, contents func())
```

ScrollArea lays out its contents inside r with a vertical scrollbar, clipping what does not fit; contentHeight is the total height the contents need. The scroll position is kept per label across frames.

<a id="Context.Separator"></a>

#### Context.Separator

```go
func (c *Context) Separator()
```

Separator draws a thin line.

<a id="Context.Slider"></a>

#### Context.Slider

```go
func (c *Context) Slider(label string, value *float32, lo, hi float32) bool
```

Slider drags \*value across \[lo, hi] and reports a change. While focused, the left and right arrows (or the d-pad) step it by a twentieth of the range.

<a id="Context.Space"></a>

#### Context.Space

```go
func (c *Context) Space(h float32)
```

Space leaves a vertical gap.

<a id="Context.Spinner"></a>

#### Context.Spinner

```go
func (c *Context) Spinner(label string, value *int, lo, hi, step int) bool
```

Spinner shows \*value between minus and plus buttons that step it within \[lo, hi]; the label sits before it. The value is the Tab stop; while focused the left and right arrows (or the d-pad) step it.

<a id="Context.Table"></a>

#### Context.Table

```go
func (c *Context) Table(columns []string, weights []float32, rows int, cell func(row, col int)) (clicked int)
```

Table lays out a header row and rows of cells in columns; weights give the columns' relative widths (nil for equal) and cell draws each cell with the usual widgets. Rows alternate in shade. The rows are one Tab stop that the arrows move through; it returns the row clicked or activated with Enter this frame, or -1. Widgets inside cells are their own Tab stops, and their identity is scoped to their row.

Rows outside the clip the table is drawn under, such as the rows of a long table scrolled out of a ScrollArea, keep their place, their Tab stop and their accessibility entry, but cell is not called for them, so a thousand-row table costs about what its visible rows do. A row holding the focused or held widget is always built, so scrolling it out of view keeps the focus. A skipped row takes the height it had when it was last built, or a row of the theme's height before that.

<a id="Context.Tabs"></a>

#### Context.Tabs

```go
func (c *Context) Tabs(labels []string, selected *int) bool
```

Tabs draws a row of tabs and keeps \*selected on the clicked one, reporting a change; the widgets that follow belong to that tab. The row is one Tab stop: the left and right arrows move along it and Enter selects.

<a id="Context.TextArea"></a>

#### Context.TextArea

```go
func (c *Context) TextArea(label string, value *string, height float32) bool
```

TextArea edits \*value over several wrapped lines in a box of the given height, with the same keys as TextField plus Up, Down and Enter for a new line; it scrolls to keep the caret in view. An open modal releases focus from areas outside it. Tab and gamepad navigation transfer text focus while preserving each area's caret and selection.

<a id="Context.TextField"></a>

#### Context.TextField

```go
func (c *Context) TextField(label string, value *string) bool
```

TextField edits \*value on one line while focused and reports a change. It has a caret, a selection (Shift with the arrows, or a drag), Home and End, word jumps with Ctrl or Cmd and the arrows, select all, cut, copy and paste through the Clipboard, and undo and redo (Ctrl or Cmd with Z, Shift+Z or Y). Enter and Escape drop focus. An open modal releases focus from fields outside it. Tab and gamepad navigation transfer text focus while preserving each field's caret and selection.

<a id="Context.Tooltip"></a>

#### Context.Tooltip

```go
func (c *Context) Tooltip(text string)
```

Tooltip shows text near the pointer when it has rested on the previous widget for a moment.

<a id="Context.Tree"></a>

#### Context.Tree

```go
func (c *Context) Tree(label string, body func())
```

Tree draws a collapsible node: a triangle and a label that toggle on click, with body's widgets indented beneath while open. Nodes start closed; TreeOpen starts them open.

<a id="Context.TreeOpen"></a>

#### Context.TreeOpen

```go
func (c *Context) TreeOpen(label string, body func())
```

TreeOpen is Tree starting open.

<a id="Context.WantsKeyboard"></a>

#### Context.WantsKeyboard

```go
func (c *Context) WantsKeyboard() bool
```

WantsKeyboard reports whether a text editor has focus after the most recent Begin. It does not report navigation focus or a modal without a focused editor; gate gameplay separately while a modal is open.

<a id="Context.WantsMouse"></a>

#### Context.WantsMouse

```go
func (c *Context) WantsMouse() bool
```

WantsMouse reports whether the pointer is over a panel or a drag is in progress, so the game can ignore clicks the interface consumed.

<a id="Context.Window"></a>

#### Context.Window

```go
func (c *Context) Window(title string, r *Rect, body func())
```

Window is a panel the user can move by its title bar and resize by its bottom-right corner; the rectangle it updates is the caller's.

<a id="Palette"></a>

<a id="Palette.Background"></a>

<a id="Palette.Surface"></a>

<a id="Palette.Border"></a>

<a id="Palette.Text"></a>

<a id="Palette.TextDim"></a>

<a id="Palette.Accent"></a>

<a id="Palette.Title"></a>

### Palette

```go
type Palette struct {
	Background gfx.Color // panels
	Surface    gfx.Color // buttons and fields sit on this
	Border     gfx.Color // panel and control outlines
	Text       gfx.Color // ordinary text
	TextDim    gfx.Color // secondary text and placeholders
	Accent     gfx.Color // sliders, focus, checks
	Title      gfx.Color // panel titles
}
```

Palette is the handful of colours a theme is derived from. Build one with a game's art in mind and pass it to FromPalette.

<a id="Rect"></a>

### Rect

```go
type Rect = lin.Rect
```

Rect is an axis-aligned box in view units: lin.Rect under a short name.

<a id="Anchored"></a>

#### Anchored

```go
func Anchored(area Rect, a Anchor, w, h, margin float32) Rect
```

Anchored places a w by h rectangle at an anchor of area, margin units in from the edges: a panel that stays in the corner whatever the window's size.

<a id="Split"></a>

#### Split

```go
func Split(area Rect, t float32, gap float32) (first, second Rect)
```

Split cuts area into two along its longer side at fraction t, for a sidebar and a main view.

<a id="Stretched"></a>

#### Stretched

```go
func Stretched(area Rect, left, top, right, bottom float32) Rect
```

Stretched fills area with margins on every side, for a panel that grows with the window.

<a id="Skin"></a>

<a id="Skin.Panel"></a>

<a id="Skin.Button"></a>

<a id="Skin.ButtonHover"></a>

<a id="Skin.ButtonActive"></a>

<a id="Skin.Field"></a>

<a id="Skin.FieldFocus"></a>

<a id="Skin.Check"></a>

<a id="Skin.CheckOn"></a>

<a id="Skin.Track"></a>

<a id="Skin.Fill"></a>

<a id="Skin.Knob"></a>

<a id="Skin.Thumb"></a>

### Skin

```go
type Skin struct {
	Panel        *Slice // panel background and border
	Button       *Slice // idle button
	ButtonHover  *Slice // hovered button; falls back to Button
	ButtonActive *Slice // held button; falls back to ButtonHover, then Button
	Field        *Slice // text fields and drop-down heads
	FieldFocus   *Slice // focused editor; falls back to Field
	Check        *Slice // checkbox box, unticked
	CheckOn      *Slice // checkbox box, ticked
	Track        *Slice // slider, progress and scrollbar tracks
	Fill         *Slice // the filled part of sliders and progress bars
	Knob         *Slice // slider handle
	Thumb        *Slice // scrollbar handle
}
```

Skin holds the art for each widget part. Any nil slice falls back to the theme's flat colours, so a skin can start with one button.

<a id="Slice"></a>

### Slice

```go
type Slice = gfx.NineSlice
```

Slice is a nine-slice texture. The corners keep their size, the edges stretch along one axis and the centre fills the rest, so one small image skins boxes of any size. It is gfx.NineSlice, so the same value draws outside the interface too.

<a id="Theme"></a>

<a id="Theme.Font"></a>

<a id="Theme.BoldFont"></a>

<a id="Theme.ItalicFont"></a>

<a id="Theme.BoldItalicFont"></a>

<a id="Theme.Text"></a>

<a id="Theme.TextDim"></a>

<a id="Theme.Panel"></a>

<a id="Theme.PanelBorder"></a>

<a id="Theme.Title"></a>

<a id="Theme.Button"></a>

<a id="Theme.ButtonHover"></a>

<a id="Theme.ButtonActive"></a>

<a id="Theme.Accent"></a>

<a id="Theme.Field"></a>

<a id="Theme.FieldBorder"></a>

<a id="Theme.Track"></a>

<a id="Theme.Padding"></a>

<a id="Theme.Spacing"></a>

<a id="Theme.BorderWidth"></a>

<a id="Theme.RowHeight"></a>

<a id="Theme.FocusWidth"></a>

<a id="Theme.Skin"></a>

### Theme

```go
type Theme struct {
	Font *gfx.Font // required default font; this Theme does not manage its lifetime
	// BoldFont, ItalicFont and BoldItalicFont serve RichLabel; nil falls
	// back to Font.
	BoldFont, ItalicFont, BoldItalicFont *gfx.Font

	Text         gfx.Color // ordinary labels and widget values
	TextDim      gfx.Color // secondary labels and placeholders
	Panel        gfx.Color // panel background
	PanelBorder  gfx.Color // panel outline and separators
	Title        gfx.Color // panel titles
	Button       gfx.Color // idle button fill
	ButtonHover  gfx.Color // hovered button fill
	ButtonActive gfx.Color // pressed button fill
	Accent       gfx.Color // selections, focus rings and filled controls
	Field        gfx.Color // editor background
	FieldBorder  gfx.Color // unfocused editor outline
	Track        gfx.Color // slider, progress and scrollbar background

	Padding     float32 // inside panels and buttons
	Spacing     float32 // between stacked widgets
	BorderWidth float32 // outline thickness in view units
	RowHeight   float32 // minimum widget height
	FocusWidth  float32 // the keyboard focus ring; zero means 2

	// Skin, when set, draws widgets from textures; colours above still
	// tint text and fill in for any slice the skin leaves nil.
	Skin *Skin
}
```

Theme is every colour and measure the widgets use. Measures are in view units. Start with a built-in theme or FromPalette; the zero Theme has no font and is not usable for text widgets through New; engine.Context.NewUI expands it into the built-in dark theme.

<a id="DarkTheme"></a>

#### DarkTheme

```go
func DarkTheme(font *gfx.Font) Theme
```

DarkTheme is the dark default; pass the font the interface should use.

<a id="FromPalette"></a>

#### FromPalette

```go
func FromPalette(font *gfx.Font, p Palette) Theme
```

FromPalette derives a full theme: buttons brighten as they are hovered and pressed, fields sit slightly below the background, tracks between.

<a id="LightTheme"></a>

#### LightTheme

```go
func LightTheme(font *gfx.Font) Theme
```

LightTheme is the light default.

<a id="NamedTheme"></a>

#### NamedTheme

```go
func NamedTheme(name string, font *gfx.Font) (theme Theme, ok bool)
```

NamedTheme builds a built-in theme by name; ok is false for an unknown name.

Example:

```go
package main

import (
	"github.com/matjam/bunyip/gfx"
	"github.com/matjam/bunyip/input"
	"github.com/matjam/bunyip/ui"
)

// In a game these come from engine.Context: ctx.Gfx and ctx.Input. The
// font is created once in Init.
var (
	g *gfx.Graphics

	font *gfx.Font
)

func main() {
	theme, _ := ui.NamedTheme("nord", font)
	theme.RowHeight = 32
	u := ui.New(g, theme)
	_ = u
}
```
