Skip to content

ui

The ui package provides a robust Immediate Mode GUI for in-engine tooling, editors, and debug overlays.

Features:

  • Comprehensive Widgets: Includes interactable controls like button, checkbox, slider, inputText, colorPicker, and debug tools like plot.
  • Layouts: Organize your interfaces using draggable windows, collapsible headers, beginTabBar systems, and horizontal beginRows.
  • State Tracking: Utilizes an internal _State tracker and an ID stack (pushId / popId) to safely handle overlapping windows, focus states, and dynamically generated UI elements inside loops.
  • Styling: Easily tweak colors, sizing, fonts, and padding via the Style struct and the DEFAULT_STYLE configuration.

The overall lifecycle (start, draw, and end) is managed automatically by the engine’s main loop. During the frame, calling a widget function (like button) will immediately return true if interacted with, allowing instant logic execution. Visually, the UI commands are queued into a Command buffer and deferred until draw is called. This deferred rendering guarantees proper Z-indexing, clipping (scissoring), and overlapping of popup menus and tooltips.


Command :: union {
LineCommand,
RectangleCommand,
TriangleCommand,
TextCommand,
ScissorCommand,
PopScissorCommand,
}

Union of all possible drawing commands recorded by the UI system. These are executed in draw at the end of the frame.


DEFAULT_STYLE :: Style {
backgroundColor = gmath.Color{0.2, 0.2, 0.2, 0.9},
hotColor = gmath.Color{0.6, 0.6, 0.6, 1.0},
activeColor = gmath.Color{0.4, 0.4, 0.4, 1.0},
textColor = gmath.Color{1.0, 1.0, 1.0, 1.0},
buttonColor = gmath.Color{0.3, 0.3, 0.3, 1.0},
titleColor = gmath.Color{0.0, 0.0, 0.0, 1.0},
rounding = 2.0,
padding = 2.0,
outlineThickness = 0.5,
itemHeight = 7.0,
titleHeight = 8.0,
titleButtonSize = 6.0,
resizeSize = 6.0,
scrollbarWidth = 2.0,
scrollSpeed = 10.0,
minimumScrollbarHeight = 20.0,
separatorHeight = 0.5,
plotHeight = 40.0,
colorPreviewWidth = 30.0,
fontSize = 6,
font = .PixelCode,
}

Default style configuration (Dark theme).


Style :: struct {
backgroundColor: gmath.Color,
hotColor: gmath.Color,
activeColor: gmath.Color,
textColor: gmath.Color,
buttonColor: gmath.Color,
titleColor: gmath.Color,
rounding: f32,
padding: f32,
outlineThickness: f32,
itemHeight: f32,
titleHeight: f32,
titleButtonSize: f32,
resizeSize: f32,
scrollbarWidth: f32,
scrollSpeed: f32,
minimumScrollbarHeight: f32,
separatorHeight: f32,
plotHeight: f32,
colorPreviewWidth: f32,
fontSize: uint,
font: generated.FontName,
}

Configuration struct defining the visual appearence of the UI.


WindowState :: struct {
position: gmath.Vector2,
size: gmath.Vector2,
isCollapsed: bool,
scrollY: f32,
contentHeight: f32,
commands: [dynamic]Command,
zIndex: f32,
lastFrameSeen: u64,
scissorRectangle: gmath.Rectangle,
}

Persistent state for a draggable window.


@(private)
_State :: struct {
hotId: u64, // widget currently hovered
activeId: u64, // widget currently clicked
cursor: gmath.Vector2, // current layout position
startX: f32, // where the current column started
indentation: f32, // current x offset
maxWidth: f32, // widest item in the current column
windows: map[u64]WindowState, // state storage
headers: map[u64]bool, // id -> is open
inputBuffers: map[u64]strings.Builder, // id -> text buffer
tabBars: map[u64]u64, // tab id -> active tab id
idStack: [dynamic]u64, // id stack for loops and scopes
currentWindowId: u64, // track if we are currently inside window
focusedWindowId: u64, // track the last clicked window
hoveredWindowId: u64,
nextFocusedWindowId: u64, // candidate for focus next frame
clickClaimedZ: f32, // z index of window claiming the click
inRow: bool,
rowStartX: f32,
rowMaxHeight: f32,
currentZIndex: f32,
nextZIndex: f32,
tooltipText: string, // if not empty drawn at the end of frame
lastWidgetRectangle: gmath.Rectangle, // rect of the widget just drawn
currentTabBarId: u64,
tabSavedCursor: gmath.Vector2,
tabContentHeight: f32,
openComboId: u64, // id of currently open combo
comboWidth: f32, // width of currently open combo
isRecordingPopup: bool, // true if inside uiBeginCombo
popupCursor: gmath.Vector2, // layout cursor for the popup
popupCommands: [dynamic]Command, // draw list for popup
mousePosition: gmath.Vector2, // store it to avoid unneeded matrix inverses
}

Internal global state for the Immediate mode UI. Stores layout cursors, input state, and widget persistence maps.


beginCombo :: proc (label: string, preview: string, width: f32 = 50.0) -> bool

Starts a dropdown menu (combo box).

  • preview: The text to display on the closed button. Returns true if the dropdown is open and items should be rendered. Must be ended with endCombo.

beginRow :: proc ()

Starts a horizontal layout row. Subsequent widgets will be placed side-by-side until endRow is called.


beginTabBar :: proc (idString: string) -> bool

