gui_toolkit

prefs_page: a settings page from the preferences

import prefs_page "gt:prefs_page" (the package is named tk_prefs_page). It builds a program's settings from its preference registry (config.md), with no code per preference, in the look of opencode native's settings.

Odin
page: prefs_page.Page                         // kept between frames; prefs_page.destroy(&page) at the end

ui.next({width = ui.fill(), height = ui.fill()})
prefs_page.build(&page, &prefs, {
	user    = app,
	pages   = {{category = "appearance", title = "Appearance", icon = ICO_PALETTE}, {category = "history", title = "History", icon = ICO_HISTORY}},
	icon_font = app.icons, search_icon = ICO_SEARCH, reset_icon = ICO_RESET,
	changed = proc(user: rawptr, name: string) {   // a value was set or reset
		app := (^App)(user)
		config.save_user(&app.prefs, prefs_file())
		apply(app)
	},
	custom_names = {"look.theme"},
	custom  = proc(user: rawptr, name: string, em: f32) {   // the program's own control, under the words
		if at := prefs_page.theme_cards(themes, current, light, em); at >= 0 { /* set look.theme */ }
	},
})

What it shows

  • On the left: the search in a well, then the pages, each a button with its icon and its title; the page shown is pressed in. A page is a category of the registry (Def.category, or the first segment of the name). Options.pages gives their order, titles and icons; without it, each category shows by name. A page narrower than 40 ems shows the icons alone. Options.foot is a faint line under the pages (where the file is).
  • On the right: the page's title, then a line per preference, in the order the program registered them: its title (config.title_of) and its description (with "Locked by a policy", "Set on the command line for this run" or "Takes effect at the next start" when it applies) on the left, its control on the right, a reset button while the user's value is set, a carved line under it.
  • While a search is typed (title, name or description: config.matches): what matches in every page, under each page's title.

The controls:

PreferenceControl
Boola switch
String with up to 4 choices (its own choices, or the program's Options.choices)a segmented choice; under the words when it takes more than 45 % of the line
String with more choicesa list
String with no choicesa field
Int bounded with at most 200 steps, Float boundeda slider (a round step: 0.1, 1, 10...), its unit after its value
other numbers, sizes in bytesa field, its unit after it
a name in Options.custom_namesthe program's own (Options.custom), under the words

A locked preference (a policy, the command line) shows its control disabled.

Changes

A value goes through config.set_from_string at the User layer: every observer of the registry runs. A field applies its text on Enter or when it loses the keyboard; a refused value stays as it was, and the reason shows under the line ("out of range: 1 to 64 KB"). A slider applies its value when the drag ends, or at each key. Options.changed runs after each value set or reset: save the file there (config.save_user) and apply the change.

Themes as cards

theme_cards(list, current, light, em) draws the themes of list three to a row, each in its own colours (its version light): its surface, its name in its text, its hues as dots, a ring of its accent around the one named current. It returns the index of the one clicked, or -1.

Keyboard

Tab goes through the search, the pages and the controls. focus_search(&page) gives the search the keyboard at the next build (a program opens the page with it). editing(&page) says whether a field has the keyboard: a program that closes the page on Escape leaves that Escape to the field.

Edit this page on GitHub