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.
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:
| Part | Colours, in order |
|---|---|
steps | well (fields, code), bg0 (the page), bg1 (bars), bg2 (panels, cards), bg3 (raised: buttons), line |
texts | faint, weak, text, bright |
hues | accent, 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.
| Procedure | What 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_NAMES | the modes by name: "system", "dark", "light" |
finish_of(name), FINISH_NAMES | the 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 = #b58900Finishes
| Finish | Raised surfaces (buttons, bars) | Sunken ones (fields) | Separators |
|---|---|---|---|
Flat | plain colour | plain colour, thin edge | a line |
Gradient | vertical gradient, a highlight line, engraved text | plain | a line |
Glass | a glossy upper half, a white rim | plain | a line |
Machined | bevelled: a soft shadow, a lit rim, a dark edge; pressed, it sinks | a well: inner shadow, lit lip, a ring of the accent when focused | a 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:
| Procedure | What 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.