Modules

UI

moduleSource
macOSLinuxWindows

since 0.31.0

#import "Extensions/UI";

A native user interface library for jaic programs. A jaic extension, not official Jai: other Jai compilers have no such module, and it may change between jaic releases. See docs/extensions/ui.md.

Windows, rendering and text are our own: the window comes from Window_Creation, drawing from Extensions/WebGPU (Metal, Vulkan or D3D12 natively, the page's WebGPU in the playground) and text from FreeType into a glyph atlas. The widget tree is retained and keyed but declared with plain calls every frame, so the app owns its data and the library owns scroll offsets, focus and cursors. Containers are for blocks.

Example
#import "Basic";
#import "Extensions/UI";

App :: struct { count: int; }

main :: () {
    app: App;
    run_app(.{title = "Counter", width = 320, height = 200}, *app, build);
}

build :: (app: *App) {
    for column(.{gap = 8, padding = 16}) {
        label(tprint("Clicked % times", app.count));
        if button("Click me") app.count += 1;
    }
}

This one is only checked to compile, not run.

Show moreShow less

Tests drive the same build procedures without a window or a GPU (headless, frame, click, layout_dump).

Procedures#

Types#

rgbsince 0.31.0
(r: int, g: int, b: int, a: int = 255) -> Color

A colour from 0 to 255 channels.

hexsince 0.31.0
(value: u32, alpha: float = 1) -> Color

A colour from a 0xRRGGBB number.

containssince 0.31.0
(rect: Rect, x: float, y: float) -> bool

Whether the point lies inside rect.

intersectsince 0.31.0
(a: Rect, b: Rect) -> Rect

The overlap of two rectangles (zero-sized when they do not touch).

insetsince 0.31.0
(rect: Rect, amount: float) -> Rect

rect shrunk by amount on every side.

roundsince 0.31.0
(value: float) -> float

value rounded to the nearest whole number (halves go up).

Theme#

set_themesince 0.31.0
(theme: Theme)

Replace the theme of the UI being built.

themesince 0.31.0
() -> *Theme

The theme of the UI being built.

Text#

load_fontsince 0.31.0
(text: *Text_System, path: string) -> bool

Open the font file at path into the next slot. Returns false when it cannot be read or parsed.

load_font_datasince 0.31.0
(text: *Text_System, data: string, name: string = "") -> bool

Open a font from bytes (which the text system keeps and frees).

load_system_fontssince 0.31.0
(text: *Text_System)

Load the platform's usual fonts: a sans font into slot 0, a monospace font into slot 1, then a few fallbacks for symbols and CJK. Any that are missing are replaced by synthetic fonts for the first two slots. The environment variables JAI_UI_FONT and JAI_UI_MONO_FONT name font files that replace the sans and monospace choices.

set_scalesince 0.31.0
(text: *Text_System, scale: float)

Set the device scale; glyphs rasterized at another scale stay in the cache under their own size.

text_widthsince 0.31.0
(text: *Text_System, string_to_measure: string, style: Text_Style) -> float

The width in points of string drawn with style, without kerning.

index_at_xsince 0.31.0
(text: *Text_System, string_to_measure: string, style: Text_Style, x: float) -> int

The byte offset in string_to_measure of the grapheme boundary nearest to horizontal position x.

get_glyphsince 0.31.0
(text: *Text_System, code: u32, slot: Font_Slot, size: float) -> Glyph

The glyph for code in a font slot at size points, rasterized into the atlas on first use. Falls back through the later fonts when the slot's font has no glyph for it.

(text: *Text_System)

Forget the cached glyphs and clear the atlas, when it ran out of room. Called at the start of a frame.

Input#

primarysince 0.31.0
(mods: Modifiers) -> bool

Whether the platform's main shortcut modifier is held.

send_scrollsince 0.31.0
(ui: *UI_State, dx: float, dy: float)

Queue wheel or trackpad scrolling; positive dy scrolls the content up (towards its end).

send_textsince 0.31.0
(ui: *UI_State, text: string)

Queue typed text, one event per code point.

Tree#

begin_framesince 0.31.0
(ui: *UI_State)

Start a frame: reset the per-frame arena, clear the display list and open the root container.

end_framesince 0.31.0
(ui: *UI_State)

Finish the frame: drop nodes nobody declared, lay the tree out and fix focus.

framesince 0.31.0
(ui: *UI_State, app: *$T, build: (app: *T))

Run one frame with the app's build procedure.

push_uisince 0.31.0
(ui: *UI_State)

Make ui the current UI for the code that follows (a second window, or tests).

pop_uisince 0.31.0
()

Restore the UI that was current before the matching push_ui.

keysince 0.31.0
(value: $T)

Give the next widget an explicit key, for items in a list whose order changes. Without it a widget in a loop is identified by its position in the loop.

statesince 0.31.0
(node: *Node, $T: Type) -> *T

The retained value of type T stored on a node, created zeroed on first use and kept until the node leaves the tree.

widgetsince 0.31.0
(kind: Node_Kind, role: Role, style: Style, loc: Source_Code_Location) -> *Node

Declare a widget: find or create its node, make it the next child of the open container and return it. This is what the library's own widgets use; a custom widget calls it too.

begin_containersince 0.31.0
(axis: Axis, role: Role, style: Style, loc: Source_Code_Location) -> *Node

Open a container as the current parent; end_container closes it. Containers written with for do both.

frame_allocatorsince 0.31.0
(ui: *UI_State) -> Allocator

The allocator of the per-frame arena: memory that lives until the next frame begins.

frame_stringsince 0.31.0
(ui: *UI_State, text: string) -> string

A copy of text that lives until the next frame, for strings a node keeps.

request_framesince 0.31.0
()

Ask for another frame (animation, a blinking caret).

ui_timesince 0.31.0
() -> float64

The frame's current time in seconds.

Layout#

layout_treesince 0.31.0
(ui: *UI_State)

Measure and arrange the whole tree for the window's size. end_frame calls it; call it again only after changing a node by hand.

(ui: *UI_State)

A focusable widget with no room is reported once: it cannot be seen or clicked, and nothing else says why. The usual cause is a column or row that is not given grow, so its children get only their natural size and a widget that has none (a text editor) gets nothing. Called by end_frame when DEBUG_WARNINGS is on.

Draw#

draw_rectsince 0.31.0
(ui: *UI_State, rect: Rect, color: Color, radius: float = 0, border: float = 0, border_color: Color = .{})

Fill rect with color; radius rounds the corners and border draws an outline inside.

draw_textsince 0.31.0
(ui: *UI_State, x: float, y: float, text: string, style: Text_Style) -> float

Draw one line of text with its top left at (x, y) and return its width in points.

draw_text_baselinesince 0.31.0
(ui: *UI_State, x: float, baseline: float, text: string, style: Text_Style) -> float

Draw one line of text with its baseline at baseline, returning its width in points.

push_clipsince 0.31.0
(ui: *UI_State, rect: Rect)

Restrict drawing to rect (intersected with the clip already in force) until pop_clip.

Widgets#

for_expansion2 overloadssince 0.31.0
(container: Container, body: Code, flags: For_Flags)

Close the container when its for body is done.

rowsince 0.31.0
(style: Style = .{}, loc := #caller_location) -> Container

A container that lays its children out left to right.

columnsince 0.31.0
(style: Style = .{}, loc := #caller_location) -> Container

A container that lays its children out top to bottom.

stacksince 0.31.0
(style: Style = .{}, loc := #caller_location) -> Container

A container whose children overlap, each filling it (or placed by align).

panelsince 0.31.0
(style: Style = .{}, loc := #caller_location) -> Container

A column on the theme's panel colour.

scrollsince 0.31.0
(style: Style = .{}, axis: Axis = .VERTICAL, loc := #caller_location) -> Container

A column whose content scrolls vertically (horizontally with axis = .HORIZONTAL) when it is larger than the space it is given. Give it grow = 1 or a fixed size.

labelsince 0.31.0
(text: string, options: Text_Options = .{}, loc := #caller_location) -> *Node

One line of text.

buttonsince 0.31.0
(text: string, options: Button_Options = .{}, loc := #caller_location) -> clicked: bool

A push button. Returns true on the frame it was clicked (or Enter or Space pressed while focused).

checkboxsince 0.31.0
(value: *bool, text: string = "", loc := #caller_location) -> changed: bool

A checkbox with a caption. Returns true on the frame the value changed.

list_itemsince 0.31.0
(text: string, selected: bool = false, indent: float = 0, loc := #caller_location) -> clicked: bool

A row in a list or tree: highlighted when selected, indented by indent points. Returns true when clicked.

separatorsince 0.31.0
(loc := #caller_location)

A horizontal rule in a column (a vertical one in a row).

spacersince 0.31.0
(grow: float = 1, loc := #caller_location)

Empty space that grows to fill what is left in its row or column.

tabsince 0.31.0
(text: string, active: bool, options: Tab_Options = .{}, loc := #caller_location) -> Tab_Result

One tab in a tab_bar, drawn selected when active.

splitsince 0.31.0
(axis: Axis, position: *float, options: Split_Options = .{}, loc := #caller_location) -> Split

Two panes with a draggable divider. position is the first pane's size along axis (HORIZONTAL puts the panes side by side); the app keeps it so it can be saved. Use for pane: split(.HORIZONTAL, *width) { if pane == 0 { ... } else { ... } }.

dividersince 0.31.0
(s: Split, loc := #caller_location)

Draws and drags the divider between split panes.

Text edit#

set_contentssince 0.31.0
(edit: *Text_Edit, text: string)

Replace everything with text and put the caret at the start. History is cleared.

contentssince 0.31.0
(edit: *Text_Edit) -> string

The whole text, as a copy in temporary storage.

selection_rangesince 0.31.0
(edit: *Text_Edit) -> (start: int, end: int)

The selected range as (start, end) byte offsets.

caret_positionsince 0.31.0
(edit: *Text_Edit) -> (line: int, column: int)

Where the caret is, as a zero-based line and a byte column within it.

move_caretsince 0.31.0
(edit: *Text_Edit, offset: int, extend: bool = false)

Put the caret at offset (clamped), selecting from the old anchor when extend is set.

insert_textsince 0.31.0
(edit: *Text_Edit, text: string)

Replace the selection (or insert at the caret) with text.

offset_bysince 0.31.0
(edit: *Text_Edit, from: int, unit: Unit, direction: int) -> int

Offset one unit to the left (direction -1) or right (1) of from.

move_bysince 0.31.0
(edit: *Text_Edit, unit: Unit, direction: int, extend: bool = false)

Move the caret one unit left or right.

delete_bysince 0.31.0
(edit: *Text_Edit, unit: Unit, direction: int)

Delete one unit before (direction -1) or after (1) the caret, or the selection if there is one.

select_line_atsince 0.31.0
(edit: *Text_Edit, offset: int)

Select the whole line containing offset, including its line break.

undo_editsince 0.31.0
(edit: *Text_Edit) -> bool

Undo the last step and put the caret where it belongs. Returns whether anything changed.

redo_editsince 0.31.0
(edit: *Text_Edit) -> bool

Redo the last undone step. Returns whether anything changed.

insert_tabsince 0.31.0
(edit: *Text_Edit, width: int)

Insert spaces up to the next multiple of width columns.

text_editorsince 0.31.0
(edit: *Text_Edit, options: Editor_Options = .{}, loc := #caller_location) -> changed: bool

A multi-line text editor over edit: line numbers, selection, scrolling, undo and the clipboard. Returns true on the frame the text changed. It fills the space it is given.

#import "Basic";
#import "Extensions/UI";

Notes :: struct {
    edit: Text_Edit;
    dirty: bool;
}

main :: () {
    notes: Notes;
    run_app(.{title = "Notes", width = 480, height = 320}, *notes, build);
}

build :: (notes: *Notes) {
    if text_editor(*notes.edit, .{line_numbers = true}) notes.dirty = true;
}

This one is only checked to compile, not run.

text_fieldsince 0.31.0
(edit: *Text_Edit, options: Editor_Options = .{}, loc := #caller_location) -> (changed: bool, submitted: bool)

A single-line text field over edit. Returns whether the text changed and whether Enter was pressed.

wake_insince 0.31.0
(ui: *UI_State, seconds: float64)

Ask for a frame in seconds even if no input arrives (animation, a blinking caret).

Headless#

headlesssince 0.31.0
(options: Headless_Options = .{}) -> UI_State

A UI that is not attached to a window. Fonts are synthetic (every character is 0.6 of the font size wide) unless system_fonts is set, so layouts are the same on every machine. Release it with free_ui.

resizesince 0.31.0
(ui: *UI_State, width: float, height: float)

Change the logical size of the window a UI draws into.

clicksince 0.31.0
(ui: *UI_State, node: *Node)

Move the pointer to the centre of node and click it. The next frame applies it and shows the result. node must come from a frame that has already laid it out.

click_atsince 0.31.0
(ui: *UI_State, x: float, y: float)

Click at a point in the window.

layout_dumpsince 0.31.0
(ui: *UI_State) -> string

The tree as indented text: role, label or text, and the rectangle of each node. Useful as a snapshot of a layout.

Render#

(window: Window_Type, width: int, height: int) -> *Renderer

Create a renderer drawing into window's surface. Returns null if no GPU or surface is available.

(width: int, height: int) -> *Renderer

Create a renderer drawing into an offscreen texture of width by height pixels, for tests and screenshots. Returns null if no adapter is available.

resize_targetsince 0.31.0
(r: *Renderer, width: int, height: int)

Match the target to a new size in pixels (a window resize).

presentsince 0.31.0
(r: *Renderer, ui: *UI_State, clear: Color) -> bool

Draw the UI's display list into the window. Returns false when there was nothing to draw into (the window is hidden) and the frame should be tried again.

write_screenshotsince 0.31.0
(ui: *UI_State, path: string) -> bool

Draw the UI's last frame offscreen and write it to path as a PNG. Needs a GPU; build the UI with system_fonts for readable text. Returns false when there is no adapter or the file cannot be written.

App#

run_appsince 0.31.0
(options: App_Options, app: *$T, build: (app: *T)) -> bool

Open a window and run build for every frame until the window closes. app is passed to build each frame. Returns false when the window or the GPU could not be set up.

#import "Basic";
#import "Extensions/UI";

Counter :: struct {
    count: int;
}

main :: () {
    app: Counter;
    run_app(.{title = "Counter", width = 320, height = 200}, *app, build);
}

build :: (app: *Counter) {
    if button(tprint("Clicked % times", app.count)) app.count += 1;
}

This one is only checked to compile, not run.

run_app_withsince 0.31.0
(options: App_Options, ui: *UI_State, app: *$T, build: (app: *T)) -> bool

Like run_app, for a UI the caller owns (to set the theme or hooks first, or to inspect it after).

Types#

Rectstructsince 0.31.0
:: struct

An axis-aligned rectangle: x, y is the top left corner.

Axisenumsince 0.31.0
:: enum

Which way a container lays out its children.

Alignenumsince 0.31.0
:: enum

How a container places children across its axis.

Justifyenumsince 0.31.0
:: enum

How a container places children along its axis when none of them grow.

Roleenumsince 0.31.0
:: enum

What a widget is, kept on its node for tests, for tools and for a later accessibility export.

Stylestructsince 0.31.0
:: struct

Layout and paint options for a container or widget. Zero values mean "automatic" or "none", so a style is written with only what differs: .{gap = 8, padding = 12}.

Theme_Modeenumsince 0.31.0
:: enum

Light or dark defaults for default_theme.

Themestructsince 0.31.0
:: struct

The look of the library's widgets. Change fields on ui.theme (or set_theme) to restyle everything.

Font_Slotenumsince 0.31.0
:: enum

Which font a piece of text uses.

Text_Stylestructsince 0.31.0
:: struct

How text is drawn.

Fontstructsince 0.31.0
:: struct

A loaded font file. Created by load_font and owned by the Text_System.

Glyphstructsince 0.31.0
:: struct

Where a glyph is in the atlas and how to place it.

Font_Metricsstructsince 0.31.0
:: struct

Vertical metrics in points.

Atlasstructsince 0.31.0
:: struct

The glyph atlas: a one-channel texture filled row by row, uploaded by the renderer when version changes.

Text_Systemstructsince 0.31.0
:: struct

Everything text needs: fonts, the glyph cache and the atlas.

Modifiersstructsince 0.31.0
:: struct

Which modifier keys are held. primary is Cmd on macOS and Ctrl elsewhere.

Input_Kindenumsince 0.31.0
:: enum

What an input event is.

Input_Eventstructsince 0.31.0
:: struct

A queued input event. Keys use the Input module's Key_Code (letters are their upper case ASCII).

Input_Statestructsince 0.31.0
:: struct

The pointer, modifiers and the keyboard events a frame sees.

Node_Kindenumsince 0.31.0
:: enum

How a node lays out and paints.

Paint_Proctypesince 0.31.0
:: #type (ui: *UI_State, node: *Node)

A procedure that paints a node's rectangle. It may use ui.draw freely.

Nodestructsince 0.31.0
:: struct

One widget in the tree. App code reads it through widget's result and the test helpers; the library fills it in.

Node_Eventsstructsince 0.31.0
:: struct

What happened to a node this frame, from the input events routed to it.

UI_Statestructsince 0.31.0
:: struct

All the state of one UI: its tree, fonts, input and the frame being drawn. Create one with headless or let run_app make it.

UI_Hooksstructsince 0.31.0
:: struct

Platform services the library needs but does not own, set by run_app. Tests leave them null.

Cursor_Kindenumsince 0.31.0
:: enum

The mouse cursor shapes widgets ask for.

Draw_Kindenumsince 0.31.0
:: enum

What a display list entry draws.

Draw_Cmdstructsince 0.31.0
:: struct

One display list entry.

Draw_Liststructsince 0.31.0
:: struct

The display list of a frame plus the clip stack used while building it.

Containerstructsince 0.31.0
:: struct

An open container, returned by row, column and the other containers so for can close it.

Text_Optionsstructsince 0.31.0
:: struct

Options for text: all optional.

Tab_Resultenumsince 0.31.0
:: enum

What a click on a tab did.

Tab_Optionsstructsince 0.31.0
:: struct

Options for tab.

Splitstructsince 0.31.0
:: struct

An open split: the two panes are visited with for pane: split(...).

Text_Editstructsince 0.31.0
:: struct

A piece of text being edited. Zero is empty; release it with free_text_edit.

Highlight_Spanstructsince 0.31.0
:: struct

A coloured range of one line, produced by an editor's highlight procedure.

Highlight_Proctypesince 0.31.0
:: #type (line: string, spans: *[..] Highlight_Span)

A procedure that colours one line of text by appending spans (unlisted text uses the default colour).

Text_Markstructsince 0.31.0
:: struct

A range of the text drawn with a background: search results, diagnostics, matching brackets.

Editor_Optionsstructsince 0.31.0
:: struct

Options for text_editor and text_field.

Unitenumsince 0.31.0
:: enum

What a movement or deletion covers.

Key_Resultstructsince 0.31.0
:: struct

What handle_key did.

Find_Querystructsince 0.31.0
:: struct

How find picks a node: every field that is set must match.

Rendererstructsince 0.31.0
:: struct

A renderer bound to one window or to an offscreen target.

Instance_Datastructsince 0.31.0
:: struct

One entry of the instance buffer, as the shader reads it.

Batchstructsince 0.31.0
:: struct

A run of instances drawn with one scissor rectangle.

Rendered_Imagestructsince 0.31.0
:: struct

Pixels read back from an offscreen render: rows top to bottom, 4 bytes per pixel (R, G, B, A).

App_Optionsstructsince 0.31.0
:: struct

Options for run_app.

App_Windowstructsince 0.31.0
:: struct

What run_app can tell the app about the window.

Constants#

Colorsince 0.31.0
:: Vector4

A colour with straight (not premultiplied) alpha, each channel 0 to 1.