gui_toolkit

Themes and finishes

A program shows one version (dark or light) of one theme, with one finish. The theme gives the colours, the finish how surfaces are painted. ui.theme_show applies the three and follows the system when it goes light or dark.

Odin
choice := ui.Theme_Choice{name = "Nord", mode = .System, finish = .Machined, glow = 1}
if ui.theme_show(&app.theme, ui.THEMES[:], choice, &app.shown) {
	platform.set_dark_title(app.win, !app.shown.light)   // the system's title bar, as the version shown
}

Call theme_show once per frame. It applies only when the choice changed, or, in the System mode, when platform.color_scheme_serial moved; otherwise it costs one hash.

A theme

ui.Theme_Def is a name and the colours of its versions. A version (ui.Theme_Colors) holds:

PartColours, in order
stepswell (fields, code), bg0 (the page), bg1 (bars), bg2 (panels, cards), bg3 (raised: buttons), line
textsfaint, weak, text, bright
huesaccent, blue, purple, green, yellow, red, cyan, rose

versions says which versions a theme has: Both, Dark_Only or Light_Only. A theme can impose its finish (finish: Aero is Glass, Bootstrap Gradient), let the page show the system's backdrop (translucent), and name the text on its accent (accent_text; 0 picks the darkest step or the brightest text, whichever reads better on it).

ui.THEMES lists the toolkit's themes; the first is the default. A program puts its own first and appends ui.THEMES[:], or shows only its own.

ProcedureWhat it does
theme_find(list, name)the theme of this name (ASCII case ignored), and its index
theme_version(t, light)the colours shown: the light version when asked and present, else the one there is
theme_palette(t, light, finish)the Palette of the built-in widgets for that version
theme_palettes(list, finish)every version of every theme as Named_Palette ("Nord", "Nord Light"): pickers, tests
palette_of(name, light)a palette of THEMES by name: examples and tests
theme_wants_light(mode)whether a mode shows the light version now (System: platform.dark_mode_preferred)
theme_mode_of(name), THEME_MODE_NAMESthe modes by name: "system", "dark", "light"
finish_of(name), FINISH_NAMESthe finishes by name: "flat", "gradient", "glass", "machined"

The palette takes, from a version: bg = bg0, bar = bg1, panel = bg2, panel2 = bg3, border = line, well = well, weak and text, accent, good = green, warn = yellow, bad = red.

A program's own colours in any theme

A program that draws with its own colours chooses them in one theme, its reference. theme_recolor(&ref, shown, c) moves a colour c of the reference to the theme shown. A grey goes to the same place on the shown theme's greys. A colour goes to the shown theme's hue nearest to it, mixed with the shown theme's grey as much as it was with the reference's. The alpha stays. theme_reference(colors) makes the reference from its colours; a program can set levels itself when its greys stand elsewhere. Cache the results: a program asks for the same few colours every frame.

A user's themes

themes_read(text, catalog, base) reads a file of the user's. Each [Name] section is one theme. It starts as base's dark version, with no light version; from = Name starts it from a theme of catalog instead. A line key = #rrggbb sets a colour; the keys are THEME_KEYS (well, bg0, bg1, bg2, bg3, line, faint, weak, text, bright, accent, blue, purple, green, yellow, red, cyan, rose). light. before a key sets the light version, which the theme then has. Free the list with themes_destroy.

[My theme]
from = Nord
accent = #e5c07b
light.accent = #b58900

Finishes

FinishRaised surfaces (buttons, bars)Sunken ones (fields)Separators
Flatplain colourplain colour, thin edgea line
Gradientvertical gradient, a highlight line, engraved textplaina line
Glassa glossy upper half, a white rimplaina line
Machinedbevelled: a soft shadow, a lit rim, a dark edge; pressed, it sinksa well: inner shadow, lit lip, a ring of the accent when focuseda carved groove

The machined reliefs are made for the dark: on a light palette, apply_palette makes the finish Flat. Theme.glow (0..1) sets the strength of its halos: a focused well, a lamp.

A box gets the machined drawing from its flags and its theme: .Groove is a groove (hline); .Sunken with a background is a well (line_edit, text_edit, field_frame, the track of segmented; a clear background sits in a well, as a bare field does); an opaque background with a sheen is raised (bevelled when it is .Clickable).

A program draws its own surfaces with the same primitives; outside the machined finish each one draws its flat form, so one call serves every finish:

ProcedureWhat it draws
machined_bevel(dc, r, radius, face, hot, pressed, glow)a raised face; glow: a halo under the pointer
machined_well(dc, r, radius, base, focus, accent)a sunken surface, its focus a ring of accent
machined_groove(dc, a, b, vertical)a carved line
machined_plate(dc, r, radius, top, bottom, shadow, rim, border, glow)a raised panel: cards, popovers
machined_lamp(dc, c, d, color, lit, glow)a status lamp: a glass bead and its halo
machined_pip(dc, c, d, color)a small hole with a dab of colour: what is over

What the machined finish draws reaches out of a box by up to MACHINED_OVERFLOW_PX logical pixels (a shadow, a halo, the lip of a well). A region redrawn on its own must hold that much around its boxes.

Edit this page on GitHub