📦nicbarker / clay
← 返回首页
High performance UI layout library in C.
⚖️ Zlib#layout#ui🌐 nicbarker.com📅 创建于 2024-07-21🔄 更新于 2026-05-20

📄 仓库 README

在 GitHub 查看 ↗
来自 GitHub 仓库自述文件 · 版权归原作者所有

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.

A screenshot of a code IDE with lots of visual and textual elements

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:

Clay Example

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

📈 近 7 天 Star 趋势

每日新增 Star(柱)与累计 Star(线)· 数据来自 GitHub

🏆 排名变化趋势

在各榜单中的排名历史(数字越小越靠前)

📋 历史上榜记录

每次快照的详细数据
日期 榜单 排名 周期新增 总 STAR