type Model {
rows: Str[dyn]
loading: Bool
}
type Msg {
Refresh
Loaded { rows: Str[dyn] }
}
func view(model: Model): ui.View {
mut ui.View[dyn] kids = [ui.button([ui.on(Msg.Refresh())], "Refresh")]
if model.loading {
append(kids, ui.spinner([]))
}
for each row in model.rows {
append(kids, ui.text([], row))
}
return ui.column([ui.pad(24), ui.gap(8)], kids)
}
proc update(model: mut Model, msg: Msg, window: Address): Msg {
if msg is Msg.Refresh {
async reload(window) // off it goes; nothing to hold
model.loading = true // ...and the spinner starts now
} else if msg is Msg.Loaded(rows) {
model = Model(rows, false) // it comes back as a message
}
return msg
}
proc reload(window: Address): void {
hive.task.sleep(400)
async window(Msg.Loaded(["north", "south"]))
}
proc main(): void {
spawn(ui.window("Colonies", view, update), Model([], false))
}The same tree, shown by a window, becomes a live interface. hive.ui.window(title, view, update) is a service's handler, and spawn opens it with the state it starts from:
view is a func(Model): View — a func, so drawing can never change anything — called again after every turn.update is a proc(mut Model, Msg): Msg, writing the model in place for each event. One that takes a third parameter, window: Address, is handed the window's own address every turn. What it answers with goes back to a request from another node, and is dropped for the window's own events.on(msg) for a click and onDismiss(msg) for a click on an overlay's backdrop, and a function to one — like Msg.Changed(_) — for events that report something: onInput, onSubmit and onChoose a Str, onToggle a Bool, onSort a column's index, and onPick a table row's, the header not counted. A link in a window sends its message rather than following its href, and a served page keeps only a path, http, https or mailto link.Opening a window does not wait for it, so a program may open several. It is a real window rather than a tab — an application-mode browser window on a profile of its own, none of anybody's session or extensions in it — and a machine with no Chromium-family browser falls back to a tab. Closing a window kills its service, closing the last ends the program, and a main that returns while one is open waits for them.
A window needs no command type for work that must not block the screen: update is a proc, so it may start work with async, and given its own address that work can send its answer back as an ordinary message. The program's icon is assets/icon.png beside the entrypoint, a PNG of at most 256×256, and its typeface assets/font.woff2: nothing in the source names either, the whole assets/ folder is built into the executable, and on Windows the executable carries the icon too, so a file manager and a pinned shortcut show it. HIVE_WINDOW=print prints the window's address, hive-window <url>, instead of opening a browser, for whatever else is going to show the page.