A colour from 0 to 255 channels.
Whether the platform's main shortcut modifier is held.
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.
#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.
Tests drive the same build procedures without a window or a GPU (headless, frame, click,
layout_dump).
A colour from 0 to 255 channels.
A colour from a 0xRRGGBB number.
color with its alpha replaced.
Whether the point lies inside rect.
The overlap of two rectangles (zero-sized when they do not touch).
rect shrunk by amount on every side.
value rounded to the nearest whole number (halves go up).
The built-in theme for mode.
Replace the theme of the UI being built.
The theme of the UI being built.
Open the font file at path into the next slot. Returns false when it cannot be read or parsed.
Open a font from bytes (which the text system keeps and frees).
Add a fixed-metrics font (no glyphs are drawn) to the next slot.
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.
Release every font and the glyph cache.
Set the device scale; glyphs rasterized at another scale stay in the cache under their own size.
Ascent, descent and line height of a font slot at size points.
The width in points of string drawn with style, without kerning.
Horizontal advance of one code point in points (a tab is four spaces).
The byte offset in string_to_measure of the grapheme boundary nearest to horizontal position x.
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.
Forget the cached glyphs and clear the atlas, when it ran out of room. Called at the start of a frame.
Whether the platform's main shortcut modifier is held.
Queue a pointer move to (x, y) in points.
Queue a button press at the position of the last queued move.
Queue a button release at the position of the last queued move.
Queue wheel or trackpad scrolling; positive dy scrolls the content up (towards its end).
Queue a key press.
Queue typed text, one event per code point.
The UI being built (context.ui).
Release everything a UI owns.
Start a frame: reset the per-frame arena, clear the display list and open the root container.
Finish the frame: drop nodes nobody declared, lay the tree out and fix focus.
Run one frame with the app's build procedure.
Make ui the current UI for the code that follows (a second window, or tests).
Restore the UI that was current before the matching push_ui.
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.
The retained value of type T stored on a node, created zeroed on first use and kept until the
node leaves the tree.
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.
Open a container as the current parent; end_container closes it. Containers written with for do both.
Close the innermost open container.
The allocator of the per-frame arena: memory that lives until the next frame begins.
A copy of text that lives until the next frame, for strings a node keeps.
What happened to node this frame.
Give keyboard focus to node.
Ask for another frame (animation, a blinking caret).
The frame's current time in seconds.
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.
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.
Release the display list.
Empty the display list for a new frame.
Fill rect with color; radius rounds the corners and border draws an outline inside.
Draw one line of text with its top left at (x, y) and return its width in points.
Draw one line of text with its baseline at baseline, returning its width in points.
Restrict drawing to rect (intersected with the clip already in force) until pop_clip.
End the innermost push_clip.
Close the container when its for body is done.
A container that lays its children out left to right.
A container that lays its children out top to bottom.
A container whose children overlap, each filling it (or placed by align).
A column on the theme's panel colour.
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.
One line of text.
A push button. Returns true on the frame it was clicked (or Enter or Space pressed while focused).
A checkbox with a caption. Returns true on the frame the value changed.
A row in a list or tree: highlighted when selected, indented by indent points. Returns true
when clicked.
A horizontal rule in a column (a vertical one in a row).
Empty space that grows to fill what is left in its row or column.
A row of tabs.
One tab in a tab_bar, drawn selected when active.
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 { ... } }.
Draws and drags the divider between split panes.
Release the text and its history.
Replace everything with text and put the caret at the start. History is cleared.
The whole text, as a copy in temporary storage.
Whether any text is selected.
The selected range as (start, end) byte offsets.
The selected text, valid until the next edit.
Where the caret is, as a zero-based line and a byte column within it.
Put the caret at offset (clamped), selecting from the old anchor when extend is set.
Select all the text.
Replace the selection (or insert at the caret) with text.
Delete the selection. Returns whether there was one.
Offset one unit to the left (direction -1) or right (1) of from.
Move the caret one unit left or right.
Delete one unit before (direction -1) or after (1) the caret, or the selection if there is one.
Move the caret lines lines up (negative) or down, keeping its horizontal position.
Select the word around offset.
Select the whole line containing offset, including its line break.
Undo the last step and put the caret where it belongs. Returns whether anything changed.
Redo the last undone step. Returns whether anything changed.
Insert a line break, repeating the current line's leading blanks.
Insert spaces up to the next multiple of width columns.
Apply one KEY or TEXT input event to edit. style is the text style the lines are drawn in
(vertical movement measures with it); page_lines is how many lines a page is.
Copy the selection to the clipboard.
Copy the selection to the clipboard and delete it.
Replace the selection with the clipboard's text.
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.
A single-line text field over edit. Returns whether the text changed and whether Enter was
pressed.
The visible rows of an editor as (first line, last line, line height), from its last layout.
Ask for a frame in seconds even if no input arrives (animation, a blinking caret).
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.
Change the logical size of the window a UI draws into.
The first node matching query, in tree order, or null.
The first node with this label.
The nth node with this role.
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 at a point in the window.
The tree as indented text: role, label or text, and the rectangle of each node. Useful as a snapshot of a layout.
Create a renderer drawing into window's surface. Returns null if no GPU or surface is available.
Create a renderer drawing into an offscreen texture of width by height pixels, for tests and
screenshots. Returns null if no adapter is available.
Release the renderer and its GPU objects.
Match the target to a new size in pixels (a window resize).
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.
Draw the UI's display list offscreen and read the pixels back. Free pixels with array_free.
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.
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.
Like run_app, for a UI the caller owns (to set the theme or hooks first, or to inspect it after).
An axis-aligned rectangle: x, y is the top left corner.
Which way a container lays out its children.
How a container places children across its axis.
How a container places children along its axis when none of them grow.
What a widget is, kept on its node for tests, for tools and for a later accessibility export.
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}.
Light or dark defaults for default_theme.
The look of the library's widgets. Change fields on ui.theme (or set_theme) to restyle everything.
Which font a piece of text uses.
How text is drawn.
A loaded font file. Created by load_font and owned by the Text_System.
Where a glyph is in the atlas and how to place it.
Vertical metrics in points.
The glyph atlas: a one-channel texture filled row by row, uploaded by the renderer when
version changes.
Everything text needs: fonts, the glyph cache and the atlas.
Mouse buttons.
Which modifier keys are held. primary is Cmd on macOS and Ctrl elsewhere.
What an input event is.
A queued input event. Keys use the Input module's Key_Code (letters are their upper case ASCII).
The pointer, modifiers and the keyboard events a frame sees.
How a node lays out and paints.
A procedure that paints a node's rectangle. It may use ui.draw freely.
One widget in the tree. App code reads it through widget's result and the test helpers; the
library fills it in.
What happened to a node this frame, from the input events routed to it.
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.
Platform services the library needs but does not own, set by run_app. Tests leave them null.
The mouse cursor shapes widgets ask for.
What a display list entry draws.
One display list entry.
The display list of a frame plus the clip stack used while building it.
An open container, returned by row, column and the other containers so for can close it.
Options for text: all optional.
Looks of a button.
Options for button.
What a click on a tab did.
Options for tab.
An open split: the two panes are visited with for pane: split(...).
Options for split.
A piece of text being edited. Zero is empty; release it with free_text_edit.
A coloured range of one line, produced by an editor's highlight procedure.
A procedure that colours one line of text by appending spans (unlisted text uses the default colour).
A range of the text drawn with a background: search results, diagnostics, matching brackets.
Options for text_editor and text_field.
What a movement or deletion covers.
What handle_key did.
Options for headless.
How find picks a node: every field that is set must match.
A renderer bound to one window or to an offscreen target.
One entry of the instance buffer, as the shader reads it.
A run of instances drawn with one scissor rectangle.
Pixels read back from an offscreen render: rows top to bottom, 4 bytes per pixel (R, G, B, A).
Options for run_app.
What run_app can tell the app about the window.
A colour with straight (not premultiplied) alpha, each channel 0 to 1.