Repository: nicbarker/clay
Stars: 17036
README.md
Clay, A UI Layout Library
_Clay_ (short for C Layout) is a high performance 2D UI layout library.
Major Features
- Microsecond layout performance
- Flex-box like layout model for complex, responsive layouts including text wrapping, scrolling containers and aspect ratio scaling
- Transition API for easy layout animations
- Single 4.8k LOC clay.h file with zero dependencies (including no standard library linking)
- Wasm support: compile with clang to a 15kb uncompressed .wasm file for use in the browser
- Static arena based memory use with no malloc / free, and low total memory overhead (e.g. ~3.5mb for 8192 layout elements).
- React-like nested declarative syntax
- Renderer agnostic: outputs a sorted list of rendering primitives that can be easily composited in any 3D engine, and even compiled to HTML (examples provided)
Take a look at the clay website for an example of clay compiled to wasm and running in the browser, or others in the examples directory.
You can also watch the introduction video for an overview of the motivation behind Clay's development and a short demo of its usage.
<img width="1394" alt="A screenshot of a code IDE with lots of visual and textual elements" src="https://github.com/user-attachments/assets/9986149a-ee0f-449a-a83e-64a392267e3d">
_An example GUI application built with clay_
Quick Start
Download or clone clay.h and include it after defining CLAY_IMPLEMENTATION in one file.
// Must be defined in one file, _before_ #include "clay.h"
#define CLAY_IMPLEMENTATION
#include "../../clay.h"const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};
const Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};
const Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};
void HandleClayErrors(Clay_ErrorData errorData) {
// See the Clay_ErrorData struct for more information
printf("%s", errorData.errorText.chars);
switch(errorData.errorType) {
// etc
}
}
// Example measure text function
static inline Clay_Dimensions MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData) {
// Clay_TextElementConfig contains members such as fontId, fontSize, letterSpacing etc
// Note: Clay_String->chars is not guaranteed to be null terminated
return (Clay_Dimensions) {
.width = text.length * config->fontSize, // <- this will only work for monospace fonts, see the renderers/ directory for more advanced text measurement
.height = config->fontSize
};
}
// Layout config is just a struct that can be declared statically, or inline
Clay_ElementDeclaration sidebarItemConfig = (Clay_ElementDeclaration) {
.layout = {
.sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) }
},
.backgroundColor = COLOR_ORANGE
};
// Re-useable components are just normal functions
void SidebarItemComponent() {
CLAY(id, sidebarItemConfig) {
// children go here...
}
}
int main() {
// Note: malloc is only used here as an example, any allocator that provides
// a pointer to addressable memory of at least totalMemorySize will work
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
// Note: screenWidth and screenHeight will need to come from your environment, Clay doesn't handle window related tasks
Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });
while(renderLoop()) { // Will be different for each renderer / environment
// Optional: Update internal layout dimensions to support resizing
Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight });
// Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling & debug tools
Clay_SetPointerState((Clay_Vector2) { mousePositionX, mousePositionY }, isMouseDown);
// Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling and debug tools
Clay_UpdateScrollContainers(true, (Clay_Vector2) { mouseWheelX, mouseWheelY }, deltaTime);
// All clay layouts are declared between Clay_BeginLayout and Clay_EndLayout
Clay_BeginLayout();
// An example of laying out a UI with a fixed width sidebar and flexible width main content
CLAY(CLAY_ID("OuterContainer"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
CLAY(CLAY_ID("SideBar"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 },
.backgroundColor = COLOR_LIGHT
}) {
CLAY(CLAY_ID("ProfilePictureOuter"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = COLOR_RED }) {
CLAY(CLAY_ID("ProfilePicture"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture } }) {}
CLAY_TEXT(CLAY_STRING("Clay - UI Library"), { .fontSize = 24, .textColor = {255, 255, 255, 255} });
}
// Standard C code like loops etc work inside components
for (int i = 0; i < 5; i++) {
SidebarItemComponent();
}
CLAY(CLAY_ID("MainContent"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}
}
}
// All clay layouts are declared between Clay_BeginLayout and Clay_EndLayout
Clay_RenderCommandArray renderCommands = Clay_EndLayout(deltaTime); // deltaTime is the time since the last frame, and is used for transitions
// More comprehensive rendering examples can be found in the renderers/ directory
for (int i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = &renderCommands.internalArray[i];
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
DrawRectangle(renderCommand->boundingBox, renderCommand->renderData.rectangle.backgroundColor);
}
// ... Implement handling of other command types
}
}
}
}
The above example, rendered correctly will look something like the following:
In summary, the general order of steps is:
1. Clay_SetLayoutDimensions(dimensions)
2. Clay_SetPointerState(pointerPosition, isPointerDown)
3. Clay_UpdateScrollContainers(enableDragScrolling, scrollDelta, deltaTime)
4. Clay_BeginLayout()
5. Declare your layout with the provided Element Macros
6. Clay_EndLayout()
7. Render the results using the outputted Clay_RenderCommandArray
For help starting out or to discuss clay, considering joining the discord server.
Summary
<!-- TOC -->
* High Level Documentation
* Building UI Hierarchies
* Configuring Layout and Styling UI Elements
* Element IDs
* Mouse, Touch and Pointer Interactions
* Scrolling Elements
* Floating Elements ("Absolute" Positioning)
* Laying Out Your Own Custom Elements
* Transitions
* Retained Mode Rendering
* Visibility Culling
* Preprocessor Directives
* Bindings for non C
* Other implementations
* Debug Tools
* Running more than one Clay instance
* API
* Naming Conventions
* Public Functions
* Lifecycle for public functions
* Clay_MinMemorySize
* Clay_CreateArenaWithCapacityAndMemory
* Clay_SetMeasureTextFunction
* Clay_ResetMeasureTextCache
* Clay_SetMaxElementCount
* Clay_SetMaxMeasureTextCacheWordCount
* Clay_Initialize
* Clay_SetCurrentContext
* Clay_GetCurrentContext
* Clay_SetLayoutDimensions
* Clay_SetPointerState
* Clay_UpdateScrollContainers
* Clay_GetScrollOffset
* Clay_BeginLayout
* Clay_EndLayout
* Clay_Hovered
* Clay_OnHover
* Clay_PointerOver
* Clay_GetOpenElementId
* Clay_GetScrollContainerData
* Clay_GetElementData
* Clay_GetElementId
* Element Macros
* CLAY()
* CLAY_AUTO_ID()
* CLAY_TEXT()
* CLAY_ID()
* CLAY_SID()
* CLAY_IDI()
* CLAY_SIDI()
* CLAY_ID_LOCAL()
* CLAY_SID_LOCAL()
* CLAY_IDI_LOCAL()
* CLAY_SIDI_LOCAL()
* Data Structures & Definitions
* Clay_ElementDeclaration
* Clay_LayoutConfig
* Clay_ImageElementConfig
* Clay_AspectRatioElementConfig
* Clay_ImageElementConfig
* Clay_ClipElementConfig
* Clay_BorderElementConfig
* Clay_FloatingElementConfig
* Clay_CustomElementConfig
* Clay_TransitionElementConfig
* Clay_Color
* Clay_String
* Clay_ElementId
* Clay_RenderCommandArray
* Clay_RenderCommand
* Clay_ScrollContainerData
* Clay_ElementData
* Clay_PointerData
* Clay_ErrorHandler
* Clay_ErrorData
<!-- TOC -->
High Level Documentation
Building UI Hierarchies
Clay UIs are built using the C macro
CLAY(id, { configuration }). This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The CLAY() macro can also be _nested_, similar to other declarative UI systems like HTML.Child elements are added by opening a block: {} after calling the CLAY() macro (exactly like you would with an if statement or for loop), and declaring child components inside the braces.
// Parent element with 8px of padding
CLAY(CLAY_ID("parent"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
// Child element 1
CLAY_TEXT(CLAY_STRING("Hello World"), { .fontSize = 16 });
// Child element 2 with red background
CLAY((CLAY_ID("child"), { .backgroundColor = COLOR_RED }) {
// etc
}
}However, unlike HTML and other declarative DSLs, this macro is just C. As a result, you can use arbitrary C code such as loops, functions and conditions inside your layout declaration code:
// Re-usable "components" are just functions that declare more UI
void ButtonComponent(Clay_String buttonText) {
// Red box button with 8px of padding
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {
CLAY_TEXT(buttonText, textConfig);
}
}// Parent element
CLAY(CLAY_ID("parent"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Render a bunch of text elements
for (int i = 0; i < textArray.length; i++) {
CLAY_TEXT(textArray.elements[i], textConfig);
}
// Only render this element if we're on a mobile screen
if (isMobileScreen) {
CLAY(0) {
// etc
}
}
// Re-usable components
ButtonComponent(CLAY_STRING("Click me!"));
ButtonComponent(CLAY_STRING("No, click me!"));
});
Configuring Layout and Styling UI Elements
The layout and style of clay elements is configured with the Clay_ElementDeclaration struct passed to the
CLAY() macro. CLAY(CLAY_ID("box"), { .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Children are 8px inset into parent, and laid out top to bottom
}This macro isn't magic - all it's doing is wrapping the standard designated initializer syntax. e.g.
(Clay_ElementDeclaration) { .layout = { .padding = { .left = 8, .right = 8 } ....See the Clay_ElementDeclaration API for the full list of options.
A Clay_ElementDeclaration struct can be defined in file scope or elsewhere, and reused.
// Define a style in the global / file scope
Clay_ElementDeclaration reuseableStyle = (Clay_ElementDeclaration) {
.layout = { .padding = { .left = 12 } },
.backgroundColor = { 120, 120, 120, 255 },
.cornerRadius = { 12, 12, 12, 12 }
};CLAY(CLAY_ID("box"), reuseableStyle) {
// ...
}
Element IDs
The Clay macro by default accepts an ID as its first argument, which is usually provided by the CLAY_ID() convenience macro. Elements can also be created with auto generated IDs, by using the CLAY_AUTO_ID() macro.
// Will always produce the same ID from the same input string
CLAY(CLAY_ID("OuterContainer"), { ...configuration }) {}// Generates a unique ID that may not be the same between two layout calls
CLAY_AUTO_ID({ ...configuration }) {}
Element IDs have two main use cases. Firstly, tagging an element with an ID allows you to query information about the element later, such as its mouseover state or dimensions.
Secondly, IDs are visually useful when attempting to read and modify UI code, as well as when using the built-in debug tools.
To avoid having to construct dynamic strings at runtime to differentiate ids in loops, clay provides the CLAY_IDI(string, index) macro to generate different ids from a single input string. Think of IDI as "ID + Index"
// This is the equivalent of calling CLAY_ID("Item0"), CLAY_ID("Item1") etc
for (int index = 0; index < items.length; index++) {
CLAY(CLAY_IDI("Item", index), { ..configuration }) {}
}This ID will be forwarded to the final Clay_RenderCommandArray for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect)
Mouse, Touch and Pointer Interactions
Clay provides several functions for handling mouse and pointer interactions.
All pointer interactions depend on the function void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) being called after each mouse position update and before any other clay functions.
During UI declaration
The function bool Clay_Hovered() can be called during element construction or in the body of an element, and returns true if the mouse / pointer is over the currently open element.
// An orange button that turns blue when hovered
CLAY(CLAY_ID("Button"), { .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {
bool buttonHovered = Clay_Hovered();
CLAY_TEXT(buttonHovered ? CLAY_STRING("Hovered") : CLAY_STRING("Hover me!"), headerTextConfig);
}The function void Clay_OnHover() allows you to attach a function pointer to the currently open element, which will be called if the mouse / pointer is over the element.
void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData) {
ButtonData buttonData = (ButtonData )userData;
// Pointer state allows you to detect mouse down / hold / release
if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
// Do some click handling
NavigateTo(buttonData->link);
}
}ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
Clay_OnHover(HandleButtonInteraction, &linkButton);
CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig);
}
Before / After UI declaration
If you want to query mouse / pointer overlaps outside layout declarations, you can use the function bool Clay_PointerOver(Clay_ElementId id), which takes an element id and returns a bool representing whether the current pointer position is within its bounding box.
// Reminder: Clay_SetPointerState must be called before functions that rely on pointer position otherwise it will have no effect
Clay_Vector2 mousePosition = { x, y };
Clay_SetPointerState(mousePosition, mouseButtonDown(0));
// ...
// If profile picture was clicked
if (mouseButtonDown(0) && Clay_PointerOver(Clay_GetElementId("ProfilePicture"))) {
// Handle profile picture clicked
}Note that the bounding box queried by Clay_PointerOver is from the last frame. This generally shouldn't make a difference except in the case of animations that move at high speed.
If this is an issue for you, performing layout twice per frame with the same data will give you the correct interaction the second time.
Scrolling Elements
Elements are configured as scrollable with the .clip configuration. Clipping instructs the renderer to not draw any pixels outside the clipped element's boundaries, and by specifying the .childOffset field, the clipped element's contents can be shifted around to provide "scrolling" behaviour.
You can either calculate scrolling yourself and simply provide the current offset each frame to .childOffset, or alternatively, Clay provides a built in mechanism for tracking and updating scroll container offsets, detailed below.
To make scroll containers respond to mouse wheel and scroll events, two functions need to be called before BeginLayout():
Clay_Vector2 mousePosition = { x, y };
// Reminder: Clay_SetPointerState must be called before Clay_UpdateScrollContainers otherwise it will have no effect
Clay_SetPointerState(mousePosition);
// Clay_UpdateScrollContainers needs to be called before Clay_BeginLayout for the position to avoid a 1 frame delay
Clay_UpdateScrollContainers(
true, // Enable drag scrolling
scrollDelta, // Clay_Vector2 scrollwheel / trackpad scroll x and y delta this frame
float deltaTime, // Time since last frame in seconds as a float e.g. 8ms is 0.008f
);
// ...
// Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
// Scrolling contents
}
// .childOffset can be provided directly if you would prefer to manage scrolling outside of clay
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
// Scrolling contents
}More specific details can be found in the docs for Clay_UpdateScrollContainers, Clay_SetPointerState, Clay_ClipElementConfig and Clay_GetScrollOffset.
Floating Elements ("Absolute" Positioning)
All standard elements in clay are laid out on top of, and _within_ their parent, positioned according to their parent's layout rules, and affect the positioning and sizing of siblings.
"Floating" is configured with the CLAY_FLOATING() macro. Floating elements don't affect the parent they are defined in, or the position of their siblings.
They also have a z-index, and as a result can intersect and render over the top of other elements.
A classic example use case for floating elements is tooltips and modals.
// The two text elements will be laid out top to bottom, and the floating container
// will be attached to "Outer"
CLAY(CLAY_ID("Outer"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_TEXT(text, &headerTextConfig);
CLAY(CLAY_ID("Tooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}
CLAY_TEXT(text, &headerTextConfig);
}More specific details can be found in the full Floating API.
Laying Out Your Own Custom Elements
Clay only supports a simple set of UI element primitives, such as rectangles, text and images. Clay provides a singular API for layout out custom elements:
#include "clay.h"typedef enum {
CUSTOM_ELEMENT_TYPE_MODEL,
CUSTOM_ELEMENT_TYPE_VIDEO
} CustomElementType;
// A rough example of how you could handle laying out 3d models in your UI
typedef struct {
CustomElementType type;
union {
Model model;
Video video;
// ...
};
} CustomElementData;
Model myModel = Load3DModel(filePath);
CustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel }
typedef struct {
void* memory;
uintptr_t offset;
} Arena;
// During init
Arena frameArena = (Arena) { .memory = malloc(1024) };
// Custom elements only take a single pointer, so we need to store the data somewhere
CustomElementData modelData = (CustomElementData )(frameArena.memory + frameArena.offset);
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
frameArena.offset += sizeof(CustomElementData);
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
// Later during your rendering
switch (renderCommand->commandType) {
// ...
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
// Your extended struct is passed through
CustomElementData *customElement = renderCommand->config.customElementConfig->customData;
if (!customElement) continue;
switch (customElement->type) {
case CUSTOM_ELEMENT_TYPE_MODEL: {
// Render your 3d model here
break;
}
case CUSTOM_ELEMENT_TYPE_VIDEO: {
// Render your video here
break;
}
// ...
}
break;
}
}
More specific details can be found in the full Custom Element API.
Transitions
Clay includes a "Transition" API, which allows you to smoothly animate / tween from one state to another.
Both layout-affecting properties such as
width and height, as well as non-layout properties such as backgroundColor are supported.See the [transition documentation]() for more info.
// Note: for transitions to work, elements need a stable ID from one frame to the next - using loop indexes or CLAY_AUTO_ID will not work.
CLAY(CLAY_IDI("box", colors[index].id), {
.layout.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() },
.layout.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
.backgroundColor = boxColor,
.overlayColor = Clay_Hovered() ? (Clay_Color) { 140, 140, 140, 80 } : (Clay_Color) { 255, 255, 255, 0 },
// Transitions will activate once a handler function is defined.
.transition = {
.handler = Clay_EaseOut,
.duration = 0.5f,
// A "flag" enum is used to specify which properties to transition, use a bitwise OR (|) to construct the flags.
.properties = CLAY_TRANSITION_PROPERTY_WIDTH | CLAY_TRANSITION_PROPERTY_POSITION | CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR | CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR,
.enter = { .setInitialState = EnterExitSlideUp },
.exit = { .setFinalState = EnterExitSlideUp },
}
}) {
Clay_OnHover(HandleCellButtonInteraction, (void*)(uint64_t)index);
CLAY_TEXT(((Clay_String) { .length = 2, .chars = colors[index].stringId, .isStaticallyAllocated = true }), {
.fontSize = 32,
.textColor = colors[index].id > 29 ? (Clay_Color) {255, 255, 255, 255} : (Clay_Color) {154, 123, 184, 255 }
});
}<video src="https://github.com/user-attachments/assets/a8e5cd88-f0da-4fad-acd0-81f253436bc7" controls></video>
_An example of the transition API action can be found at examples/raylib-transitions_
Retained Mode Rendering
Clay was originally designed for Immediate Mode rendering - where the entire UI is redrawn every frame. This may not be possible with your platform, renderer design or performance constraints.
There are some general techniques that can be used to integrate clay into a retained mode rendering system:
- Clay_RenderCommand includes the uint32_t id that was used to declare the element. If unique ids are used, these can be mapped to persistent graphics objects across multiple frames / layouts.
- Render commands are culled automatically to only currently visible elements, and Clay_RenderCommand is a small enough struct that you can simply compare the memory of two render commands with matching IDs to determine if the element is "dirty" and needs to be re-rendered or updated.
For a worked example, see the provided HTML renderer. This renderer converts clay layouts into persistent HTML documents with minimal changes per frame.
Visibility Culling
Clay provides a built-in visibility-culling mechanism that is enabled by default. It will only output render commands for elements that are visible - that is, at least one pixel of their bounding box is inside the viewport.
This culling mechanism can be disabled via the use of the #define CLAY_DISABLE_CULLING directive. See Preprocessor Directives for more information.
Preprocessor Directives
Clay supports C preprocessor directives to modulate functionality at compile time. These can be set either in code using
#define CLAY_DISABLE_CULLING or on the command line when compiling using the appropriate compiler specific arguments, e.g. clang -DCLAY_DISABLE_CULLING main.c ...The supported directives are:
- CLAY_WASM - Required when targeting Web Assembly.
- CLAY_DLL - Required when creating a .Dll file.
Bindings for non C
Clay is usable out of the box as a .h include in both C99 and C++20 with designated initializer support.
There are also supported bindings for other languages, including:
- Odin Bindings
- Rust Bindings
Other implementations
Clay has also been implemented in other languages:
- glay - Go line-by-line rewrite with readability as main goal.
- totallygamerjet/clay - Port using cxgo, a C to Go transpiler.
- goclay - Go line-by-line rewrite closely matching the reference.
Debug Tools
Clay includes built-in UI debugging tools, similar to the "inspector" in browsers such as Chrome or Firefox. These tools are included in clay.h, and work by injecting additional render commands into the output Clay_RenderCommandArray.
As long as the renderer that you're using works correctly, no additional setup or configuration is required to use the debug tools.
To enable the debug tools, use the function Clay_SetDebugModeEnabled(bool enabled). This boolean is persistent and does not need to be set every frame.
The debug tooling by default will render as a panel to the right side of the screen, compressing your layout by its width. The default width is 400 and is currently configurable via the direct mutation of the internal variable Clay__debugViewWidth, however this is an internal API and is potentially subject to change.
<img width="1506" alt="Screenshot 2024-09-12 at 12 54 03 PM" src="https://github.com/user-attachments/assets/2d122658-3305-4e27-88d6-44f08c0cb4e6">
_The official Clay website with debug tooling visible_
Running more than one Clay instance
Clay allows you to run more than one instance in a program. To do this, Clay_Initialize returns a Clay_Context* reference. You can activate a specific instance using Clay_SetCurrentContext. If Clay_SetCurrentContext is not called, then Clay will default to using the context from the most recently called Clay_Initialize.
⚠ Important: Do not render instances across different threads simultaneously, as Clay does not currently support proper multi-threading.
``c++
// Define separate arenas for the instances.
Clay_Arena arena1, arena2;
// ... allocate arenas
// Initialize both instances, storing the context for each one.
Clay_Context* instance1 = Clay_Initialize(arena1, layoutDimensions, errorHandler);
Clay_Context* instance2 = Clay_Initialize(arena2, layoutDimensions, errorHandler);
// In the program's render function, activate each instance before executing clay commands and macros.
Clay_SetCurrentContext(instance1);
Clay_BeginLayout();
// ... declare layout for instance1
Clay_RenderCommandArray renderCommands1 = Clay_EndLayout(deltaTime);
render(renderCommands1);
// Switch to the second instance
Clay_SetCurrentContext(instance2);
Clay_BeginLayout();
// ... declare layout for instance2
Clay_RenderCommandArray renderCommands2 = Clay_EndLayout(deltaTime);
render(renderCommands2);
Clay_MinMemorySizeAPI
Naming Conventions
- "CAPITAL_LETTERS()" are used for macros.
- "Clay__" ("Clay" followed by double underscore) is used for internal functions that are not intended for use and are subject to change.
- "Clay_" ("Clay" followed by single underscore) is used for external functions that can be called by the user.Public Functions
Lifecycle for public functions
At startup / initialization time, run once
->Clay_CreateArenaWithCapacityAndMemory->Clay_Initialize->Clay_SetMeasureTextFunctionClay_SetLayoutDimensionsEach Frame
->Clay_SetPointerState->Clay_UpdateScrollContainers->Clay_BeginLayout->CLAY() etc...->Clay_EndLayoutuint32_t Clay_MinMemorySize()---
Clay_MinMemorySize
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity, void *memory)Returns the minimum amount of memory in bytes that clay needs to accommodate the current CLAY_MAX_ELEMENT_COUNT.
---
Clay_CreateArenaWithCapacityAndMemory
Clay_ArenaCreates a
struct with the given capacity and base memory pointer, which can be passed to Clay_Initialize.void Clay_SetMeasureTextFunction(Clay_Dimensions (measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig config, void userData), void userData)---
Clay_SetMeasureTextFunction
width, heightTakes a pointer to a function that can be used to measure the
dimensions of a string. Used by clay during layout to determine CLAY_TEXT element sizing and wrapping.void Clay_ResetMeasureTextCache(void)Note 1: This string is not guaranteed to be null terminated. Clay saves significant performance overhead by using slices when wrapping text instead of having to clone new null terminated strings. If your renderer does not support ptr, length style strings (e.g. Raylib), you will need to clone this to a new C string before rendering.
Note 2: It is essential that this function is as fast as possible. For text heavy use-cases this function is called many times, and despite the fact that clay caches text measurements internally, it can easily become the dominant overall layout cost if the provided function is slow. This is on the hot path!
---
Clay_ResetMeasureTextCache
void Clay_SetMaxElementCount(int32_t maxElementCount)Clay caches measurements from the provided MeasureTextFunction, and this will be sufficient for the majority of use-cases. However, if the measurements can depend on external factors that clay does not know about, like DPI changes, then the cached values may be incorrect. When one of these external factors changes, Clay_ResetMeasureTextCache can be called to force clay to recalculate all string measurements in the next frame.
---
Clay_SetMaxElementCount
void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount)Sets the internal maximum element count that will be used in subsequent Clay_Initialize() and Clay_MinMemorySize() calls, allowing clay to allocate larger UI hierarchies.
Note: You will need to reinitialize clay, after calling Clay_MinMemorySize() to calculate updated memory requirements.
---
Clay_SetMaxMeasureTextCacheWordCount
Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler)Sets the internal text measurement cache size that will be used in subsequent Clay_Initialize() and Clay_MinMemorySize() calls, allowing clay to allocate more text. The value represents how many separate words can be stored in the text measurement cache.
Note: You will need to reinitialize clay, after calling Clay_MinMemorySize() to calculate updated memory requirements.
---
Clay_Initialize
void Clay_SetCurrentContext(Clay_Context* context)Initializes the internal memory mapping, sets the internal dimensions for layout, and binds an error handler for clay to use when something goes wrong. Returns a Clay_Context* that can optionally be given to Clay_SetCurrentContext to allow running multiple instances of clay in the same program, and sets it as the current context. See Running more than one Clay instance.
Reference: Clay_Arena, Clay_ErrorHandler, Clay_SetCurrentContext
---
Clay_SetCurrentContext
Clay_Context* Clay_GetCurrentContext()Sets the context that subsequent clay commands will operate on. You can get this reference from Clay_Initialize or Clay_GetCurrentContext. See Running more than one Clay instance.
---
Clay_GetCurrentContext
void Clay_SetLayoutDimensions(Clay_Dimensions dimensions)Returns the context that clay commands are currently operating on, or null if no context has been set. See Running more than one Clay instance.
---
Clay_SetLayoutDimensions
void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown)Sets the internal layout dimensions. Cheap enough to be called every frame with your screen dimensions to automatically respond to window resizing, etc.
---
Clay_SetPointerState
trueSets the internal pointer position and state (i.e. current mouse / touch position) and recalculates overlap info, which is used for mouseover / click calculation (via Clay_PointerOver and updating scroll containers with Clay_UpdateScrollContainers. isPointerDown should represent the current state this frame, e.g. it should be
for the entire duration the left mouse button is held down. Clay has internal handling for detecting click / touch start & end.void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime)---
Clay_UpdateScrollContainers
scrollDeltaThis function handles scrolling of containers. It responds to both
, which represents mouse wheel or trackpad scrolling this frame, as well as "touch scrolling" on mobile devices, or "drag scrolling" with a mouse or similar device.enableDragScrollingTouch / drag scrolling only occurs if the
parameter istrue, and Clay_SetPointerState has been called this frame. As a result, you can simply always call it withfalseas the first argument if you want to disable touch scrolling.deltaTimeis the time in seconds since the last frame (e.g. 0.016 is 16 milliseconds), and is used to normalize & smooth scrolling across different refresh rates.Clay_Vector2 Clay_GetScrollOffset()---
Clay_GetScrollOffset
.childOffsetReturns the internally stored scroll offset for the currently open element.
Generally intended for use with clip elements and the
field to create scrolling containers.See Scrolling Elements for more details.
// Create a horizontally scrolling container
CLAY(CLAY_ID("ScrollContainer"), {
.clip = { .horizontal = true, .childOffset = Clay_GetScrollOffset() }
})
---void Clay_BeginLayout()Clay_BeginLayout
Clay_RenderCommandArray Clay_EndLayout()Prepares clay to calculate a new layout. Called each frame / layout before any of the Element Macros.
---
Clay_EndLayout
bool Clay_Hovered()Ends declaration of element macros and calculates the results of the current layout. Renders a Clay_RenderCommandArray containing the results of the layout calculation.
---
Clay_Hovered
trueCalled during layout declaration, and returns
if the pointer position previously set withClay_SetPointerStateis inside the bounding box of the currently open element. Note: this is based on the element's position from the last frame.void Clay_OnHover(void (onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, void userData), void *userData)---
Clay_OnHover
Clay_SetPointerStateCalled during layout declaration, this function allows you to attach a function pointer to the currently open element that will be called once per layout if the pointer position previously set with
is inside the bounding box of the currently open element. See Clay_PointerData for more information on thepointerDataargument.
void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {
ButtonData buttonData = (ButtonData )userData;
// Pointer state allows you to detect mouse down / hold / release
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
// Do some click handling
NavigateTo(buttonData->link);
}
}
ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
Clay_OnHover(HandleButtonInteraction, &buttonData);
CLAY_TEXT(CLAY_STRING("Click me!"), &headerTextConfig);
}
---bool Clay_PointerOver(Clay_ElementId id)Clay_PointerOver
trueReturns
if the pointer position previously set withClay_SetPointerStateis inside the bounding box of the layout element with the providedid. Note: this is based on the element's position from the last frame. If frame-accurate pointer overlap detection is required, perhaps in the case of significant change in UI layout between frames, you can simply run your layout code twice that frame. The second call toClay_PointerOverwill be frame-accurate.Clay_ElementId Clay_GetOpenElementId()---
Clay_GetOpenElementId
Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id)Returns the Clay_ElementId of the currently open element. Useful for getting the ID of elements opened with CLAY_AUTO_ID.
---
Clay_GetScrollContainerData
Clay_ElementData Clay_GetElementData(Clay_ElementId id)Returns Clay_ScrollContainerData for the scroll container matching the provided ID. This function allows imperative manipulation of scroll position, allowing you to build things such as scroll bars, buttons that "jump" to somewhere in a scroll container, etc.
---
Clay_GetElementData
Clay_ElementId Clay_GetElementId(Clay_String idString)Returns Clay_ElementData for the element matching the provided ID.
Used to retrieve information about elements such as their final calculated bounding box.---
Clay_GetElementId
CLAY(...configuration) { ...children }Returns a Clay_ElementId for the provided id string, used for querying element info such as mouseover state, scroll container data, etc.
Element Macros
CLAY()
UsageClay_BeginLayout()Lifecycle
->CLAY()->Clay_EndLayout()CLAY(0)Notes
CLAY opens a generic empty container, that is configurable and supports nested children.
CLAY requires a parameter, so if you want to create an element without any configuration, use.Examples
// Define an element with 16px of x and y padding
CLAY(CLAY_ID("Outer"), { .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
// A nested child element
CLAY(CLAY_ID("SideBar"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
// Children laid out top to bottom with a 16 px gap between them
}
// A vertical scrolling container with a colored background
CLAY(CLAY_ID("ScrollContainer"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10),
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
}) {
// child elements
}
}
---CLAY_AUTO_ID()
A version of the core CLAY() element creation macro that generates an ID automatically instead of requiring it as the first argument.
Note that under the hood this ID is generated in the same way as CLAY_ID_LOCAL(), which is based on the element's position in the hierarchy, and may chance between layout calls if elements are added / removed from the hierarchy before the element is defined. As a result, for transitions & retained mode backends to work correctly, IDs should be specified.
// Note that CLAY_AUTO_ID only takes one argument: the configuration
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
// A nested child element
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
// Children laid out top to bottom with a 16 px gap between them
}
// A vertical scrolling container with a colored background
CLAY_AUTO_ID({
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10),
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
}) {
// child elements
}
}
---CLAY_TEXT(Clay_String textContents, Clay_TextElementConfig *textConfig);CLAY_TEXT()
UsageClay_BeginLayout()Lifecycle
->CLAY_TEXT()->Clay_EndLayout()Clay_TextElementConfigNotes
TEXT is a measured, auto-wrapped text element. It uses
to configure text specific options.Clay_TextElementConfigNote that
usesuint32_t fontId. Font ID to font asset mapping is managed in user code and passed to render commands.Struct API (Pseudocode)
// CLAY_TEXT(text, { .member = value }) supports these options
Clay_TextElementConfig {
Clay_Color textColor {
float r; float g; float b; float a;
};
uint16_t fontId;
uint16_t fontSize;
uint16_t letterSpacing;
uint16_t lineHeight;
Clay_TextElementConfigWrapMode wrapMode {
CLAY_TEXT_WRAP_WORDS (default),
CLAY_TEXT_WRAP_NEWLINES,
CLAY_TEXT_WRAP_NONE,
};
Clay_TextAlignment textAlignment {
CLAY_TEXT_ALIGN_LEFT (default),
CLAY_TEXT_ALIGN_CENTER,
CLAY_TEXT_ALIGN_RIGHT,
};
void *userData;
};
Fields.textColorCLAY_TEXT(text, { .textColor = {120, 120, 120, 255} })rgbaUses Clay_Color. Conventionally accepts
float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout..fontId---
CLAY_TEXT(text, { .fontId = FONT_ID_LATO })fontIdIt's up to the user to load fonts and create a mapping from
to a font that can be measured and rendered..fontSize---
CLAY_TEXT(text, { .fontSize = 16 })x pixels tallFont size is generally thought of as
, but interpretation is left up to the user & renderer..letterSpacing---
CLAY_TEXT(text, { .letterSpacing = 1 }).letterSpacingresults in horizontal white space between individual rendered characters..lineHeight---
CLAY_TEXT(text, { .lineHeight = 20 }).lineHeight- when non zero - forcibly sets theheightof each wrapped line of text to.lineheightpixels tall. Will affect the layout of both parents and siblings. A value of0will use the measured height of the font..wrapMode---
CLAY_TEXT(text, { .wrapMode = CLAY_TEXT_WRAP_NONE }).wrapModespecifies under what conditions text should wrap.CLAY_TEXT_WRAP_WORDSAvailable options are:
-
(default) - Text will wrap on whitespace characters as container width shrinks, preserving whole words.CLAY_TEXT_WRAP_NEWLINES
-- will only wrap when encountering newline characters.CLAY_TEXT_WRAP_NONE
-- Text will never wrap even if its container is compressed beyond the text measured width..textAlignment---
CLAY_TEXT(text, { .textAlignment = CLAY_TEXT_ALIGN_CENTER }).textAlignmentcontrols how wrapping text lines are aligned. If you want to control the alignment of single lines of text, instead use thechildAlignmentproperty of the parent layout element.CLAY_TEXT_ALIGN_LEFTAvailable options are:
-
(default)CLAY_TEXT_ALIGN_CENTER
-CLAY_TEXT_ALIGN_RIGHT
----
Examples
// Define a font somewhere in your code
const uint32_t FONT_ID_LATO = 3;
// ..
CLAY_TEXT(CLAY_STRING("John Smith"), { .fontId = FONT_ID_LATO, .fontSize = 24, .textColor = {255, 0, 0, 255} });
// Rendering example
Font fontToUse = LoadedFonts[renderCommand->renderData.text.fontId];
RenderingClay_RenderCommandElement is subject to culling. Otherwise, multiple
s withcommandType = CLAY_RENDER_COMMAND_TYPE_TEXTmay be created, one for each wrapped line of text.Clay_RenderCommand.textContentwill be populated with aClay_String_slice_ of the original string passed in (i.e. wrapping doesn't reallocate, it just returns aClay_Stringpointing to the start of the new line with alength)Clay_ElementId CLAY_ID(STRING_LITERAL idString)---
CLAY_ID()
charCLAY_ID() is used to generate and attach a Clay_ElementId to a layout element during declaration.
Note this macro only works with String literals and won't compile if used with a
variable. To use a heap allocatedcharstring as an ID, use CLAY_SID.To regenerate the same ID outside of layout declaration when using utility functions such as Clay_PointerOver, use the Clay_GetElementId function.
Examples
// Tag a button with the Id "Button"
CLAY(CLAY_ID("Button"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }
}) {
// ...children
}
// Later on outside of layout code
bool buttonIsHovered = Clay_PointerOver(Clay_GetElementId("Button"));
if (buttonIsHovered && leftMouseButtonPressed) {
// ... do some click handling
}
---Clay_ElementId CLAY_SID(Clay_String idString)CLAY_SID()
char *A version of CLAY_ID that can be used with heap allocated
data. The underlyingchardata will not be copied internally and should live until at least the next frame.Clay_ElementId CLAY_IDI(STRING_LITERAL idString, int32_t index)---
CLAY_IDI()
char *labelAn offset version of CLAY_ID. Generates a Clay_ElementId string id from the provided
, combined with theint index.forUsed for generating ids for sequential elements (such as in a
loop) without having to construct dynamic strings at runtime.charNote this macro only works with String literals and won't compile if used with a
variable. To use a heap allocatedcharstring as an ID, use CLAY_SIDI.Clay_ElementId CLAY_SIDI(Clay_String idString, int32_t index)---
CLAY_SIDI()
char *A version of CLAY_IDI that can be used with heap allocated
data. The underlyingchardata will not be copied internally and should live until at least the next frame.Clay_ElementId CLAY_ID_LOCAL(STRING_LITERAL idString)---
CLAY_ID_LOCAL()
Usage
Clay_BeginLayout()Lifecycle
->CLAY(->CLAY_ID_LOCAL()->)->Clay_EndLayout()charNotes
CLAY_ID_LOCAL() is used to generate and attach a Clay_ElementId to a layout element during declaration.
Unlike CLAY_ID which needs to be globally unique, a local ID is based on the ID of it's parent and only needs to be unique among its siblings.
As a result, local id is suitable for use in reusable components and loops.
Note this macro only works with String literals and won't compile if used with a
variable. To use a heap allocatedcharstring as an ID, use CLAY_SID_LOCAL.Examples
void RenderHeaderButton(ButtonData button) {
CLAY({
.id = CLAY_ID_LOCAL("HeaderButton"),
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }
}) {
// ...children
}
}
for (int i = 0; i < headerButtons.length; i++) {
RenderHeaderButton(headerButtons.items[i]);
}
---Clay_ElementId CLAY_SID_LOCAL(Clay_String idString)CLAY_SID_LOCAL()
char *A version of CLAY_ID_LOCAL that can be used with heap allocated
data. The underlyingchardata will not be copied internally and should live until at least the next frame.Clay_ElementId CLAY_IDI_LOCAL(STRING_LITERAL idString, int32_t index)---
CLAY_IDI_LOCAL()
char *labelAn offset version of CLAY_ID_LOCAL. Generates a Clay_ElementId string id from the provided
, combined with theint index.forUsed for generating ids for sequential elements (such as in a
loop) without having to construct dynamic strings at runtime.charNote this macro only works with String literals and won't compile if used with a
variable. To use a heap allocatedcharstring as an ID, use CLAY_SIDI_LOCAL.Clay_ElementId CLAY_SIDI_LOCAL(Clay_String idString, int32_t index)---
CLAY_SIDI_LOCAL()
char *A version of CLAY_IDI_LOCAL that can be used with heap allocated
data. The underlyingchardata will not be copied internally and should live until at least the next frame.CLAY()---
Data Structures & Definitions
Clay_ElementDeclaration
The Clay_ElementDeclaration struct is the only argument to themacro and provides configuration options for layout elements.
typedef struct {
Clay_LayoutConfig layout;
Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius;
Clay_AspectRatioElementConfig aspectRatio;
Clay_ImageElementConfig image;
Clay_FloatingElementConfig floating;
Clay_CustomElementConfig custom;
Clay_ClipElementConfig clip;
Clay_BorderElementConfig border;
void *userData;
} Clay_ElementDeclaration;
Fields.layout-Clay_LayoutConfigCLAY(CLAY_ID("Element"), { .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }).backgroundColorUses Clay_LayoutConfig. Controls various settings related to _layout_, which can be thought of as "the size and position of this element and its children".
---
-Clay_ColorCLAY(CLAY_ID("Element"), { .backgroundColor = { 120, 120, 120, 255 } } })rgbaUses Clay_Color. Conventionally accepts
float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout..overlayColor---
-Clay_ColorCLAY(CLAY_ID("Element"), { .overlayColor = { 255, 120, 120, 255 } } })rgbaUses Clay_Color. Conventionally accepts
float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout..overlayColorSpecifying
will cause two new render commands to be emitted:OVERLAY_COLOR_BEGINimmediately, andOVERLAY_COLOR_ENDafter this elements subtree has been emitted.mix(source, target, alpha)This instructs the renderer to begin applying a color overlay to this element, and all child elements. The color overlay effect is similar to glsl's
. As a result, the strength of the color overlay is controlled by the.achannel of.overlayColor..cornerRadius
Zero alpha means "all child colors remain the same", full alpha means "all child colours are replaced by the RGB overlayColor", and values in between mean "lerp from the child color to the overlay color by the alpha".This can be used as a cheap replacement for alpha / opacity to "fade in / out", by applying (and potentially transitioning) an overlay color to the same color as the background with full alpha.
---
-Clay_CornerRadiusCLAY(CLAY_ID("Element"), { .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })0Defines the radius in pixels for the arc of rectangle corners (
is square,rectangle.width / 2is circular).CLAY_CORNER_RADIUS(radius)Note that the
function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g.CLAY_BORDER({ .cornerRadius = CLAY_CORNER_RADIUS(10) }).aspectRatio---
-Clay_AspectRatioElementConfigCLAY(CLAY_ID("Element"), { .aspectRatio = 1 }).imageUses Clay_AspectRatioElementConfig. Configures the element as an aspect ratio scaling element. Especially useful for rendering images, but can also be used to enforce a fixed width / height ratio of other elements.
---
-Clay_ImageElementConfigCLAY(CLAY_ID("Element"), { .image = { .imageData = &myImage } })IMAGEUses Clay_ImageElementConfig. Configures the element as an image element. Causes a render command with type
to be emitted..floating---
-Clay_FloatingElementConfigCLAY(CLAY_ID("Element"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }).customUses Clay_FloatingElementConfig. Configures the element as an floating element, which allows it to stack "in front" and "on top" of other elements without affecting sibling or parent size or position.
---
-Clay_CustomElementConfigCLAY(CLAY_ID("Element"), { .custom = { .customData = &my3DModel } })CUSTOMUses Clay_CustomElementConfig. Configures the element as a custom element, which allows you to pass custom data through to the renderer. Causes a render command with type
to be emitted..clip---
-Clay_ClipElementConfigCLAY(CLAY_ID("Element"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }).borderUses Clay_ClipElementConfig. Configures the element as a clip element, which causes child elements to be clipped / masked if they overflow, and together with the functions listed in Scrolling Elements enables scrolling of child contents.
<img width="580" alt="An image demonstrating the concept of clipping which prevents rendering of a child elements pixels if they fall outside the bounds of the parent element." src="https://github.com/user-attachments/assets/2eb83ff9-e186-4ea4-8a87-d90cbc0838b5">
---
-Clay_BorderElementConfigCLAY(CLAY_ID("Element"), { .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })BORDERUses Clay_BorderElementConfig. Configures the element as a border element, which instructs the renderer to draw coloured border lines along the perimeter of this element's bounding box. Causes a render command with type
to be emitted..userData---
-void *CLAY(CLAY_ID("Element"), { .userData = &extraData })Transparently passes a pointer through to the corresponding Clay_RenderCommandss generated by this element.
---
Examples
// Declare a reusable rectangle config, with a purple color and 10px rounded corners
Clay_RectangleElementConfig rectangleConfig = (Clay_RectangleElementConfig) { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) };
// Declare a rectangle element using a reusable config
CLAY(CLAY_ID("Box"), rectangleConfig) {}
// Declare a retangle element using an inline config
CLAY(CLAY_ID("BoxInline"), { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {
// child elements
}
// Declare a scrolling container with a colored background
CLAY(CLAY_ID("ScrollingContainer"), {
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10)
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
) {
// child elements
}
Element is subject to culling. Otherwise, a singleClay_RenderCommands withcommandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLEwill be created, withrenderCommand->renderData.rectanglecontaining a pointer to the element's Clay_RectangleElementConfig.Clay_LayoutConfig
Clay_LayoutConfig is used for configuring _layout_ options (i.e. options that affect the final position and size of an element, its parents, siblings, and children)
Struct API (Pseudocode)
// CLAY({ .layout = { ...fields } }) supports these options
Clay_LayoutConfig {
Clay_LayoutDirection layoutDirection = CLAY_LEFT_TO_RIGHT (default) | CLAY_TOP_TO_BOTTOM;
Clay_Padding padding {
u16 left; u16 right; u16 top; u16 bottom;
};
uint16_t childGap;
Clay_ChildAlignment childAlignment {
.x = CLAY_ALIGN_X_LEFT (default) | CLAY_ALIGN_X_CENTER | CLAY_ALIGN_X_RIGHT;
.y = CLAY_ALIGN_Y_TOP (default) | CLAY_ALIGN_Y_CENTER | CLAY_ALIGN_Y_BOTTOM;
};
Clay_Sizing sizing { // Recommended to use the provided macros here - see #sizing for more in depth explanation
.width = CLAY_SIZING_FIT(float min, float max) (default) | CLAY_SIZING_GROW(float min, float max) | CLAY_SIZING_FIXED(float width) | CLAY_SIZING_PERCENT(float percent)
.height = CLAY_SIZING_FIT(float min, float max) (default) | CLAY_SIZING_GROW(float min, float max) | CLAY_SIZING_FIXED(float height) | CLAY_SIZING_PERCENT(float percent)
}; // See CLAY_SIZING_GROW() etc for more details
};
Fields.layoutDirection-Clay_LayoutDirectionCLAY(CLAY_ID("Element"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })CLAY_LEFT_TO_RIGHTControls the axis / direction in which child elements are laid out. Available options are
(default) andCLAY_TOP_TO_BOTTOM..padding_Did you know that "left to right" and "top to bottom" both have 13 letters?_
<img width="580" alt="Screenshot 2024-08-22 at 11 10 27 AM" src="https://github.com/user-attachments/assets/7008aa47-8826-4338-9257-8bc83f7813ce">
---
-Clay_PaddingCLAY(CLAY_ID("Element"), { .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } }).childGapControls white-space "padding" around the outside of child elements.
<img width="486" alt="Screenshot 2024-08-22 at 10 50 49 AM" src="https://github.com/user-attachments/assets/b454fa36-92d5-4b1d-bf8b-e4c25428e9de">
---
-uint16_tCLAY(CLAY_ID("Element"), { .layout = { .childGap = 16 } }).layoutDirectionControls the white-space between child elements as they are laid out. When
isCLAY_LEFT_TO_RIGHT(default), this will be horizontal space, whereas forCLAY_TOP_TO_BOTTOMit will be vertical space..childAlignment<img width="600" alt="Screenshot 2024-08-22 at 11 05 15 AM" src="https://github.com/user-attachments/assets/fa0dae1f-1936-47f6-a299-634bd7d40d58">
---
-Clay_ChildAlignmentCLAY(CLAY_ID("Element"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })Controls the alignment of children relative to the height and width of the parent container. Available options are:
.x = CLAY_ALIGN_X_LEFT (default) | CLAY_ALIGN_X_CENTER | CLAY_ALIGN_X_RIGHT;
.y = CLAY_ALIGN_Y_TOP (default) | CLAY_ALIGN_Y_CENTER | CLAY_ALIGN_Y_BOTTOM;
<img width="1030" alt="Screenshot 2024-08-22 at 11 25 16 AM" src="https://github.com/user-attachments/assets/be61b4a7-db4f-447c-b6d6-b2d4a91fc664">.sizing---
-Clay_SizingCLAY(CLAY_ID("Element"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } }).widthControls how final width and height of element are calculated. The same configurations are available for both the
and.heightaxis. There are several options:CLAY_SIZING_FIT(float min, float max) (default)-
- The element will be sized to fit its children (plus padding and gaps), up tomax. Ifmaxis left unspecified, it will default toFLOAT_MAX. When elements are compressed to fit into a smaller parent, this element will not shrink belowmin.CLAY_SIZING_GROW(float min, float max)-
- The element will grow to fill available space in its parent, up tomax. Ifmaxis left unspecified, it will default toFLOAT_MAX. When elements are compressed to fit into a smaller parent, this element will not shrink belowmin.CLAY_SIZING_FIXED(float fixed)-
- The final size will always be exactly the providedfixedvalue. Shorthand forCLAY_SIZING_FIT(fixed, fixed)CLAY_SIZING_PERCENT(float percent)-
- Final size will be a percentage of parent size, minus padding and child gaps.percentis assumed to be a float between0and1.<img width="1056" alt="Screenshot 2024-08-22 at 2 10 33 PM" src="https://github.com/user-attachments/assets/1236efb1-77dc-44cd-a207-7944e0f5e500">
<img width="1141" alt="Screenshot 2024-08-22 at 2 19 04 PM" src="https://github.com/user-attachments/assets/a26074ff-f155-4d35-9ca4-9278a64aac00">
Example Usage
CLAY(CLAY_ID("Button"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) {
// Children will be laid out vertically with 16px of padding around and between
}
---CLAY(CLAY_ID("Element"), { .image = { ...image config } }) {}Clay_ImageElementConfig
UsageClay_ImageElementConfig configures a clay element to render an image as its background.
Struct API (Pseudocode)
Clay_ImageElementConfig {
void * imageData;
};
Fields.imageData-void *CLAY(CLAY_ID("Image"), { .image = { .imageData = &myImage } }) {}.imageDatais a generic void pointer that can be used to pass through image data to the renderer.
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Note that when rendering, .imageData will be void* type.
CLAY(CLAY_ID("Image"), { .image = { .imageData = &profilePicture } }) {}
Examples// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Declare a reusable image config
Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };
// Declare an image element using a reusable config
CLAY(CLAY_ID("Image"), { .image = imageConfig }) {}
// Declare an image element using an inline config
CLAY(CLAY_ID("ImageInline"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
// Rendering example
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
RenderingClay_RenderCommandElement is subject to culling. Otherwise, a single
s withcommandType = CLAY_RENDER_COMMAND_TYPE_IMAGEwill be created. The user will need to accessrenderCommand->renderData.image.imageDatato retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blendrenderCommand->renderData.image.backgroundColorwith the image.CLAY(CLAY_ID("Aspect"), { .aspectRatio = 16.0 / 9.0 }) {}---
Clay_AspectRatioElementConfig
Usage
Clay_AspectRatioElementConfig configures a clay element to enforce a fixed width / height ratio in its final dimensions. Mostly used for image elements, but can also be used for non image elements.
Struct API (Pseudocode)
Clay_AspectRatioElementConfig {
float aspectRatio;
};
Fields.aspectRatio-floatCLAY(CLAY_ID("Aspect"), { .aspectRatio = { .aspectRatio = 16.0 / 9.0 } }) {}CLAY(CLAY_ID("Aspect"), { .aspectRatio = 16.0 / 9.0 }) {}or alternatively, as C will automatically pass the value to the first nested struct field:
Examples
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Declare an image element that will grow along the X axis while maintaining its original aspect ratio
CLAY(CLAY_ID("ProfilePicture"), {
.layout = { .width = CLAY_SIZING_GROW() },
.aspectRatio = profilePicture.width / profilePicture.height,
.image = { .imageData = &profilePicture },
}) {}
---CLAY(CLAY_ID("Image"), { .image = { ...image config } }) {}Clay_ImageElementConfig
UsageClay_ImageElementConfig configures a clay element to render an image as its background.
Struct API (Pseudocode)
Clay_ImageElementConfig {
void * imageData;
};
Fields.imageData-void *CLAY(CLAY_ID("Image"), { .image = { .imageData = &myImage } }) {}.imageDatais a generic void pointer that can be used to pass through image data to the renderer.
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Note that when rendering, .imageData will be void* type.
CLAY(CLAY_ID("Image"), { .image = { .imageData = &profilePicture } }) {}
Note: for an image to maintain its original aspect ratio when using dynamic scaling, the .aspectRatio config option must be used.Examples
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Declare a reusable image config
Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };
// Declare an image element using a reusable config
CLAY(CLAY_ID("Image"), { .image = imageConfig }) {}
// Declare an image element using an inline config
CLAY(CLAY_ID("ImageInline"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
// Rendering example
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
RenderingClay_RenderCommandElement is subject to culling. Otherwise, a single
s withcommandType = CLAY_RENDER_COMMAND_TYPE_IMAGEwill be created. The user will need to accessrenderCommand->renderData.image.imageDatato retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blendrenderCommand->renderData.image.backgroundColorwith the image.CLAY(CLAY_ID("ScrollBox"), { .clip = { ...clip config } }) {}---
Clay_ClipElementConfig
Usage
Clay_ClipElementConfigNotes
configures the element as a clipping container, enabling masking of children that extend beyond its boundaries.Clay_SetPointerState()Note: In order to process scrolling based on pointer position and mouse wheel or touch interactions, you must call
andClay_UpdateScrollContainers()_before_ callingBeginLayout.Struct Definition (Pseudocode)
Clay_ClipElementConfig {
bool horizontal;
bool vertical;
Clay_Vector2 childOffset;
};
Fields.horizontal-boolCLAY(CLAY_ID("HorizontalScroll"), { .clip = { .horizontal = true } }).verticalEnables or disables horizontal clipping for this container element.
---
-boolCLAY(LAY_ID("VerticalScroll"), { .clip = { .vertical = true } }).childOffsetEnables or disables vertical clipping for this container element.
---
-Clay_Vector2CLAY(LAY_ID("VerticalScroll"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_STARTControls the x/y offset for child elements of this clip container. Used to control scrolling. You can either provide the vector manually if you want to manage scrolling yourself, or you can use the built in Clay_GetScrollOffset function which will manage scrolling for you automatically.
---
Rendering
Enabling clip for an element will result in two additional render commands:
-, which should create a rectangle mask with itsboundingBoxand is not subject to cullingcommandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END
-, which disables the previous rectangle mask and is not subject to cullingExamples
CLAY(CLAY_ID("ScrollOuter"), { .clip = { .vertical = true } }) {
// Create child content with a fixed height of 5000
CLAY(CLAY_ID("ScrollInner"), { .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
}
---CLAY(CLAY_ID("Border"), { .border = { ...border config } }) {}Clay_BorderElementConfig
Usage
Clay_BorderElementConfigNotes
adds borders to the edges or between the children of elements. It uses Clay_BorderElementConfig to configure border specific options.Struct Definition (Pseudocode)
typedef struct Clay_BorderElementConfig
{
Clay_Color color {
float r; float g; float b; float a;
};
Clay_BorderWidth width {
uint16_t left;
uint16_t right;
uint16_t top;
uint16_t bottom;
uint16_t betweenChildren;
};
} Clay_BorderElementConfig;
Fields.color-Clay_ColorCLAY(CLAY_ID("Border"), { .border = { .color = { 255, 0, 0, 255 } } })rgbaUses Clay_Color. Specifies the shared color for all borders configured by this element. Conventionally accepts
float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout..width---
-Clay_BorderWidthCLAY(CLAY_ID("Border"), { .border = { .width = { .left = 2, .right = 10 } } }).colorIndicates to the renderer that a border of
should be draw at the specified edges of the bounding box, inset and overlapping the box contents by.width..width.betweenChildrenThis means that border configuration does not affect layout, as the width of the border doesn't contribute to the total container width or layout position. Border containers with zero padding will be drawn over the top of child elements.
Note:
CLAY(CLAY_ID("Border"), { .border = { .width = { .betweenChildren = 2 } }, .color = COLOR_RED }).layoutDirection = CLAY_LEFT_TO_RIGHTConfigures the width and color of borders to be drawn between children. These borders will be vertical lines if the parent uses
and horizontal lines if the parent usesCLAY_TOP_TO_BOTTOM. Unlike.left, .topetc, this option will generate additional rectangle render commands representing the borders between children. As a result, the renderer does not need to specifically implement rendering for these border elements.---
Examples
// 300x300 container with a 1px red border around all the edges
CLAY(CLAY_ID("OuterBorder"), {
.layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) } },
.border = { .width = { 1, 1, 1, 1, 0 }, .color = COLOR_RED }
}) {
// ...
}
// Container with a 3px yellow bottom border
CLAY(CLAY_ID("OuterBorder"), {
.border = { .width = { .bottom = 3 }, .color = COLOR_YELLOW }
}) {
// ...
}
// Container with a 5px curved border around the edges, and a 5px blue border between all children laid out top to bottom
CLAY(CLAY_ID("OuterBorder"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
.border = { .width = { 5, 5, 5, 5, 5 }, .color = COLOR_BLUE }
}) {
// Child
// -- 5px blue border will be here --
// Child
// -- 5px blue border will be here --
// Child
}
RenderingClay_RenderCommandElement is subject to culling. Otherwise, a single
withcommandType = CLAY_RENDER_COMMAND_TYPE_BORDERrepresenting the container will be created.CLAY(CLAY_ID("Floating"), { .floating = { ...floating config } }) {}
Rendering of borders and rounded corners is left up to the user. See the provided Raylib Renderer for examples of how to draw borders using line and curve primitives.---
Clay_FloatingElementConfig
Usage
Clay_FloatingElementConfigNotes
Floating Elements defines an element that "floats" above other content. Typical use-cases include tooltips and modals.
Floating containers:
- With the default configuration, attach to the top left corner of their "parent"
- Don't affect the width and height of their parent
- Don't affect the positioning of sibling elements
- Depending on their z-index can appear above or below other elements, partially or completely occluding them
- Apart from positioning, function just like standard elements - including expanding to fit their children, etc.The easiest mental model to use when thinking about floating containers is that they are a completely separate UI hierarchy, attached to a specific x,y point on their "parent".
Floating elements uses
to configure specific options.Struct Definition (Pseudocode)
Clay_FloatingElementConfig {
Clay_Vector2 offset {
float x, float y
};
Clay_Dimensions expand {
float width, float height
};
uint32_t parentId;
int16_t zIndex;
Clay_FloatingAttachPoints attachPoints {
.element = CLAY_ATTACH_POINT_LEFT_TOP (default) | CLAY_ATTACH_POINT_LEFT_CENTER | CLAY_ATTACH_POINT_LEFT_BOTTOM | CLAY_ATTACH_POINT_CENTER_TOP | CLAY_ATTACH_POINT_CENTER_CENTER | CLAY_ATTACH_POINT_CENTER_BOTTOM | CLAY_ATTACH_POINT_RIGHT_TOP | CLAY_ATTACH_POINT_RIGHT_CENTER | CLAY_ATTACH_POINT_RIGHT_BOTTOM
.parent = CLAY_ATTACH_POINT_LEFT_TOP (default) | CLAY_ATTACH_POINT_LEFT_CENTER | CLAY_ATTACH_POINT_LEFT_BOTTOM | CLAY_ATTACH_POINT_CENTER_TOP | CLAY_ATTACH_POINT_CENTER_CENTER | CLAY_ATTACH_POINT_CENTER_BOTTOM | CLAY_ATTACH_POINT_RIGHT_TOP | CLAY_ATTACH_POINT_RIGHT_CENTER | CLAY_ATTACH_POINT_RIGHT_BOTTOM
};
Clay_PointerCaptureMode pointerCaptureMode {
CLAY_POINTER_CAPTURE_MODE_CAPTURE (default),
CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH
};
Clay_FloatingAttachToElement attachTo {
CLAY_ATTACH_TO_NONE (default),
CLAY_ATTACH_TO_PARENT,
CLAY_ATTACH_TO_ELEMENT_WITH_ID,
CLAY_ATTACH_TO_ROOT,
};
Clay_FloatingClipToElement clipTo {
CLAY_CLIP_TO_NONE (default),
CLAY_CLIP_TO_ATTACHED_PARENT,
};
};
Fields.offset-Clay_Vector2CLAY(CLAY_ID("Floating"), { .floating = { .offset = { -24, -24 } } }).expandUsed to apply a position offset to the floating container _after_ all other layout has been calculated.
---
-Clay_DimensionsCLAY(CLAY_ID("Floating"), { .floating = { .expand = { 16, 16 } } }).zIndexUsed to expand the width and height of the floating container _before_ laying out child elements.
---
-int16_tCLAY(CLAY_ID("Floating"), { .floating = { .zIndex = 1 } }).zIndexAll floating elements (as well as their entire child hierarchies) will be sorted by
order before being converted to render commands. If render commands are drawn in order, elements with higher.zIndexvalues will be drawn on top..parentId---
-uint32_tCLAY(CLAY_ID("Floating"), { .floating = { .parentId = Clay_GetElementId("HeaderButton").id } }).parentIdBy default, floating containers will "attach" to the parent element that they are declared inside. However, there are cases where this limitation could cause significant performance or ergonomics problems.
allows you to specify aCLAY_ID().idto attach the floating container to. The parent element with the matching id can be declared anywhere in the hierarchy, it doesn't need to be declared before or after the floating container in particular.Consider the following case:
// Load an image somewhere in your code
CLAY(CLAY_IDI("SidebarButton", 1), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 1) {
CLAY(CLAY_ID("OptionTooltip"), { / floating config... / })
}
}
CLAY(CLAY_IDI("SidebarButton", 2), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 2) {
CLAY(CLAY_ID("OptionTooltip"), { / floating config... / })
}
}
CLAY(CLAY_IDI("SidebarButton", 3), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 3) {
CLAY(CLAY_ID("OptionTooltip"), { / floating config... / })
}
}
CLAY(CLAY_IDI("SidebarButton", 4), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 4) {
CLAY(CLAY_ID("OptionTooltip"), { / floating config... / })
}
}
CLAY(CLAY_IDI("SidebarButton", 5), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 5) {
CLAY(CLAY_ID("OptionTooltip"), { / floating config... / })
}
}
The definition of the above UI is significantly polluted by the need to conditionally render floating tooltips as a child of many possible elements. The alternative, usingparentId, looks like this:
// Load an image somewhere in your code
CLAY(CLAY_IDI("SidebarButton", 1), { }) {
// .. some button contents
}
CLAY(CLAY_IDI("SidebarButton", 2), { }) {
// .. some button contents
}
CLAY(CLAY_IDI("SidebarButton", 3), { }) {
// .. some button contents
}
CLAY(CLAY_IDI("SidebarButton", 4), { }) {
// .. some button contents
}
CLAY(CLAY_IDI("SidebarButton", 5), { }) {
// .. some button contents
}
// Any other point in the hierarchy
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, .parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id }) {
// Tooltip contents...
}
---.attachPoints-Clay_FloatingAttachPointsCLAY(CLAY_ID("Floating"), { .floating = { .attachPoints = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } }) {}.attachPointsIn terms of positioning the floating container,
specifies.element- The point on the floating container (
).parent
- The point on the parent element that it "attaches" to ()CLAY(CLAY_ID("Floating"), { .floating = { .attachPoints = { .element = CLAY_ATTACH_POINT_LEFT_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_CENTER } } });!Screenshot 2024-08-23 at 11 47 21 AM
You can mentally visualise this as finding a point on the floating container, then finding a point on the parent, and lining them up over the top of one another.
For example:
"Attach the LEFT_BOTTOM of the floating container to the CENTER_CENTER of the parent"
.pointerCaptureMode<img width="1200" height="675" alt="Clay README Graphics" src="https://github.com/user-attachments/assets/95a829e3-ec17-47f2-8d16-10fda4968189" />
---
-Clay_PointerCaptureModeCLAY({ .floating = { .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_CAPTURE } })CLAY_POINTER_CAPTURE_MODE_CAPTUREControls whether pointer events like hover and click should pass through to content underneath this floating element, or whether the pointer should be "captured" by this floating element. Defaults to
..clipTo---
-Clay_FloatingClipToElementCLAY({ .floating = { .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT } }).clipBy default, floating elements will appear with a z-order above their parent, and won't be clipped by a
defined on that parent. To clip floating elements by their parents' clip rectangle, use.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT.
typedef enum {
CLAY_CLIP_TO_NONE,
CLAY_CLIP_TO_ATTACHED_PARENT,
} Clay_FloatingClipToElement;
Examples// Horizontal container with three option buttons
CLAY(CLAY_ID("OptionsList"), { .layout = { childGap = 16 } }) {
CLAY(CLAY_IDI("Option", 1), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY_TEXT(CLAY_STRING("Option 1"), {});
}
CLAY(CLAY_IDI("Option", 2), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY_TEXT(CLAY_STRING("Option 2"), {});
// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .zIndex = 1, .attachPoints = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {
CLAY_TEXT(CLAY_STRING("Most popular!"), {});
}
}
CLAY(CLAY_IDI("Option", 3), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY_TEXT(CLAY_STRING("Option 3"), {});
}
}
// Floating containers can also be declared elsewhere in a layout, to avoid branching or polluting other UI
for (int i = 0; i < 1000; i++) {
CLAY(CLAY_IDI("Option", i + 1), { }) {
// ...
}
}
// Note the use of "parentId".
// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachPoints = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER } } }) {
CLAY_TEXT(CLAY_STRING("Most popular!"), {});
}
When using.parentId, the floating container can be declared anywhere afterBeginLayoutand beforeEndLayout. The target element matching the.parentIddoesn't need to exist whenClay_FloatingElementConfigis used.Clay_FloatingElementConfigRendering
will not generate any specific render commands.CLAY(CLAY_ID("Custom"), { .custom = { .customData = &something } }) {}---
Clay_CustomElementConfig
Usage
Notes
Clay_CustomElementConfig allows the user to pass custom data to the renderer.
Struct Definition (Pseudocode)
typedef struct
{
void * customData;
} Clay_CustomElementConfig;
Fields.customData-void *CLAY({ .custom = { .customData = &myCustomData } }).customDatais a generic void pointer that can be used to pass through custom data to the renderer.Examples
#include "clay.h"
typedef enum {
CUSTOM_ELEMENT_TYPE_MODEL,
CUSTOM_ELEMENT_TYPE_VIDEO
} CustomElementType;
// A rough example of how you could handle laying out 3d models in your UI
typedef struct {
CustomElementType type;
union {
Model model;
Video video;
// ...
};
} CustomElementData;
Model myModel = Load3DModel(filePath);
CustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel }
typedef struct {
void* memory;
uintptr_t offset;
} Arena;
// During init
Arena frameArena = (Arena) { .memory = malloc(1024) };
// ...
CLAY(0) {
// Custom elements only take a single pointer, so we need to store the data somewhere
CustomElementData modelData = (CustomElementData )(frameArena.memory + frameArena.offset);
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
frameArena.offset += sizeof(CustomElementData);
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
}
// Later during your rendering
switch (renderCommand->commandType) {
// ...
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
// Your extended struct is passed through
CustomElementData *customElement = renderCommand->config.customElementConfig->customData;
if (!customElement) continue;
switch (customElement->type) {
case CUSTOM_ELEMENT_TYPE_MODEL: {
// Render your 3d model here
break;
}
case CUSTOM_ELEMENT_TYPE_VIDEO: {
// Render your video here
break;
}
// ...
}
break;
}
}
RenderingClay_RenderCommandElement is subject to culling. Otherwise, a single
withcommandType = CLAY_RENDER_COMMAND_TYPE_CUSTOMwill be created.CLAY(CLAY_ID("Transition"), { .transition = { ...transition config } }) {}Clay_TransitionElementConfig
Usage
Clay_TransitionElementConfigNotes
is used to configure element "transitions", which are animations between states..handler
Afunction and.propertiesmust be provided for transitions to occur.Struct Definition (Pseudocode)
typedef struct Clay_TransitionElementConfig
{
// Handler function pointer for computing current frame state, see below for more info
bool (*handler)(Clay_TransitionCallbackArguments arguments);
float duration;
// Note: this is a flags field. You can pass multiple properties using a bitwise OR, e.g. CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR | CLAY_TRANSITION_PROPERTY_CORNER_RADIUS
Clay_TransitionProperty properties = {
CLAY_TRANSITION_PROPERTY_NONE (default),
CLAY_TRANSITION_PROPERTY_X,
CLAY_TRANSITION_PROPERTY_Y,
CLAY_TRANSITION_PROPERTY_POSITION = CLAY_TRANSITION_PROPERTY_X | CLAY_TRANSITION_PROPERTY_Y,
CLAY_TRANSITION_PROPERTY_WIDTH,
CLAY_TRANSITION_PROPERTY_HEIGHT,
CLAY_TRANSITION_PROPERTY_DIMENSIONS = CLAY_TRANSITION_PROPERTY_WIDTH | CLAY_TRANSITION_PROPERTY_HEIGHT,
CLAY_TRANSITION_PROPERTY_BOUNDING_BOX = CLAY_TRANSITION_PROPERTY_POSITION | CLAY_TRANSITION_PROPERTY_DIMENSIONS,
CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR,
CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR,
CLAY_TRANSITION_PROPERTY_CORNER_RADIUS,
CLAY_TRANSITION_PROPERTY_BORDER_COLOR,
CLAY_TRANSITION_PROPERTY_BORDER_WIDTH,
CLAY_TRANSITION_PROPERTY_BORDER = CLAY_TRANSITION_PROPERTY_BORDER_COLOR | CLAY_TRANSITION_PROPERTY_BORDER_WIDTH
};
Clay_TransitionInteractionHandlingType interactionHandling {
CLAY_TRANSITION_DISABLE_INTERACTIONS_WHILE_TRANSITIONING_POSITION (default),
CLAY_TRANSITION_ALLOW_INTERACTIONS_WHILE_TRANSITIONING_POSITION,
};
struct {
// Function pointer, see below for details
Clay_TransitionData (*setInitialState)(Clay_TransitionData targetState, Clay_TransitionProperty properties);
Clay_TransitionEnterTriggerType trigger {
CLAY_TRANSITION_ENTER_SKIP_ON_FIRST_PARENT_FRAME (default),
CLAY_TRANSITION_ENTER_TRIGGER_ON_FIRST_PARENT_FRAME,
};
} enter;
struct {
Clay_TransitionData (*setFinalState)(Clay_TransitionData initialState, Clay_TransitionProperty properties);
Clay_TransitionExitTriggerType trigger {
CLAY_TRANSITION_EXIT_SKIP_WHEN_PARENT_EXITS (default),
CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITS,
};
Clay_ExitTransitionSiblingOrdering siblingOrdering {
CLAY_EXIT_TRANSITION_ORDERING_UNDERNEATH_SIBLINGS (default),
CLAY_EXIT_TRANSITION_ORDERING_NATURAL_ORDER,
CLAY_EXIT_TRANSITION_ORDERING_ABOVE_SIBLINGS,
};
} exit;
} Clay_TransitionElementConfig;
Fields.handler-bool (Clay_TransitionCallbackArguments arguments) {}CLAY(CLAY_ID("Transition"), { .transition = { .handler = Clay_EaseOut } })Clay_EaseOutWhen a transition has begun, this function will be called each frame to determine the current state of the element in transition. Clay provides the built-in
function which uses a standard EaseOut curve.boolIf you want to implement your own transition handler, the handler function takes Clay_TransitionCallbackArguments and returns a
to indicate whether the transition has finished or not (return truemeans the transition is complete,return falsemeans that the handler should be called again next frame)
Consider inspecting the source of the [Clay_EaseOut]() function for more information.
// Example custom handler
bool TransitionHandler(Clay_TransitionCallbackArguments arguments) {
float ratio = 1;
if (arguments.duration > 0) {
// You may want to guard against durations of zero if you use them
ratio = arguments.elapsedTime / arguments.duration;
}
float lerpAmount = (1 - powf(1 - CLAY__MIN(ratio, 1.f), 3.0f));
// Only animate properties that were specified in the original config
if (arguments.properties & CLAY_TRANSITION_PROPERTY_X) {
// Clay provides the initial state from when the transition first started, as well as the target state, to allow
// easy interpolation
arguments.current->boundingBox.x = Lerp(arguments.initial.boundingBox.x, arguments.target.boundingBox.x, lerpAmount);
}
if (arguments.properties & CLAY_TRANSITION_PROPERTY_Y) {
arguments.current->boundingBox.y = Lerp(arguments.initial.boundingBox.y, arguments.target.boundingBox.y, lerpAmount);
}
// etc...
// End (return true) once elapsedTime is greater than duration
return ratio >= 1;
}
---.duration-floatCLAY(CLAY_ID("Transition"), { .transition = { .duration = 0.2f } }).propertiesThe duration in seconds that the transition should take to arrive at its target state. Passed through to the handler function.
---
-Clay_TransitionPropertyCLAY(CLAY_ID("Transition"), { .transition = { .properties = CLAY_TRANSITION_PROPERTY_X | CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR } })|A flag field containing the properties on which to transition. Any properties that change but are not listed here will immediately snap to their target value.
This flag is a bitfield, which means that you will use bitwise operations to interact with it. For example, use bitwise OR
to combine multiple flags, or bitwise AND&to test if a flag is switched on.The full list of available transition properties is as follows:
typedef enum {
CLAY_TRANSITION_PROPERTY_NONE (default),
CLAY_TRANSITION_PROPERTY_X,
CLAY_TRANSITION_PROPERTY_Y,
CLAY_TRANSITION_PROPERTY_POSITION = CLAY_TRANSITION_PROPERTY_X | CLAY_TRANSITION_PROPERTY_Y,
CLAY_TRANSITION_PROPERTY_WIDTH,
CLAY_TRANSITION_PROPERTY_HEIGHT,
CLAY_TRANSITION_PROPERTY_DIMENSIONS = CLAY_TRANSITION_PROPERTY_WIDTH | CLAY_TRANSITION_PROPERTY_HEIGHT,
CLAY_TRANSITION_PROPERTY_BOUNDING_BOX = CLAY_TRANSITION_PROPERTY_POSITION | CLAY_TRANSITION_PROPERTY_DIMENSIONS,
CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR,
CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR,
CLAY_TRANSITION_PROPERTY_CORNER_RADIUS,
CLAY_TRANSITION_PROPERTY_BORDER_COLOR,
CLAY_TRANSITION_PROPERTY_BORDER_WIDTH,
CLAY_TRANSITION_PROPERTY_BORDER = CLAY_TRANSITION_PROPERTY_BORDER_COLOR | CLAY_TRANSITION_PROPERTY_BORDER_WIDTH
} Clay_TransitionProperty;
---.interactionHandling-Clay_TransitionInteractionHandlingTypeCLAY(CLAY_ID("Transition"), { .transition = { .interactionHandling = CLAY_TRANSITION_ALLOW_INTERACTIONS_WHILE_TRANSITIONING_POSITION } })falseThis flag controls how interactions are handled when elements are transitioning their positions. By default, Clay will ignore interactions i.e. returning
for functions likeClay_Hovered()when.interactionHandlingis in the default mode ofCLAY_TRANSITION_DISABLE_INTERACTIONS_WHILE_TRANSITIONING.interactionHandling = CLAY_TRANSITION_ALLOW_INTERACTIONS_WHILE_TRANSITIONING_POSITIONYou can set
if you want to interact with transitioning elements.The full list of values is as follows:
typedef enum {
CLAY_TRANSITION_DISABLE_INTERACTIONS_WHILE_TRANSITIONING_POSITION (default),
CLAY_TRANSITION_ALLOW_INTERACTIONS_WHILE_TRANSITIONING_POSITION,
} Clay_TransitionInteractionHandlingType;
---.enter.setInitialState-Clay_TransitionData (Clay_TransitionData targetState, Clay_TransitionProperty properties) {}CLAY(CLAY_ID("Transition"), { .transition = { .enter = { .setInitialState = { EnterSlideUp } } }).overlayColorThis function pointer is called the first frame an element appears, and allows you to modify the "initial state" of the element to create an entry transition. Common techniques include offsetting the y position to "slide up / down",
or using amatched to the background color to fade in.Clay_TransitionDataThe function will be called with
that provides the first-frame state of the element, which you can modify and then return.Note: "Enter" transitions will only trigger if this function pointer is set.
Here is an example "fade in & slide up" function:
Clay_TransitionData EnterSlideUp(Clay_TransitionData initialState, Clay_TransitionProperty properties) {
Clay_TransitionData targetState = initialState;
if (properties & CLAY_TRANSITION_PROPERTY_Y) {
targetState.boundingBox.y += 20;
}
if (properties & CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR) {
// Assuming the background color is white, this will produce an alpha-like "fade-in" effect
targetState.overlayColor = (Clay_Color) { 255, 255, 255, 255 };
}
return targetState;
}
---.enter.trigger-Clay_TransitionEnterTriggerTypeCLAY(CLAY_ID("Transition"), { .transition = { .enter = { .trigger = CLAY_TRANSITION_ENTER_TRIGGER_ON_FIRST_PARENT_FRAME } } }).trigger = CLAY_TRANSITION_ENTER_TRIGGER_ON_FIRST_PARENT_FRAMEThis flag controls whether or not the "enter" transition of this element will trigger on the same frame that the parent element first appeared.
A common use case for enter transitions is adding items to animated lists. Without this flag, the first frame the list is displayed, all the item enter animations will simultaneously trigger, which is usually undesirable.
For cases where you _do_ want enter animations to trigger when the parent first appears, you can set
.The full list of values is as follows:
typedef enum trigger {
CLAY_TRANSITION_ENTER_SKIP_ON_FIRST_PARENT_FRAME (default),
CLAY_TRANSITION_ENTER_TRIGGER_ON_FIRST_PARENT_FRAME,
} Clay_TransitionEnterTriggerType;
---.exit.setFinalState-Clay_TransitionData (Clay_TransitionData currentState, Clay_TransitionProperty properties) {}CLAY(CLAY_ID("Transition"), { .transition = { .exit = { .setFinalState = { ExitSlideDown } } }).overlayColorThis function pointer is called the frame the element "exits" (i.e. it was in the layout tree last frame, and this frame it isn't).
It allows you to modify the "final state" of the element to create an exit transition. Common techniques include offsetting the y position to "slide up / down",
or using amatched to the background color to fade out.Clay_TransitionDataThe function will be called with
that provides the final-frame state of the element, which you can modify and then return.Note: "Exit" transitions will only trigger if this function pointer is set.
Here is an example "fade out & slide down" function:
Clay_TransitionData ExitSlideDown(Clay_TransitionData currentState, Clay_TransitionProperty properties) {
Clay_TransitionData finalState = currentState;
if (properties & CLAY_TRANSITION_PROPERTY_Y) {
finalState.boundingBox.y += 20;
}
if (properties & CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR) {
// Assuming the background color is white, this will produce an alpha-like "fade-out" effect
finalState.overlayColor = (Clay_Color) { 255, 255, 255, 255 };
}
return finalState;
}
---.exit.trigger-Clay_TransitionExitTriggerTypeCLAY(CLAY_ID("Transition"), { .transition = { .exit = { .trigger = CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITS } } }).trigger = CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITSThis flag controls whether or not the "exit" transition of this element will trigger on the same frame that the parent element disappears.
A common use case for exit transitions is removing items from animated lists. Without this flag, the frame after the list disappears (due to say, menu navigation), all the item exit animations will simultaneously trigger, which is usually undesirable.
For cases where you _do_ want exit animations to trigger when the parent exits, you can set
.The full list of values is as follows:
typedef enum {
CLAY_TRANSITION_EXIT_SKIP_WHEN_PARENT_EXITS (default),
CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITS,
} Clay_TransitionExitTriggerType;
---.exit.siblingOrdering-Clay_ExitTransitionSiblingOrderingCLAY(CLAY_ID("Transition"), { .transition = { .exit = { .siblingOrdering = CLAY_EXIT_TRANSITION_ORDERING_NATURAL_ORDER } } })This flag controls the relative z-ordering of exiting elements.
It's often useful to be able to control whether you want an exiting element to appear "underneath siblings" in its "natural order" (i.e. above the previous sibling, below the subsequent sibling) or "above all siblings."
By default, exiting elements will be underneath siblings as this appears to be the most common case.
The full list of values is as follows:
typedef enum {
CLAY_EXIT_TRANSITION_ORDERING_UNDERNEATH_SIBLINGS (default),
CLAY_EXIT_TRANSITION_ORDERING_NATURAL_ORDER,
CLAY_EXIT_TRANSITION_ORDERING_ABOVE_SIBLINGS,
} Clay_ExitTransitionSiblingOrdering;
---Examples
See video below for how the following would look.
// Note: for transitions to work, elements need a stable ID from one frame to the next - using loop indexes or CLAY_AUTO_ID will not work.
CLAY(CLAY_IDI("box", colors[index].id), {
.layout.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() },
.layout.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
.backgroundColor = boxColor,
.overlayColor = Clay_Hovered() ? (Clay_Color) { 140, 140, 140, 80 } : (Clay_Color) { 255, 255, 255, 0 },
// Transitions will activate once a handler function is defined.
.transition = {
.handler = Clay_EaseOut,
.duration = 0.5f,
// A "flag" enum is used to specify which properties to transition, use a bitwise OR (|) to construct the flags.
.properties = CLAY_TRANSITION_PROPERTY_WIDTH
| CLAY_TRANSITION_PROPERTY_POSITION
| CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR
| CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR,
.enter = { .setInitialState = EnterExitSlideUp },
.exit = { .setFinalState = EnterExitSlideUp },
}
}) {
CLAY_TEXT(CLAY_STRING("Animated Box"), {
.fontSize = 32,
.textColor = colors[index].id > 29 ? (Clay_Color) { 255, 255, 255, 255 } : (Clay_Color) { 154, 123, 184, 255 }
});
}
<video src="https://github.com/user-attachments/assets/a8e5cd88-f0da-4fad-acd0-81f253436bc7" controls></video>_An example of the transition API action can be found at examples/raylib-transitions_
Clay_Color
typedef struct {
float r, g, b, a;
} Clay_Color;
Clay_Coloris an RGBA color struct used in Clay's declarations and rendering. By convention the channels are represented as 0-255, but this is left up to the renderer.
Note: when using the debug tools, their internal colors are represented as 0-255.Clay_String
typedef struct {
bool isStaticallyAllocated;
int32_t length;
const char *chars;
} Clay_String;
Clay_Stringis a string container that clay uses internally to represent all strings..isStaticallyAllocatedFields
-bool.lengthWhether or not the string is statically allocated, or in other words, whether
or not it lives for the entire lifetime of the program.---
-int32_t.charsThe number of characters in the string, _not including an optional null terminator._
---
-const char *A pointer to the contents of the string. This data is not guaranteed to be null terminated, so if you are passing it to code that expects standard null terminated C strings, you will need to copy the data and append a null terminator.
---
Clay_ElementId
typedef struct {
uint32_t id;
uint32_t offset;
uint32_t baseId;
Clay_String stringId;
} Clay_ElementId;
Returned by CLAY_ID and CLAY_IDI, this struct contains a hash id, as well as the source string that was used to generate it..idFields
-uint32_t.offsetA unique ID derived from the string passed to CLAY_ID or CLAY_IDI.
---
-uint32_t.offsetIf this id was generated using CLAY_IDI,
is the value passed as the second argument. For CLAY_ID, this will always be0..baseId---
-uint32_t.baseIdIf this id was generated using CLAY_IDI,
is the hash of the base string passed, before it is additionally hashed with.offset. For CLAY_ID, this will always be the same as.id..stringId---
-Clay_StringStores the original string that was passed in when CLAY_ID or CLAY_IDI were called.
---
Clay_RenderCommandArray
typedef struct
{
int32_t capacity;
int32_t length;
Clay_RenderCommand *internalArray;
} Clay_RenderCommandArray;
Returned by Clay_EndLayout, this array contains the Clay_RenderCommands representing the calculated layout..capacityFields
-uint32_t.internalArrayRepresents the total capacity of the allocated memory in
..length---
-uint32_tClay_RenderCommandRepresents the total number of
elements stored consecutively at the address.internalArray..internalArray---
-Clay_RenderCommand.internalArray[0]An array of Clay_RenderCommands representing the calculated layout. If there was at least one render command, this array will contain elements from
to.internalArray[.length - 1].---
Clay_RenderCommand
typedef struct {
Clay_BoundingBox boundingBox;
Clay_RenderData renderData;
void* userData;
uint32_t id;
int16_t zIndex;
Clay_RenderCommandType commandType;
} Clay_RenderCommand;
Fields.commandType-Clay_RenderCommandTypeCLAY_RENDER_COMMAND_TYPE_NONEAn enum indicating how this render command should be handled. Possible values include:
-
- Should be ignored by the renderer, and never emitted by clay under normal conditions.CLAY_RENDER_COMMAND_TYPE_RECTANGLE
-- A rectangle should be drawn, configured with.renderData.rectangleCLAY_RENDER_COMMAND_TYPE_BORDER
-- A border should be drawn, configured with.renderData.borderCLAY_RENDER_COMMAND_TYPE_TEXT
-- Text should be drawn, configured with.renderData.textCLAY_RENDER_COMMAND_TYPE_IMAGE
-- An image should be drawn, configured with.renderData.imageCLAY_RENDER_COMMAND_TYPE_SCISSOR_START
-- Named after glScissor, this indicates that the renderer should begin culling any subsequent pixels that are drawn outside the.boundingBoxof this render command.CLAY_RENDER_COMMAND_TYPE_SCISSOR_END
-- Only ever appears after a matchingCLAY_RENDER_COMMAND_TYPE_SCISSOR_STARTcommand, and indicates that the scissor has ended.CLAY_RENDER_COMMAND_TYPE_OVERLAY_COLOR_START
-- The renderer should begin applying an overlay color to all subsequent render commands, similar to glsl'smix(source, target, alpha).CLAY_RENDER_COMMAND_TYPE_OVERLAY_COLOR_END
-- The previousOVERLAY_COLORshould be removed. Note: nested color overlays may require a stack data structure on the renderer side.CLAY_RENDER_COMMAND_TYPE_CUSTOM
-- A custom render command controlled by the user, configured with.renderData.custom.boundingBox---
-Clay_BoundingBox
typedef struct {
float x, y, width, height;
} Clay_BoundingBox;
A rectangle representing the bounding box of this render command, with.xand.yrepresenting the top left corner of the element..id---
-uint32_t.zIndexThe id that was originally used with the element macro that created this render command. See CLAY_ID for details.
---
-int16_t.renderDataThe z index of the element, based on what was passed to the root floating configuration that this element is a child of.
Higher z indexes should be rendered _on top_ of lower z indexes.---
-Clay_RenderData
typedef union {
Clay_RectangleRenderData rectangle;
Clay_TextRenderData text;
Clay_ImageRenderData image;
Clay_CustomRenderData custom;
Clay_BorderRenderData border;
} Clay_RenderData;
A C union containing various structs, with the type dependent on.commandType. Possible values include:renderData.rectangle-
- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE.renderData.text
-- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_TEXT. See Clay_Text for details.renderData.image
-- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE. See Clay_ImageElementConfig for details.renderData.custom
-- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM. See Clay_CustomElementConfig for details.renderData.border
-- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_BORDER. See Clay_BorderElementConfig for details.renderData.clip
-- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START. See Clay_ClipElementConfig for details.renderData.overlayColor
-- Used when.commandType == CLAY_RENDER_COMMAND_TYPE_OVERLAY_COLOR_START. See Clay_ElementDeclaration.overlayColor for details.Union Structs
typedef struct {
Clay_StringSlice stringContents;
Clay_Color textColor;
uint16_t fontId;
uint16_t fontSize;
uint16_t letterSpacing;
uint16_t lineHeight;
} Clay_TextRenderData;
typedef struct {
Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius;
} Clay_RectangleRenderData;
typedef struct {
Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius;
void* imageData;
} Clay_ImageRenderData;
typedef struct {
Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius;
void* customData;
} Clay_CustomRenderData;
typedef struct {
Clay_Color color;
Clay_CornerRadius cornerRadius;
Clay_BorderWidth width;
} Clay_BorderRenderData;
typedef union {
Clay_RectangleRenderData rectangle;
Clay_TextRenderData text;
Clay_ImageRenderData image;
Clay_CustomRenderData custom;
Clay_BorderRenderData border;
} Clay_RenderData;
Clay_ScrollContainerData
// Data representing the current internal state of a scrolling element.
typedef struct {
// Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.
// Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.
Clay_Vector2 *scrollPosition;
// The bounding box of the scroll element.
Clay_Dimensions scrollContainerDimensions;
// The outer dimensions of the inner scroll container content, including the padding of the parent scroll container.
Clay_Dimensions contentDimensions;
// The config that was originally passed to the scroll element.
Clay_ClipElementConfig config;
// Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.
bool found;
} Clay_ScrollContainerData;
Fields.scrollPosition-Clay_Vector2 *.yA pointer to the internal scroll position of this scroll container. Mutating it will result in elements inside the scroll container shifting up / down (
) or left / right (.x)..scrollContainerDimensions---
-Clay_Dimensions
typedef struct {
float width, height;
} Clay_Dimensions;
Dimensions representing the outer width and height of the scroll container itself..contentDimensions---
-Clay_Dimensions
typedef struct {
float width, height;
} Clay_Dimensions;
Dimensions representing the inner width and height of the content _inside_ the scroll container. Scrolling is only possible when thecontentDimensionsare larger in at least one dimension than thescrollContainerDimensions..config---
-Clay_ClipElementConfigThe Clay_ClipElementConfig for the matching scroll container element.
---
Clay_ElementData
// Bounding box and other data for a specific UI element.
typedef struct {
// The rectangle that encloses this UI element, with the position relative to the root of the layout.
Clay_BoundingBox boundingBox;
// Indicates whether an actual Element matched the provided ID or if the default struct was returned.
bool found;
} Clay_ElementData;
Fields.boundingBox-Clay_BoundingBox
typedef struct {
float x, y, width, height;
} Clay_BoundingBox;
A rectangle representing the bounding box of this render command, with.xand.yrepresenting the top left corner of the element..found---
-bool.foundA boolean representing whether or not the ID passed to Clay_GetElementData matched a valid element or not. In the case that
isfalse,.boundingBoxwill be the default value (zeroed).---
Clay_PointerData
typedef struct
{
Clay_Vector2 position;
Clay_PointerDataInteractionState state;
} Clay_PointerData;
Fields.position-Clay_Vector2.stateA Vector2 containing the current x,y coordinates of the mouse pointer, which were originally passed into Clay_SetPointerState().
---
-Clay_PointerDataInteractionState
typedef enum
{
CLAY_POINTER_DATA_PRESSED_THIS_FRAME,
CLAY_POINTER_DATA_PRESSED,
CLAY_POINTER_DATA_RELEASED_THIS_FRAME,
CLAY_POINTER_DATA_RELEASED,
} Clay_PointerDataInteractionState;
An enum value representing the current "state" of the pointer interaction. As an example, consider the case where a user is on a desktop computer, moves the mouse pointer over a button, clicks and holds the left mouse button for a short time, then releases it:CLAY_POINTER_DATA_RELEASED- While the mouse pointer is over ("hovering") the button, but no mouse button has been pressed:
CLAY_POINTER_DATA_PRESSED_THIS_FRAME
- First frame that the user presses the left mouse button:CLAY_POINTER_DATA_PRESSED
- All subsequent frames where the user is still holding the left mouse button:CLAY_POINTER_DATA_RELEASED_THIS_FRAME
- The single frame where the left mouse button goes from pressed -> released:CLAY_POINTER_DATA_RELEASED
- All subsequent frames while the mouse pointer is still over the button:---
Clay_ErrorHandler
typedef struct
{
void (*errorHandlerFunction)(Clay_ErrorData errorText);
uintptr_t userData;
} Clay_ErrorHandler;
Fields.errorHandlerFunction-void (Clay_ErrorData errorText) {}Clay_ErrorDataA function pointer to an error handler function, which takes
as an argument. This function will be called whenever Clay encounters an internal error..userData---
-uintptr_tClay_InitializeA generic pointer to extra userdata that is transparently passed through from
to Clay's error handler callback. Defaults to NULL.---
Clay_ErrorData
typedef struct
{
Clay_ErrorType errorType;
Clay_String errorText;
uintptr_t userData;
} Clay_ErrorData;
Fields.errorType-Clay_ErrorType
typedef enum {
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,
CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
CLAY_ERROR_TYPE_DUPLICATE_ID,
CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,
CLAY_ERROR_TYPE_INTERNAL_ERROR,
} Clay_ErrorType;
`
An enum representing the type of error Clay encountered. It's up to the user to handle on a case by case basis, but as some general guidance:
-
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED - The user is attempting to use CLAY_TEXT and either forgot to call Clay_SetMeasureTextFunction or accidentally passed a null function pointer.
- CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED - Clay was initialized with an Arena that was too small for the configured Clay_SetMaxElementCount. Try using Clay_MinMemorySize() to get the exact number of bytes required by the current configuration.
- CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED - The declared UI hierarchy has too many elements for the configured max element count. Use Clay_SetMaxElementCount to increase the max, then call Clay_MinMemorySize() again and reinitialize clay's memory with the required size.
- CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED - The declared UI hierarchy has too much text for the configured text measure cache size. Use Clay_SetMaxMeasureTextCacheWordCount to increase the max, then call Clay_MinMemorySize() again and reinitialize clay's memory with the required size.
- CLAY_ERROR_TYPE_DUPLICATE_ID - Two elements in Clays UI Hierarchy have been declared with exactly the same ID. Set a breakpoint in your error handler function for a stack trace back to exactly where this occured.
- CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND - A CLAY_FLOATING element was declared with the .parentId property, but no element with that ID was found. Set a breakpoint in your error handler function for a stack trace back to exactly where this occured.
- CLAY_ERROR_TYPE_INTERNAL_ERROR - Clay has encountered an internal logic or memory error. Please report this as a bug with a stack trace to help us fix these!---
.errorText - Clay_StringA Clay_String that provides a human readable description of the error. May change in future and should not be relied on to detect error types.
---
.userData - uintptr_tA generic pointer to extra userdata that is transparently passed through from
Clay_Initialize` to Clay's error handler callback. Defaults to NULL.---