Clay Module
GX includes a Clay module for immediate-mode declarative UI layout. Clay
(nicbarker/clay) is a high-performance
layout engine that computes element positions and sizes, then outputs render
commands for any backend to draw. Clay itself does not draw anything — you
pair it with a renderer (Raylib, Sokol, or your own).
How Clay Works
- Declare your UI tree each frame (elements, text, config)
- Compute layout — Clay resolves sizes, positions, scrolling
- Iterate render commands — rectangles, text, borders, images, scissors
- Draw using your renderer of choice
This separation means the same layout code works with Raylib, Sokol, or
a custom GPU renderer.
Usage
import clay
Build with -I modules:
gx myapp.gx -I modules -o myapp.exe
Quick Example — Console Layout
import clay
fn main() {
var ctx = clay.gx_clay_init(800.0, 600.0)
clay.gx_clay_use_default_measure()
clay.gx_clay_set_pointer(400.0, 300.0, false)
clay.gx_clay_update_scroll(false, 0.0, 0.0, 0.016)
clay.gx_clay_begin_layout()
// Root container: dark column, padded
clay.gx_clay_open_id("root")
clay.gx_clay_config_full(
clay.COLUMN,
clay.SIZING_GROW, 0.0, 0.0,
clay.SIZING_GROW, 0.0, 0.0,
16, 16, 16, 16, 8,
clay.ALIGN_LEFT, clay.ALIGN_TOP,
40.0, 40.0, 40.0, 255.0,
0.0, 0.0, 0.0, 0.0
)
clay.gx_clay_text("Hello from Clay!", 255.0, 255.0, 255.0, 255.0, 0, 24)
clay.gx_clay_text("Layout computed, render commands ready.", 180.0, 180.0, 180.0, 255.0, 0, 16)
clay.gx_clay_close()
var count: i32 = 0
var cmds = clay.gx_clay_end_layout(0.016, &count)
// Iterate render commands
for (i = 0 : count) {
var cmd = clay.gx_clay_get_command(cmds, i)
var cmd_type = clay.gx_clay_cmd_type(cmd)
var x = clay.gx_clay_cmd_x(cmd)
var y = clay.gx_clay_cmd_y(cmd)
var w = clay.gx_clay_cmd_w(cmd)
var h = clay.gx_clay_cmd_h(cmd)
if (cmd_type == clay.CMD_RECTANGLE) {
print("RECT ({x}, {y}) {w}x{h}\n")
}
if (cmd_type == clay.CMD_TEXT) {
var text = clay.gx_clay_cmd_text_chars(cmd)
print("TEXT ({x}, {y}) \"{text}\"\n")
}
}
}
Initialization
// Initialize with layout dimensions
var ctx = clay.gx_clay_init(800.0, 600.0)
// Use built-in rough text measurement (good for prototyping)
clay.gx_clay_use_default_measure()
// Or set custom measurement function (for accurate font metrics)
clay.gx_clay_set_measure_text(my_measure_fn)
| Function | Signature | Description |
|---|
gx_clay_init | (f32, f32) → *void | Initialize Clay, returns context |
gx_clay_set_dimensions | (f32, f32) | Update layout size (on resize) |
gx_clay_use_default_measure | () | Use built-in text measurement |
gx_clay_set_measure_text | (*void) | Set custom text measurement function |
Frame Lifecycle
Each frame follows this pattern:
// 1. Feed input
clay.gx_clay_set_pointer(mouse_x, mouse_y, mouse_down)
clay.gx_clay_update_scroll(drag, dx, dy, dt)
// 2. Declare UI
clay.gx_clay_begin_layout()
// ... open/close elements, add text, configure ...
// 3. Compute layout and get render commands
var count: i32 = 0
var cmds = clay.gx_clay_end_layout(dt, &count)
// 4. Draw (using your renderer)
for (i = 0 : count) {
var cmd = clay.gx_clay_get_command(cmds, i)
// ... draw based on cmd type ...
}
Element Building
Elements form a tree. Open an element, configure it, add children, then close.
clay.gx_clay_open_id("panel") // open with string ID
clay.gx_clay_config_col(8, 16, 16) // column layout, gap=8, padding=16
clay.gx_clay_text("Hello", 255.0, 255.0, 255.0, 255.0, 0, 20)
clay.gx_clay_text("World", 200.0, 200.0, 200.0, 255.0, 0, 16)
clay.gx_clay_close()
| Function | Description |
|---|
gx_clay_open() | Open element (no ID) |
gx_clay_open_id(id) | Open element with string ID |
gx_clay_open_idi(id, index) | Open element with ID + index (for loops) |
gx_clay_close() | Close current element |
gx_clay_text(text, r, g, b, a, font_id, font_size) | Add text element |
gx_clay_text_ex(text, r, g, b, a, font_id, font_size, letter_spacing, line_height, wrap_mode, alignment) | Add text with full config |
Layout Configuration
Simple Helpers
clay.gx_clay_config_row(gap, pad_x, pad_y) // horizontal layout
clay.gx_clay_config_col(gap, pad_x, pad_y) // vertical layout
clay.gx_clay_config_grow() // fill both axes
clay.gx_clay_config_bg(r, g, b, a) // background color only
clay.gx_clay_config_rect(r, g, b, a, radius) // background + corner radius
Full Configuration
For complete control, use gx_clay_config_full:
clay.gx_clay_config_full(
direction, // ROW or COLUMN
w_type, w_min, w_max, // width sizing
h_type, h_min, h_max, // height sizing
pad_left, pad_right, // horizontal padding
pad_top, pad_bottom, // vertical padding
child_gap, // gap between children
align_x, align_y, // child alignment
bg_r, bg_g, bg_b, bg_a, // background color (0-255)
corner_tl, corner_tr, // corner radius
corner_bl, corner_br
)
Sizing Types
| Constant | Description |
|---|
SIZING_FIT | Shrink to fit content |
SIZING_GROW | Expand to fill parent |
SIZING_PERCENT | Percentage of parent (min = percentage as 0-1) |
SIZING_FIXED | Fixed pixel size (min = max = size) |
Layout Direction
| Constant | Description |
|---|
ROW | Children laid out left to right |
COLUMN | Children laid out top to bottom |
Alignment
| Constant | Axis | Description |
|---|
ALIGN_LEFT | X | Align children to left |
ALIGN_RIGHT | X | Align children to right |
ALIGN_CENTER | X/Y | Center children |
ALIGN_TOP | Y | Align children to top |
ALIGN_BOTTOM | Y | Align children to bottom |
Additional Configuration
| Function | Description |
|---|
gx_clay_config_border(r, g, b, a, left, right, top, bottom, between) | Border around element |
gx_clay_config_scroll(horizontal, vertical) | Make scrollable container |
gx_clay_config_floating(off_x, off_y, z_index) | Floating/overlay element |
gx_clay_config_image(image_data, w, h) | Image element |
Render Commands
After gx_clay_end_layout(), iterate the command array:
var cmd = clay.gx_clay_get_command(cmds, i)
var cmd_type = clay.gx_clay_cmd_type(cmd)
Command Types
| Constant | Value | Description |
|---|
CMD_RECTANGLE | 1 | Filled rectangle with optional rounded corners |
CMD_BORDER | 2 | Border lines around element |
CMD_TEXT | 3 | Text string with font and color |
CMD_IMAGE | 4 | Image/texture |
CMD_SCISSOR_START | 5 | Begin clipping region |
CMD_SCISSOR_END | 6 | End clipping region |
CMD_CUSTOM | 9 | Custom render data |
Command Data Accessors
Bounding box (all commands):
| Function | Returns | Description |
|---|
gx_clay_cmd_x/y/w/h | f32 | Position and size |
gx_clay_cmd_type | u8 | Command type |
gx_clay_cmd_id | u32 | Element ID |
Rectangle data:
| Function | Returns |
|---|
gx_clay_cmd_rect_r/g/b/a | Color (0-255 as f32) |
gx_clay_cmd_rect_corner_tl/tr/bl/br | Corner radii |
Text data:
| Function | Returns |
|---|
gx_clay_cmd_text_chars | Text string (cstr) |
gx_clay_cmd_text_len | String length |
gx_clay_cmd_text_r/g/b/a | Text color |
gx_clay_cmd_text_font_id | Font ID (u16) |
gx_clay_cmd_text_font_size | Font size (u16) |
Border data:
| Function | Returns |
|---|
gx_clay_cmd_border_r/g/b/a | Border color |
gx_clay_cmd_border_left/right/top/bottom | Border widths (u16) |
Interaction
// Check if current element is hovered
if (clay.gx_clay_hovered()) {
// highlight this element
}
// Check if a specific element is under the pointer
if (clay.gx_clay_pointer_over("button1")) {
// handle click
}
| Function | Returns | Description |
|---|
gx_clay_hovered | bool | Is current element hovered |
gx_clay_pointer_over | bool | Is named element under pointer |
gx_clay_hash_id | u32 | Get hash for string ID |
gx_clay_get_open_id | u32 | Get current element’s ID |
Text Configuration
| Constant | Description |
|---|
WRAP_WORDS | Wrap at word boundaries |
WRAP_NEWLINES | Wrap only at newlines |
WRAP_NONE | No wrapping |
TEXT_LEFT | Left-align text |
TEXT_CENTER | Center text |
TEXT_RIGHT | Right-align text |
Debug Mode
clay.gx_clay_set_debug(true) // enable debug overlay
clay.gx_clay_set_debug(false) // disable
if (clay.gx_clay_is_debug()) { /* ... */ }
The debug overlay shows element boundaries, IDs, and layout info.
Module Layout
modules/
clay/
c/
clay.h Clay library header
gx_clay.h GX binding helpers
clay_impl.c Implementation (compiled via @cfile)
gx/
clay.gx Module API (this file)
Renderers
Clay produces render commands but does not draw. Use one of the provided renderers:
| Renderer | Module | Description |
|---|
| Raylib | clay_raylib | Full support: rectangles, text, borders, rounded corners, images |
| Sokol | clay_sokol | Rectangles, borders, images, scissors. No text or rounded corners. |
| Custom | — | Iterate commands yourself with any drawing API |
See Clay Raylib Module and Clay Sokol Module.
Examples
| Example | Description |
|---|
examples/48_clay.gx | Console-only: layout + render command iteration |
examples/49_clay_raylib.gx | Full GUI: Clay layout rendered with Raylib |