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#

fn button(win, title, x, y, w, h, onclick)

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#

fn cancel(t)

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#

fn checkbox(win, title, x, y, w, h, onchange)

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#

fn checked(widget)

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#

fn every(ms, handler)

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#

fn input(win, text, x, y, w, h, onsubmit)

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#

fn label(win, text, x, y, w, h)

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#

fn on_close(win, handler)

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#

fn place(widget, x, y, w, h)

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#

fn quit()

quit stops run() after the current event-loop turn.

Source lib/ui.mu:408
fn quit() {
    _ui["running"] = false
    return nil
}

remove#

fn remove(widget)

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#

fn 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#

fn set_checked(widget, v)

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#

fn set_text(widget, 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#

fn text(widget)

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#

fn textarea(win, content, x, y, w, h)

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#

fn vstack(win, x, y, w, rowh, gap, items)

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#

fn window(title, w, h)

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