module ui
module lib/ui.mu
import "ui"
ui is a minimal native GUI toolkit: windows, labels, buttons, text inputs, checkboxes, multi-line textareas and repeating timers, laid out in pixels from the top-left corner and driven by an event loop that dispatches to µ closures. The API says nothing platform-shaped; behind it sit two backends chosen by platform() — Cocoa through objc on macOS (ui_cocoa), and the X11 wire protocol through x11 on Linux (ui_x11), which covers X.org sessions and, via Xwayland, every Wayland desktop.
A widget is a map handle: {"kind", "win", ...}. Handlers receive the widget they were attached to, and run on the one event loop run() owns — there is no other thread.
import "ui"
w := ui.window("Counter", 320, 120)
n := {"count": 0}
lbl := ui.label(w, "0", 20, 20, 280, 24)
ui.button(w, "+1", 20, 60, 130, 32, fn(_) {
n["count"] = n["count"] + 1
ui.set_text(lbl, str(n["count"]))
})
ui.run()
run() returns when every window has been closed or quit() is called.
Imports
Functions
button#
button places a push button in a window; onclick(widget) fires on click. Returns a widget handle, or an error.
Source lib/ui.mu:105
fn button(win, title, x, y, w, h, onclick) {
err := _require_window(win, "ui.button")
if err != nil {
return err
}
f := _ui["be"]["button"]
return f(_ui, win, title, x, y, w, h, onclick)
}cancel#
cancel stops a timer made by every().
Source lib/ui.mu:264
fn cancel(t) {
err := _require_kind(t, "timer", "ui.cancel")
if err != nil {
return err
}
t["dead"] = true
return nil
}checkbox#
checkbox places a toggle in a window; onchange(widget) fires when it is clicked (pass nil for none). Read it with checked(), set it with set_checked(). Returns a widget handle, or an error.
Source lib/ui.mu:179
fn checkbox(win, title, x, y, w, h, onchange) {
err := _require_window(win, "ui.checkbox")
if err != nil {
return err
}
f := _ui["be"]["checkbox"]
return f(_ui, win, title, x, y, w, h, onchange)
}checked#
checked answers whether a checkbox is ticked.
Source lib/ui.mu:192
fn checked(widget) {
err := _require_kind(widget, "checkbox", "ui.checked")
if err != nil {
return err
}
f := _ui["be"]["get_checked"]
return f(_ui, widget)
}every#
every schedules handler(timer) to run on the event loop every ms milliseconds, starting one interval from now. Returns a timer handle for cancel(). Timers only fire while run() is running, and do not keep it alive once every window has closed.
Source lib/ui.mu:252
fn every(ms, handler) {
if type(ms) != "INTEGER" || ms < 1 {
return error("ui.every expects a positive integer millisecond interval")
}
t := {"kind": "timer", "ms": ms, "fn": handler, "next": time.now_ms() + ms, "dead": false}
_ui["timers"] = append(_ui["timers"], t)
return t
}input#
input places an editable text field in a window; onsubmit(widget) fires when Return is pressed in it (pass nil for none). Returns a widget handle, or an error.
Source lib/ui.mu:120
fn input(win, text, x, y, w, h, onsubmit) {
err := _require_window(win, "ui.input")
if err != nil {
return err
}
f := _ui["be"]["input"]
return f(_ui, win, text, x, y, w, h, onsubmit)
}label#
label places a static text label in a window. Coordinates are pixels from the window's top-left corner. Returns a widget handle, or an error.
Source lib/ui.mu:91
fn label(win, text, x, y, w, h) {
err := _require_window(win, "ui.label")
if err != nil {
return err
}
f := _ui["be"]["label"]
return f(_ui, win, text, x, y, w, h)
}on_close#
on_close registers handler(win) to fire once when the window is closed.
Source lib/ui.mu:330
fn on_close(win, handler) {
err := _require_window(win, "ui.on_close")
if err != nil {
return err
}
win["onclose"] = handler
return nil
}place#
place moves and resizes a view within its window (top-left coordinates).
Source lib/ui.mu:160
fn place(widget, x, y, w, h) {
err := _require_widget(widget, "ui.place")
if err != nil {
return err
}
if widget["kind"] == "window" {
return error("ui.place expects a view widget, got a window")
}
f := _ui["be"]["place"]
return f(_ui, widget, x, y, w, h)
}quit#
quit stops run() after the current event-loop turn.
Source lib/ui.mu:408
fn quit() {
_ui["running"] = false
return nil
}remove#
remove takes a view widget out of its window for good.
Source lib/ui.mu:232
fn remove(widget) {
err := _require_widget(widget, "ui.remove")
if err != nil {
return err
}
if widget["kind"] == "window" {
return error("ui.remove expects a view widget, got a window")
}
f := _ui["be"]["remove"]
return f(_ui, widget)
}run#
run shows every window and pumps the platform event loop until quit() is called or the last window closes.
Source lib/ui.mu:394
fn run() {
ok := _init()
if is_error(ok) {
return ok
}
_ui["running"] = true
f := _ui["be"]["run"]
return f(_ui)
}set_checked#
set_checked ticks or unticks a checkbox programmatically.
Source lib/ui.mu:205
fn set_checked(widget, v) {
err := _require_kind(widget, "checkbox", "ui.set_checked")
if err != nil {
return err
}
f := _ui["be"]["set_checked"]
return f(_ui, widget, v)
}set_text#
set_text replaces a widget's text: a button's title, a window's title, or the string value of a label or input.
Source lib/ui.mu:134
fn set_text(widget, text) {
err := _require_widget(widget, "ui.set_text")
if err != nil {
return err
}
f := _ui["be"]["set_text"]
return f(_ui, widget, text)
}text#
text returns a widget's current text.
Source lib/ui.mu:147
fn text(widget) {
err := _require_widget(widget, "ui.text")
if err != nil {
return err
}
f := _ui["be"]["get_text"]
return f(_ui, widget)
}textarea#
textarea places a multi-line editable text view (fixed size). Read and write it with text()/set_text(). Returns a widget handle, or an error.
Source lib/ui.mu:219
fn textarea(win, content, x, y, w, h) {
err := _require_window(win, "ui.textarea")
if err != nil {
return err
}
f := _ui["be"]["textarea"]
return f(_ui, win, content, x, y, w, h)
}vstack#
vstack lays out widgets top to bottom in one call. Each item is a map with a "kind" of "label", "button" or "input", its "text", and "onclick" for buttons / "onsubmit" for inputs. Rows start at (x, y), are w wide and rowh tall, gap apart. Returns the widget handles in item order, or an error.
Source lib/ui.mu:346
fn vstack(win, x, y, w, rowh, gap, items) {
err := _require_window(win, "ui.vstack")
if err != nil {
return err
}
out := []
cy := y
i := 0
while i < len(items) {
it := items[i]
made := nil
if it["kind"] == "label" {
made = label(win, it["text"], x, cy, w, rowh)
} else {
if it["kind"] == "button" {
made = button(win, it["text"], x, cy, w, rowh, it["onclick"])
} else {
if it["kind"] == "input" {
made = input(win, it["text"], x, cy, w, rowh, it["onsubmit"])
} else {
if it["kind"] == "checkbox" {
made = checkbox(win, it["text"], x, cy, w, rowh, it["onchange"])
} else {
return error("ui.vstack expects kind label/button/input/checkbox, got " + inspect(it["kind"]))
}
}
}
}
if is_error(made) {
return made
}
out = append(out, made)
cy = cy + rowh + gap
i = i + 1
}
return out
}window#
window creates a titled, closable window of the given content size. The window stays hidden until run() shows it. Returns a window handle, or an error.
Source lib/ui.mu:70
fn window(title, w, h) {
ok := _init()
if is_error(ok) {
return ok
}
f := _ui["be"]["window"]
handle := f(_ui, title, w, h)
if is_error(handle) {
return handle
}
_ui["windows"] = append(_ui["windows"], handle)
return handle
}Internal helpers
Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.
| _init() | — |
| _require_kind(widget, kind, who) | — |
| _require_widget(widget, who) | — |
| _require_window(win, who) | — |
| _tick() | — |
| _ui | — |