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

  1. Declare your UI tree each frame (elements, text, config)
  2. Compute layout — Clay resolves sizes, positions, scrolling
  3. Iterate render commands — rectangles, text, borders, images, scissors
  4. 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)
FunctionSignatureDescription
gx_clay_init(f32, f32) → *voidInitialize 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()
FunctionDescription
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

ConstantDescription
SIZING_FITShrink to fit content
SIZING_GROWExpand to fill parent
SIZING_PERCENTPercentage of parent (min = percentage as 0-1)
SIZING_FIXEDFixed pixel size (min = max = size)

Layout Direction

ConstantDescription
ROWChildren laid out left to right
COLUMNChildren laid out top to bottom

Alignment

ConstantAxisDescription
ALIGN_LEFTXAlign children to left
ALIGN_RIGHTXAlign children to right
ALIGN_CENTERX/YCenter children
ALIGN_TOPYAlign children to top
ALIGN_BOTTOMYAlign children to bottom

Additional Configuration

FunctionDescription
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

ConstantValueDescription
CMD_RECTANGLE1Filled rectangle with optional rounded corners
CMD_BORDER2Border lines around element
CMD_TEXT3Text string with font and color
CMD_IMAGE4Image/texture
CMD_SCISSOR_START5Begin clipping region
CMD_SCISSOR_END6End clipping region
CMD_CUSTOM9Custom render data

Command Data Accessors

Bounding box (all commands):

FunctionReturnsDescription
gx_clay_cmd_x/y/w/hf32Position and size
gx_clay_cmd_typeu8Command type
gx_clay_cmd_idu32Element ID

Rectangle data:

FunctionReturns
gx_clay_cmd_rect_r/g/b/aColor (0-255 as f32)
gx_clay_cmd_rect_corner_tl/tr/bl/brCorner radii

Text data:

FunctionReturns
gx_clay_cmd_text_charsText string (cstr)
gx_clay_cmd_text_lenString length
gx_clay_cmd_text_r/g/b/aText color
gx_clay_cmd_text_font_idFont ID (u16)
gx_clay_cmd_text_font_sizeFont size (u16)

Border data:

FunctionReturns
gx_clay_cmd_border_r/g/b/aBorder color
gx_clay_cmd_border_left/right/top/bottomBorder 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
}
FunctionReturnsDescription
gx_clay_hoveredboolIs current element hovered
gx_clay_pointer_overboolIs named element under pointer
gx_clay_hash_idu32Get hash for string ID
gx_clay_get_open_idu32Get current element’s ID

Text Configuration

ConstantDescription
WRAP_WORDSWrap at word boundaries
WRAP_NEWLINESWrap only at newlines
WRAP_NONENo wrapping
TEXT_LEFTLeft-align text
TEXT_CENTERCenter text
TEXT_RIGHTRight-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:

RendererModuleDescription
Raylibclay_raylibFull support: rectangles, text, borders, rounded corners, images
Sokolclay_sokolRectangles, borders, images, scissors. No text or rounded corners.
CustomIterate commands yourself with any drawing API

See Clay Raylib Module and Clay Sokol Module.

Examples

ExampleDescription
examples/48_clay.gxConsole-only: layout + render command iteration
examples/49_clay_raylib.gxFull GUI: Clay layout rendered with Raylib