Starts a tab bar container. Must be followed by tabItem calls and ended with endTabBar.


beginTabItem :: proc (label: string) -> bool

Draws a tab button. Returns true if this tab is currently selected.


button :: proc (text: string, width: f32 = 60.0) -> bool

Draws a clickable button. Returns true if clicked.


checkbox :: proc (label: string, value: ^bool) -> bool

Draws a checkbox. Modifies value directly via a pointer. Returns true if the value was toggled this frame.


colorPicker :: proc (label: string, color: ^gmath.Color, alpha: bool = true) -> bool

Draws a color picker (preview box + RGB(A) sliders). Returns true if the color changed.


comboEnum :: proc (
label: string,
value: ^$T,
width: f32 = 50.0,
) -> bool where intrinsics.type_is_enum(T)

Draws a combo box for any enum. Automatically extracts enum names.

  • value: Pointer to the enum variable.

comboString :: proc (
label: string,
currentItemIndex: ^int,
items: []string,
width: f32 = 50.0,
) -> bool

Draws a combo box that selects an index from a list of strings.

  • currentItemIndex: Pointer to the index of the selected item
  • items: Slice of strings to display. Returns true if the selection changed.

draw :: proc ()

Executes all deferred UI commands. Sorts windows by Z-index, renders them back-to-front, then renders popups and tooltips on top. Called internally at the end of the frame.


end :: proc ()

Finalizes the UI frame state. Commits focus changes and resets active widget state on mouse release. Called internally at the end of each frame.


endCombo :: proc ()

Ends the dropdown menu block. Must be called if beginCombo returns true.


endRow :: proc ()

Ends the current horizontal layout row. Moves the cursor down by the height of the tallest item in the row.


endTabBar :: proc ()

Ends the tab bar container.


endTabItem :: proc ()

Ends the current tab item content block. Must be called if beginTabItem returns true.


endWindow :: proc ()

Ends the current window block. Calculates content height, draws scrollbar, and cleans up window state. Must be called if window returns true.


getAvailableWidth :: proc () -> f32

Returns the available width in the current window/column. Automatically subtracts scrollbar width if active.


header :: proc (text: string, width: f32 = 60.0) -> bool

Draws a collapsible header. Returns true if open.


indent :: proc (amount: f32 = 5.0)

Increases the layout indentation (shifts cursor right). Useful for hierarchical data or grouping.


inputText :: proc (text: ^string, label: string = "", width: f32 = 60.0) -> bool

Draws a text input field that modifies a string directly. Manages a temporary builder internally. Updates text only when enter is pressed or focus is lost. Returns true if the user pressed enter.


inputTextBuilder :: proc (
label: string,
builder: ^strings.Builder,
width: f32 = 60.0,
idOverride: u64 = 0,
) -> bool

Low-level builder for text input. Handles the core interaction and rendering logic for inputText


isRectangleVisible :: proc (rectangle: gmath.Rectangle) -> bool

Checks if rectangle is visible within the current window’s scissor area. Useful for culling optimization.


label :: proc (text: string)

Draws a static text label.


plot :: proc (
label: string,
values: []f32,
minimumValue: f32 = 0.0,
maximumValue: f32 = 0.0,
height: f32 = 0.0,
)

Draws a simple line plot for a slice of numbers. Useful for FPS counters or physics debugging.


popId :: proc ()

Pops the last identifier from the stack.


pushId :: proc {
pushIdInt,
pushIdString,
pushIdPointer,
}

Generic overload for functions used to pushing variable based identifiers onto the ID stack.


pushIdInt :: proc (value: int)

Pushes an integer identifier onto the ID stack.


pushIdPointer :: proc (pointer: rawptr)

Pushes a pointer identifier onto the ID stack.


pushIdString :: proc (value: string)

Pushes a string identifier onto the ID stack.


selectable :: proc (text: string, selected: bool) -> bool

Draws a selectable item inside a dropdown menu. Returns true if clicked.


separator :: proc ()

Draws a horizontal separator line. Spans the full available width of the current window or column.


slider :: proc {
sliderFloat,
sliderInteger,
}

Draws a slider. Modifies value directly via a pointer. Returns true if the value was changed this frame. Function overload for sliderFloat/sliderInteger


sliderFloat :: proc (
value: ^f32,
minimumValue: f32,
maximumValue: f32,
label: string = "",
width: f32 = 60.0,
step: f32 = 0.0,
) -> bool

Draws a float slider.


sliderInteger :: proc (
value: ^int,
minimumValue: int,
maximumValue: int,
label: string = "",
width: f32 = 60.0,
step: int = 1,
) -> bool

Draws a integer slider.


start :: proc ()

Initializes the UI state for the new frame. Resets per-frame counters, clears command buffers, and performs window hit-testing. Called internally at the start of each frame.


tooltip :: proc (text: string)

Shows a tooltip if the previous widget is hovered. Call this immediately after the widget you want to describe.


unindent :: proc (amount: f32 = 5.0)

Decreases the layout indentation (shifts cursor left). Clamps indentation to 0.


window :: proc (
title: string,
rectangle: gmath.Rectangle,
isOpen: ^bool = nil,
idSuffix: string = "",
) -> bool

Begins a draggable window. Returns true if the window is visible (not collapsed). Accepts an optional isOpen pointer. If provided, an ‘X’ button is drawn. If clicked, isOpen^ becomes false and the window stops rendering.