### Build and Run on macOS Source: https://github.com/nicbarker/clay/blob/main/examples/GLES3-SDL2-sidebar-scrolling-container/README.md Build and run the example on macOS. Requires SDL2 (install via Homebrew) and STB. Uses CMake for configuration and a Makefile for building. ```bash brew install sdl2 cmake -B build make -f Makefile.macos ./macos-sidebar-scrolling-container-sdl2 ``` -------------------------------- ### Initialize CMake for Playdate Examples Source: https://github.com/nicbarker/clay/blob/main/examples/playdate-project-example/README.md Use this command to initialize your CMake build directory and include Playdate examples. Ensure the Playdate SDK is installed. ```bash cmake -DCLAY_INCLUDE_PLAYDATE_EXAMPLES=ON cmake-build-debug ``` -------------------------------- ### Build Playdate Example with CMake Source: https://github.com/nicbarker/clay/blob/main/examples/playdate-project-example/README.md After initializing CMake, use this command to build the Playdate example project. The output pdx file will be in the specified directory. ```bash cmake --build cmake-build-debug ``` -------------------------------- ### Project and Raylib Setup Source: https://github.com/nicbarker/clay/blob/main/examples/raylib-transitions/CMakeLists.txt Initializes the CMake project and configures FetchContent to download and build the Raylib library. It sets specific build options for Raylib to exclude its examples and games. ```cmake cmake_minimum_required(VERSION 3.27) project(clay_examples_raylib_transitions C) set(CMAKE_C_STANDARD 99) # Adding Raylib include(FetchContent) set(FETCHCONTENT_QUIET FALSE) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples set(BUILD_GAMES OFF CACHE BOOL "" FORCE) # don't build the supplied example games FetchContent_Declare( raylib GIT_REPOSITORY "https://github.com/raysan5/raylib.git" GIT_TAG "5.5" GIT_PROGRESS TRUE GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(raylib) ``` -------------------------------- ### Build Playdate Example for Device Source: https://github.com/nicbarker/clay/blob/main/examples/playdate-project-example/README.md Build the Playdate example for the hardware using the release build configuration. This command generates a pdx file suitable for the Playdate device. ```bash cmake --build cmake-build-release ``` -------------------------------- ### Project and Dependency Setup Source: https://github.com/nicbarker/clay/blob/main/examples/raylib-sidebar-scrolling-container/CMakeLists.txt Configures the minimum CMake version, project name, C standard, and fetches the Raylib library. It also sets options to disable building Raylib's examples and games. ```cmake cmake_minimum_required(VERSION 3.27) project(clay_examples_raylib_sidebar_scrolling_container C) set(CMAKE_C_STANDARD 99) # Adding Raylib include(FetchContent) set(FETCHCONTENT_QUIET FALSE) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples set(BUILD_GAMES OFF CACHE BOOL "" FORCE) # don't build the supplied example games FetchContent_Declare( raylib GIT_REPOSITORY "https://github.com/raysan5/raylib.git" GIT_TAG "5.5" GIT_PROGRESS TRUE GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(raylib) ``` -------------------------------- ### Animated Box Transition Example Source: https://github.com/nicbarker/clay/blob/main/README.md This example demonstrates setting up an animated box with various transition properties including width, position, overlay color, and background color. Ensure elements have stable IDs for transitions to function correctly. ```C // 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 } }); } ``` -------------------------------- ### Example: Rendering a 3D Model with Custom Element Data Source: https://github.com/nicbarker/clay/blob/main/README.md This example demonstrates how to initialize and render a custom element, specifically a 3D model, using Clay. It involves setting up custom data structures, allocating memory, and handling custom render commands. ```c #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; } } ``` -------------------------------- ### Custom Transition Handler Example Source: https://github.com/nicbarker/clay/blob/main/README.md An example of a custom handler function for `Clay_TransitionElementConfig`. This function demonstrates how to calculate interpolation ratios based on elapsed time and duration, and how to apply animated changes to element properties. ```APIDOC ## Custom Transition Handler ### Description This example shows how to implement a custom transition handler function. It calculates the animation progress and updates element properties based on specified transition properties. ### Code Example ```c // 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; } ``` ``` -------------------------------- ### Example Usage: Button Layout Source: https://github.com/nicbarker/clay/blob/main/README.md Demonstrates a comprehensive layout configuration for a button, including direction, sizing, and padding. ```c CLAY(CLAY_ID("Button"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) { ``` -------------------------------- ### Clay UI Library Setup and Initialization Source: https://github.com/nicbarker/clay/blob/main/README.md Define CLAY_IMPLEMENTATION before including clay.h in one file for implementation. Initialize the Clay arena with minimum memory size and create it with a custom allocator. Set up the error handler and initialize Clay with the arena, screen dimensions, and error handler. ```C // 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++) { ``` -------------------------------- ### CMake Project Configuration Source: https://github.com/nicbarker/clay/blob/main/examples/sokol-corner-radius/CMakeLists.txt Sets up the minimum CMake version and project name. This is the standard starting point for any CMake project. ```cmake cmake_minimum_required(VERSION 3.27) project(sokol_corner_radius C) ``` -------------------------------- ### Configure Sizing Source: https://github.com/nicbarker/clay/blob/main/README.md Defines how an element's width and height are calculated. This example sets a fixed width and a height as 50% of the parent. ```c CLAY(CLAY_ID("Element"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } }) ``` -------------------------------- ### Conditional Tooltip Rendering Example Source: https://github.com/nicbarker/clay/blob/main/README.md This example demonstrates a common pattern where tooltips are conditionally rendered as children of multiple elements. It highlights the potential for UI definition pollution. ```C 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... */ }) } } ``` -------------------------------- ### Define Layouts and Components Source: https://github.com/nicbarker/clay/blob/main/bindings/odin/README.md Define colors, layout configurations, and reusable UI components. This example shows how to create a sidebar item component and a basic layout structure. ```odin // Define some colors. COLOR_LIGHT :: clay.Color{224, 215, 210, 255} COLOR_RED :: clay.Color{168, 66, 28, 255} COLOR_ORANGE :: clay.Color{225, 138, 50, 255} COLOR_BLACK :: clay.Color{0, 0, 0, 255} // Layout config is just a struct that can be declared statically, or inline sidebar_item_layout := clay.LayoutConfig { sizing = { width = clay.SizingGrow({}), height = clay.SizingFixed(50) }, } // Re-useable components are just normal procs. sidebar_item_component :: proc(index: u32) { if clay.UI()({ id = clay.ID("SidebarBlob", index), layout = sidebar_item_layout, backgroundColor = COLOR_ORANGE, }) {} } // An example function to create your layout tree create_layout :: proc() -> clay.ClayArray(clay.RenderCommand) { // Begin constructing the layout. clay.BeginLayout() // An example of laying out a UI with a fixed-width sidebar and flexible-width main content // NOTE: To create a scope for child components, the Odin API uses `if` with components that have children if clay.UI()({ id = clay.ID("OuterContainer"), layout = { sizing = { width = clay.SizingGrow({}), height = clay.SizingGrow({}) }, padding = { 16, 16, 16, 16 }, childGap = 16, }, backgroundColor = { 250, 250, 255, 255 }, }) { if clay.UI()({ id = clay.ID("SideBar"), layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingFixed(300), height = clay.SizingGrow({}) }, padding = { 16, 16, 16, 16 }, childGap = 16, }, backgroundColor = COLOR_LIGHT, }) { if clay.UI()({ id = clay.ID("ProfilePictureOuter"), layout = { sizing = { width = clay.SizingGrow({}) }, padding = { 16, 16, 16, 16 }, childGap = 16, childAlignment = { y = .Center }, }, backgroundColor = COLOR_RED, cornerRadius = { 6, 6, 6, 6 }, }) { if clay.UI()({ id = clay.ID("ProfilePicture"), layout = { sizing = { width = clay.SizingFixed(60), height = clay.SizingFixed(60) }, }, image = { // How you define `profile_picture` depends on your renderer. imageData = &profile_picture, sourceDimensions = { width = 60, ``` -------------------------------- ### Initialize Clay Project Source: https://github.com/nicbarker/clay/blob/main/examples/clay-official-website/index.html Initializes the Clay project. This function should be called once at the start of your application. ```javascript dThisFrame = false; window.dKeyPressedThisFrame = false; } init(); ``` -------------------------------- ### Configure CMake for Playdate Device Build Source: https://github.com/nicbarker/clay/blob/main/examples/playdate-project-example/README.md To build for the Playdate hardware, specify the toolchain, toolchain file, and disable unnecessary examples. This ensures compatibility with the device. ```bash cmake -DTOOLCHAIN=armgcc -DCMAKE_TOOLCHAIN_FILE=/Users/mattahj/Developer/PlaydateSDK/C_API/buildsupport/arm.cmake -DCLAY_INCLUDE_ALL_EXAMPLES=OFF -DCLAY_INCLUDE_PLAYDATE_EXAMPLES=ON -B cmake-build-release ``` -------------------------------- ### Create Odin UI Layout with Clay Source: https://github.com/nicbarker/clay/blob/main/bindings/odin/README.md Define a UI layout using Odin procedures and Clay's layout system. This example demonstrates creating a main layout with a sidebar and main content area. ```odin package main import "core:fmt" import "core:mem" import clay "vendor/clay" // Define a simple sidebar item component sidebar_item_component :: proc(id: u32) { clay.Button(clay.ID(fmt.tprintf("SidebarItem_%d", id)), { text = clay.TextConfig({ text = fmt.tprintf("Item %d", id), textColor = COLOR_WHITE, fontSize = 14, }), layout = { sizing = { width = clay.SizingPixels(150), height = clay.SizingPixels(40), }, padding = clay.Padding(10), }, }) } // Main layout procedure create_layout :: proc() -> clay.ClayArray(clay.RenderCommand) { // Use clay.BeginLayout to start defining the UI structure. clay.BeginLayout({ id = clay.ID("MainLayout"), layout = { sizing = { width = clay.SizingGrow({}), height = clay.SizingGrow({}), }, direction = .Horizontal, }, backgroundColor = COLOR_DARK, }) // Sidebar clay.Container({ id = clay.ID("Sidebar"), layout = { sizing = { width = clay.SizingPixels(200), height = clay.SizingGrow({}) }, padding = clay.Padding(10), backgroundColor = COLOR_GRAY, }, }) { clay.Text("Clay - UI Library", clay.TextConfig({textColor = COLOR_BLACK, fontSize = 16})) } // Standard Odin code like loops, etc. work inside components. // Here we render 5 sidebar items. for i in u32(0)..<5 { sidebar_item_component(i) } // Main Content Area if clay.UI()({ id = clay.ID("MainContent"), layout = { sizing = { width = clay.SizingGrow({}), height = clay.SizingGrow({}) }, }, backgroundColor = COLOR_LIGHT, }) {} // Returns a list of render commands return clay.EndLayout() } ``` -------------------------------- ### Configure Raylib Fetching in CMake Source: https://github.com/nicbarker/clay/blob/main/examples/raylib-multi-context/CMakeLists.txt This snippet sets up FetchContent to download and build the Raylib library from GitHub. It specifies the repository, tag, and disables building of examples and games to avoid conflicts. ```cmake cmake_minimum_required(VERSION 3.27) project(clay_examples_raylib_multi_context C) set(CMAKE_C_STANDARD 99) # Adding Raylib include(FetchContent) set(FETCHCONTENT_QUIET FALSE) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples set(BUILD_GAMES OFF CACHE BOOL "" FORCE) # don't build the supplied example games FetchContent_Declare( raylib GIT_REPOSITORY "https://github.com/raysan5/raylib.git" GIT_TAG "5.5" GIT_PROGRESS TRUE GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(raylib) ``` -------------------------------- ### Configure Padding Source: https://github.com/nicbarker/clay/blob/main/README.md Applies padding around the outside of child elements. This example sets uniform padding on all sides. ```c CLAY(CLAY_ID("Element"), { .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } }) ``` -------------------------------- ### Configure CMake Project Source: https://github.com/nicbarker/clay/blob/main/examples/clay-official-website/CMakeLists.txt Sets the minimum CMake version and project name. This is a standard starting point for any CMake project. ```cmake cmake_minimum_required(VERSION 3.27) project(clay_official_website C) ``` -------------------------------- ### Example: Horizontal Container with Floating Tooltip Source: https://github.com/nicbarker/clay/blob/main/README.md Demonstrates a horizontal layout with option buttons, where a floating tooltip is attached to one of the buttons. The tooltip is configured to appear above the button and does not affect the layout of other elements. ```C // 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"), {}); } } ``` -------------------------------- ### Process Clay Render Commands in Odin Source: https://github.com/nicbarker/clay/blob/main/bindings/odin/README.md Iterate through the render commands generated by Clay and execute drawing operations. This example shows basic handling for rectangle commands. ```odin render_commands := create_layout() for i in 0..commandType) { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { DrawRectangle(renderCommand->boundingBox, renderCommand->renderData.rectangle.backgroundColor); } // ... Implement handling of other command types } ``` -------------------------------- ### Example: Scrollable Container with Fixed Height Child Source: https://github.com/nicbarker/clay/blob/main/README.md Demonstrates creating a scrollable outer container with vertical clipping enabled, containing an inner element with a fixed height. This setup is useful for content that exceeds the viewport. ```C 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) } } }) {} } ``` -------------------------------- ### Build with CMake Source: https://github.com/nicbarker/clay/blob/main/examples/GLES3-GLFW-video-demo/README.md Use CMake to configure and build the project. This is the recommended method for building the demo. ```bash mkdir build cmake -S . -B ./build ``` -------------------------------- ### Initialize Application Source: https://github.com/nicbarker/clay/blob/main/examples/clay-official-website/index.html Sets up the application by loading fonts, creating DOM elements for HTML and canvas rendering, and initializing global event listeners for mouse and touch input. ```javascript async function init() { await Promise.all(fontsById.map(f => document.fonts.load(`12px "${f}"`))); window.htmlRoot = document.body.appendChild(document.createElement('div')); window.canvasRoot = document.body.appendChild(document.createElement('canvas')); window.canvasContext = window.canvasRoot.getContext("2d"); window.mousePositionXThisFrame = 0; window.mousePositionYThisFrame = 0; window.mouseWheelXThisFrame = 0; window.mouseWheelYThisFrame = 0; window.touchDown = false; window.arrowKeyDownPressedThisFrame = false; window.arrowKeyUpPressedThisFrame = false; let zeroTimeout = null; document.addEventListener("wheel", (event) => { window.mouseWheelXThisFrame = event.deltaX * -0.1; window.mouseWheelYThisFrame = event.deltaY * -0.1; clearTimeout(zeroTimeout); zeroTimeout = setTimeout(() => { window.mouseWheelXThisFrame = 0; window.mouseWheelYThisFrame = 0; }, 10); }); function handleTouch (event) { if (event.touches.length === 1) { window.touchDown = true; let target = event.target; let scrollTop = 0; let scrollLeft = 0; let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true); while (activeRendererIndex !== 1 && target) { scrollLeft += target.scrollLeft; scrollTop += target.scrollTop; target = target.parentElement; } window.mousePositionXThisFrame = event.changedTouches[0].pageX + scrollLeft; window.mousePositionYThisFrame = event.changedTouches[0].pageY + scrollTop; } } document.addEventListener("touchstart", handleTouch); document.addEventListener("touchmove", handleTouch); document.addEventListener("touchend", () => { window.touchDown = false; window.mousePositionXThisFrame = 0; window.mousePositionYThisFrame = 0; }) document.addEventListener("mousemove", (event) => { let target = event.target; let scrollTop = 0; let scrollLeft = 0; let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true); while (activeRendererIndex !== 1 && target) { scrollLeft += target.scrollLeft; scrollTop += target.scrollTop; target = target.parentElement; } window.mousePositionXThisFrame ``` -------------------------------- ### Initialize and Render Multiple Clay Instances Source: https://github.com/nicbarker/clay/blob/main/README.md Demonstrates how to initialize and manage multiple Clay instances within a single program. Each instance requires its own arena, and the current context must be explicitly set before rendering commands for that instance. ```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); ``` -------------------------------- ### Build and Run Emscripten Demo Source: https://github.com/nicbarker/clay/blob/main/examples/GLES3-SDL2-video-demo/README.md Use this command to build and run the Emscripten version of the demo. Navigate to the specified URL in your browser to view the demo. ```bash make -f Makefile.emscripten test ``` -------------------------------- ### Clay_OnHover Source: https://github.com/nicbarker/clay/blob/main/README.md Registers a callback function to be executed when the pointer starts or stops hovering over an element. ```APIDOC ## Clay_OnHover ### Description Registers a callback function to be executed when the pointer starts or stops hovering over an element. The callback receives the element ID and a boolean indicating if hovering started. ### Function Signature `void Clay_OnHover(Clay_ElementId element_id, void (*callback)(Clay_ElementId, bool));` ``` -------------------------------- ### Container with 3px Yellow Bottom Border Source: https://github.com/nicbarker/clay/blob/main/README.md Example of a container with only a 3px yellow border on its bottom edge. ```C // Container with a 3px yellow bottom border CLAY(CLAY_ID("OuterBorder"), { .border = { .width = { .bottom = 3 }, .color = COLOR_YELLOW } }) { // ... } ``` -------------------------------- ### Compile with CLAY_DISABLE_CULLING Source: https://github.com/nicbarker/clay/blob/main/README.md Example of how to disable Clay's visibility culling by passing a preprocessor directive during compilation. ```bash clang -DCLAY_DISABLE_CULLING main.c ... ``` -------------------------------- ### 300x300 Container with 1px Red Border Source: https://github.com/nicbarker/clay/blob/main/README.md Example of creating a fixed-size container with a 1px red border applied to all edges. ```C // 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 } }) { // ... } ``` -------------------------------- ### Configure C Project with CMake Source: https://github.com/nicbarker/clay/blob/main/examples/terminal-example/CMakeLists.txt Sets up the minimum CMake version, project name, and C standard for the build. This is a foundational step for any CMake project. ```cmake cmake_minimum_required(VERSION 3.27) project(clay_examples_terminal C) set(CMAKE_C_STANDARD 99) ``` -------------------------------- ### Initialize Clay Game State Source: https://github.com/nicbarker/clay/blob/main/examples/clay-official-website/build/clay/index.html Initializes the game state for Clay, setting up necessary flags. This should be called once at the start of the application. ```javascript function init() { window.dKeyPressedThisFrame = false; window.dKeyPressedThisFrame = false; } ``` -------------------------------- ### Clay_Initialize Source: https://github.com/nicbarker/clay/blob/main/README.md Initializes the Clay UI system with a memory arena. ```APIDOC ## Clay_Initialize ### Description Initializes the Clay UI system. This function must be called before any other Clay functions. It requires a pre-allocated memory arena. ### Function Signature `void Clay_Initialize(Clay_Arena* arena);` ``` -------------------------------- ### Import Clay Odin Bindings Source: https://github.com/nicbarker/clay/blob/main/bindings/odin/README.md Import the necessary Clay Odin bindings into your project. ```odin import clay "clay-odin" ``` -------------------------------- ### Initialize Clay Arena Source: https://github.com/nicbarker/clay/blob/main/bindings/odin/README.md Determine the minimum memory size, allocate memory, create an arena, and initialize Clay with a custom error handler and dimensions. ```odin error_handler :: proc "c" (errorData: clay.ErrorData) { // Do something with the error data. } min_memory_size := clay.MinMemorySize() memory := make([^]u8, min_memory_size) arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(uint(min_memory_size), memory) clay.Initialize(arena, {1080, 720}, { handler = error_handler }) ``` -------------------------------- ### Configure Child Alignment Source: https://github.com/nicbarker/clay/blob/main/README.md Controls the alignment of children within the parent container. This example aligns children to the left horizontally and center vertically. ```c CLAY(CLAY_ID("Element"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } }) ``` -------------------------------- ### Set Text Color with CLAY_TEXT Source: https://github.com/nicbarker/clay/blob/main/README.md Example of setting the text color for a CLAY_TEXT element using RGBA float values. The renderer interprets these values. ```C CLAY_TEXT(text, { .textColor = {120, 120, 120, 255} }) ``` -------------------------------- ### Initialize Clay Application Source: https://github.com/nicbarker/clay/blob/main/examples/clay-official-website/index.html Initializes the Clay application by setting up memory, heap, and scratch space, then calling the main initialization function. This is typically done after instantiating the WebAssembly module. ```javascript const importObject = { clay: { measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig, userData) => { let stringLength = memoryDataView.getUint32(textToMeasure, true); let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true); let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition); let textDecoder = new TextDecoder("utf-8"); let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength)); let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`); memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true); memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true); }, queryScrollOffsetFunction: (addressOfOffset, elementId) => { let container = document.getElementById(elementId.toString()); if (container) { memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); } }, }, }; const { instance } = await WebAssembly.instantiateStreaming( fetch("/clay/index.wasm"), importObject ); memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); scratchSpaceAddress = instance.exports.__heap_base.value; let clayScratchSpaceAddress = instance.exports.__heap_base.value + 1024; heapSpaceAddress = instance.exports.__heap_base.value + 2048; let arenaAddress = scratchSpaceAddress + 8; window.instance = instance; createMainArena(arenaAddress, heapSpaceAddress); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); instance.exports.SetScratchMemory(clayScratchSpaceAddress); renderCommandSize = getStructTotalSize(renderCommandDefinition); renderLoop(); ``` -------------------------------- ### Configure Element as Image Source: https://github.com/nicbarker/clay/blob/main/README.md Sets up an element to display an image by providing image data. This emits an IMAGE render command. ```C CLAY(CLAY_ID("Element"), { .image = { .imageData = &myImage } }) ``` -------------------------------- ### Initialize Canvas2D Renderer Source: https://github.com/nicbarker/clay/blob/main/renderers/web/canvas2d/clay-canvas2d-renderer.html Loads fonts, sets up HTML and canvas elements, and initializes the WebAssembly instance. Handles mouse and touch input events. ```javascript async function init() { await Promise.all(fontsById.map(f => document.fonts.load(`12px "${f}"`))); window.htmlRoot = document.body.appendChild(document.createElement('div')); window.canvasRoot = document.body.appendChild(document.createElement('canvas')); window.canvasContext = window.canvasRoot.getContext("2d"); window.mousePositionXThisFrame = 0; window.mousePositionYThisFrame = 0; window.mouseWheelXThisFrame = 0; window.mouseWheelYThisFrame = 0; window.touchDown = false; let zeroTimeout = null; addEventListener("wheel", (event) => { window.mouseWheelXThisFrame = event.deltaX * -0.1; window.mouseWheelYThisFrame = event.deltaY * -0.1; clearTimeout(zeroTimeout); zeroTimeout = setTimeout(() => { window.mouseWheelXThisFrame = 0; window.mouseWheelYThisFrame = 0; }, 10); }); function handleTouch(event) { if (event.touches.length === 1) { window.touchDown = true; window.mousePositionXThisFrame = event.changedTouches[0].pageX; window.mousePositionYThisFrame = event.changedTouches[0].pageY; } } document.addEventListener("touchstart", handleTouch); document.addEventListener("touchmove", handleTouch); document.addEventListener("touchend", () => { window.touchDown = false; window.mousePositionXThisFrame = 0; window.mousePositionYThisFrame = 0; }) document.addEventListener("mousemove", (event) => { window.mousePositionXThisFrame = event.x; window.mousePositionYThisFrame = event.y; }); document.addEventListener("mousedown", (event) => { window.mouseDown = true; }); document.addEventListener("keydown", (event) => { if (event.key === "ArrowDown") { window.arrowKeyDownPressedThisFrame = true; } if (event.key === "ArrowUp") { window.arrowKeyUpPressedThisFrame = true; } if (event.key === "d") { window.dKeyPressedThisFrame = true; } }); const importObject = { clay: { measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => { let stringLength = memoryDataView.getUint32(textToMeasure, true); let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true); let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition); let textDecoder = new TextDecoder("utf-8"); let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength)); let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`); memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true); memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true); } }, }; const { instance } = await WebAssembly.instantiateStreaming( fetch("./index.wasm"), importObject ); memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); scratchSpaceAddress = instance.exports.__heap_base.value; heapSpaceAddress = instance.exports.__heap_base.value + 1024; let arenaAddress = scratchSpaceAddress; window.instance = instance; createMainArena(arenaAddress, heapSpaceAddress); instance.exports.Clay_Initialize(arenaAddress); renderCommandSize = getStructTotalSize(renderCommandDefinition); renderLoop(); } ``` -------------------------------- ### Get Current Clay Context Source: https://github.com/nicbarker/clay/blob/main/README.md Retrieves the currently active Clay context. Returns null if no context has been set. This is useful for managing multiple Clay instances. ```c Clay_Context* Clay_GetCurrentContext(); ``` -------------------------------- ### Clay_BeginLayout Source: https://github.com/nicbarker/clay/blob/main/README.md Begins the layout process for UI elements. ```APIDOC ## Clay_BeginLayout ### Description Begins the layout process. This function should be called before defining any UI elements for the current frame. ### Function Signature `void Clay_BeginLayout(void);` ``` -------------------------------- ### Begin Layout Calculation Source: https://github.com/nicbarker/clay/blob/main/README.md Prepares Clay to calculate a new layout. This function must be called each frame before declaring any elements using the Element Macros. ```c void Clay_BeginLayout(); ``` -------------------------------- ### Configure Floating Element Z-Index in Clay Source: https://github.com/nicbarker/clay/blob/main/README.md Set the `.zIndex` to control the drawing order of floating elements. Higher values are drawn on top. Example sets the zIndex to 1. ```C CLAY(CLAY_ID("Floating"), { .floating = { .zIndex = 1 } }) ``` -------------------------------- ### Configure Floating Element Expansion in Clay Source: https://github.com/nicbarker/clay/blob/main/README.md Use the `.expand` field to set the width and height of the floating container before its children are laid out. Example expands the container to 16x16 units. ```C CLAY(CLAY_ID("Floating"), { .floating = { .expand = { 16, 16 } } }) ``` -------------------------------- ### Render Loop Initialization and Update Source: https://github.com/nicbarker/clay/blob/main/renderers/web/html/clay-html-renderer.html Sets up the initial state and the main rendering loop. It calls `UpdateDrawFrame` on the WebAssembly instance and then `renderLoopHTML` for HTML-specific rendering, followed by scheduling the next frame using `requestAnimationFrame`. It also resets mouse button states. ```javascript function renderLoop(currentTime) { const elapsed = currentTime - previousFrameTime; previousFrameTime = currentTime; instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, elapsed / 1000); renderLoopHTML(); requestAnimationFrame(renderLoop); window.mouseDown = false; } init(); ``` -------------------------------- ### Configure Layout and Styling with Clay_ElementDeclaration Source: https://github.com/nicbarker/clay/blob/main/README.md Illustrates configuring element layout and styling using the Clay_ElementDeclaration struct. This allows for detailed control over padding, direction, and background colors. ```C 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 } ``` -------------------------------- ### Get Scroll Offset Source: https://github.com/nicbarker/clay/blob/main/README.md Retrieves the internally stored scroll offset for the currently active element. Primarily used with clip elements and the `.childOffset` field to implement scrolling containers. ```c Clay_Vector2 Clay_GetScrollOffset(); ``` ```c // Create a horizontally scrolling container CLAY(CLAY_ID("ScrollContainer"), { .clip = { .horizontal = true, .childOffset = Clay_GetScrollOffset() } }) ``` -------------------------------- ### Configure Floating Element Offset in Clay Source: https://github.com/nicbarker/clay/blob/main/README.md Use the `.offset` field to apply a position offset to the floating container after layout calculations. Example shows an offset of -24px on both x and y axes. ```C CLAY(CLAY_ID("Floating"), { .floating = { .offset = { -24, -24 } } }) ``` -------------------------------- ### Build Termbox2 Demo with CMake Source: https://github.com/nicbarker/clay/blob/main/examples/termbox2-demo/readme.md Provides the shell commands to build the termbox2 demo executable using CMake. This involves creating a build directory, navigating into it, configuring the build with CMake, and then compiling the project. ```sh mkdir build cd build cmake .. make ``` -------------------------------- ### Clay_BeginLayout Source: https://github.com/nicbarker/clay/blob/main/README.md Prepares the Clay library to calculate a new layout. This function must be called at the beginning of each frame or layout pass, before any element macros are declared. ```APIDOC ## Clay_BeginLayout ### Description Prepares Clay to calculate a new layout. Call this before declaring any element macros. ``` -------------------------------- ### Configure Exit Transition Trigger Source: https://github.com/nicbarker/clay/blob/main/README.md Control when an element's exit transition triggers. Set to `CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITS` to have the animation start when the parent element disappears, which is useful for list item animations. ```C CLAY(CLAY_ID("Transition"), { .transition = { .exit = { .trigger = CLAY_TRANSITION_EXIT_TRIGGER_WHEN_PARENT_EXITS } } }) ``` -------------------------------- ### Handle Text Render Command Source: https://github.com/nicbarker/clay/blob/main/examples/clay-official-website/index.html Configures text elements, including content, color, and font size. It decodes string contents from memory and applies styling. ```javascript case (CLAY_RENDER_COMMAND_TYPE_TEXT): { let config = renderCommand.renderData.text; let configMemory = JSON.stringify(config); let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value)); if (configMemory !== elementData.previousMemoryConfig) { element.className = 'text'; let textColor = config.textColor; let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR); element.s ```