Luau Bindings — gui
The
require("gui")script API — a retained, event-driven UI layer over immediate-modeimgui. Create widgets as objects, set their properties, attach event handlers;guimaintains the immediate-mode frame for you. This is the friendly default for scripts and user programs — rawimguistays for AntOS internals, power users, and dynamic UIs. Every function is[antos]. Design & native core: antos_gui.
Design-stage.
guiis a proposed convenience layer built on the (also design-stage)imguiwindowing system. The shape below is the intended API.
local gui = require("gui")
local win = gui.window("Counter")
local lbl = win:label("count: 0")
local n = 0
win:button("Add").on_click = function()
n = n + 1
lbl.text = "count: " .. n -- just set a property; the UI updates itself
end
gui.run() -- event loop on *this* (your) thread
Your code runs on your own thread — gui marshals widget changes to the render thread and delivers events back to you. You never write a per-frame draw, and a slow handler stalls only your program, not the desktop.
Windows & widgets
| Call | Returns |
|---|---|
gui.window(title [, opts]) |
A window object. opts.context targets "local" / "remote" / "both" (see below). |
win:label(text) · win:button(text) |
A label / button widget. |
win:checkbox(text, value) · win:input(label, value) |
A checkbox / text field. |
win:slider(label, value, min, max) · win:combo(label, items, i) |
A slider / drop-down. |
win:row{...} · win:column{...} · win:group(title) |
Layout containers. |
win:menu(title) → menu:item(label) |
A window menu bar (see imgui menus). |
Properties (retained, mutable, thread-safe)
Set a property and the widget updates on the next frame — no redraw call:
| Property | On |
|---|---|
.text |
labels, buttons, inputs |
.value |
checkboxes, sliders, inputs, combos |
.enabled · .visible |
any widget |
Events
Assign a handler; gui calls it (on your thread) when it fires:
| Event | Fires |
|---|---|
.on_click |
buttons, menu items |
.on_change |
checkboxes, sliders, inputs, combos |
win.on_close |
the window is closed |
The loop
| Function | Behaviour |
|---|---|
gui.run() |
Run the event loop on your thread — dispatch events to handlers until all windows close. |
gui.quit() |
Stop the loop. |
Dialogs
A message box blocks your thread by default and returns the pressed button — the render thread keeps drawing it, so the modal stays live while your code waits, exactly like a normal desktop dialog. This is safe precisely because your code runs on its own thread, not the render thread.
| Call | Behaviour |
|---|---|
gui.message_box(title, text [, buttons]) |
Show a modal and block until answered → the pressed button. |
gui.message_box(title, text, buttons, on_result) |
Non-blocking — returns at once; on_result(button) fires when answered. |
if gui.message_box("Quit?", "Save before quitting?", {"Save","Discard","Cancel"}) == "Save" then
save()
end
Raw
imgui'smessage_boxcan't block — its draw is on the render thread — so it polls instead. Blocking isgui's to offer because aguiprogram has its own thread to block.
Where it renders — local, remote, or both
Like an imgui window, a gui window targets the local desktop, the netImgui context (a connected PC), or both — gui reconciles its widget tree into each active context.
| Call | Effect |
|---|---|
gui.window(title, { context = "local" }) |
Local desktop only. |
gui.window(title, { context = "remote" }) |
Only on a connected PC over netImgui — nothing locally. |
gui.window(title, { context = "both" }) |
Both — appears on the PC when one connects. |
gui.window(title, { context = "panel" }) |
The keyboard touch panel — a portrait, ~230 PPI, 5-point-touch surface; its surface profile scales and target-floors the same widgets automatically. |
gui.context(default) |
Set the default context for windows created after (state it once per app). |
gui.contexts() |
Which are live — { local = true, panel = <present?>, remote = <connected?> }. |
gui.surface(name) |
A surface's profile — { scale, input = "pointer" or "touch", points, orientation }. |
Each surface carries a profile (scale from physical DPI, input mode, touch points, orientation), and gui reconciles the one tree into each through its profile — so a window on "panel" comes out finger-sized, portrait, and target-floored with no different code. On the panel's 5-point surface gui hit-tests each touch against the retained tree and dispatches concurrently, so several pads or faders respond at once (raw imgui's single mouse can't) — see surface profiles.
Because the tree is retained, this is cleaner than immediate mode: one widget, one handler, reconciled into every active context. A button on a "both" window is a single on_click — clicked on the machine or on the PC, gui delivers the one event to your thread once, no per-context bookkeeping.
guiusesimguiunderneath, so aguiwindow docks, floats, and mirrors to netImgui exactly like a raw imgui one — you just don't manage the frame. Reach for rawimguiwhen you want immediate-mode control (procedural or per-frame-dynamic UIs); reach forguifor a conventional app.
Related
- antos_gui — design, threading & native core · luau_imgui (the immediate-mode layer beneath) · AntOS Libraries hub