Skip to content

0.13 - Simplified Config, Multi Instance Support

Latest
Compare
Choose a tag to compare
@nicbarker nicbarker released this 12 Feb 00:26
· 14 commits to main since this release

New Features

Replace Element Macros with a Single Configuration Struct

We've removed the majority of the configuration macros in favour of a large config struct that you pass to CLAY.
You can find exhaustive documentation of this struct and its fields in the README.

// Before - macros
CLAY(
    CLAY_ID("FloatingContainer"),
    CLAY_LAYOUT({
        .sizing = {
            .width = CLAY_SIZING_FIXED(300),
            .height = CLAY_SIZING_FIXED(300)
        },
        .padding = { 16, 16, 16, 16 }
    }),
    CLAY_FLOATING({
        .zIndex = 1,
        .attachment = {
            CLAY_ATTACH_POINT_CENTER_TOP,
            CLAY_ATTACH_POINT_CENTER_TOP
        },
        .offset = { 0, 0 }
    }),
    CLAY_BORDER_OUTSIDE({ .color = {80, 80, 80, 255}, .width = 2 }),
    CLAY_RECTANGLE({ .color = {140,80, 200, 200 }}
) {
    // children
}
// After - single struct
CLAY({
    .id = CLAY_ID("FloatingContainer"),
    .layout = {
        .sizing = {
            .width = CLAY_SIZING_FIXED(300),
            .height = CLAY_SIZING_FIXED(300)
        },
        .padding = { 16, 16, 16, 16 }
    },
    .backgroundColor = { 140, 80, 200, 200 },
    .floating = {
        .attachTo = CLAY_ATTACH_TO_PARENT,
        .zIndex = 1,
        .attachPoints = {
            CLAY_ATTACH_POINT_CENTER_TOP,
            CLAY_ATTACH_POINT_CENTER_TOP
        },
        .offset = { 0, 0 }
    },
    .border = {
        .width = { 2, 2, 2, 2 },
        .color = { 80, 80, 80, 255 }
    },
}) {
    // children
}

The two main benefits we get from this approach are:

  1. The available configuration options are naturally discoverable for those using intellisense just by pressing . inside CLAY({ }), whereas previously you had to look up the macros in the documentation.
  2. You can define the configuration for an entire element and save it as a struct:
Clay_ElementDeclaration buttonStyle = (Clay_ElementDeclaration) {
    .layout = { .padding = { .left = 16, .right = 16 } },
    .backgroundColor = COLOR_RED,
    .cornerRadius = { 16, 16, 16, 16 }
}

// later
CLAY(buttonStyle) {
    // button contents
}

// Or return a declaration from a function
Clay_ElementDeclaration GetButtonStyle(bool hovered) {
    return (Clay_ElementDeclaration) {
        .layout = { ... },
        .backgroundColor = hovered ? BLUE : RED
    };
}

// Generate element style from template
CLAY(GetButtonStyle(Clay_Hovered())) {
    // button contents
}

"Context" support (i.e. Multiple independent Clay instances & groundwork for multithreading support)

Thanks to some great discussions in the Clay discord and great work by @monodop here: #174 Clay now supports creation and management of multiple "instances" which are separate from one another. This is not only useful for systems with separate internal "windows" requiring different layout trees, but also paves the way for us to support running these instances on separate threads.

Screenshot 2025-02-12 at 9 35 56β€―am
An example of the same layout rendered twice by two separate Clay instances

For more information, see the Multi Context Example

Error Handler

Clay now requires that you bind an error handler callback function when calling Clay_Initialize. This allows us to catch and report some common mistakes such as forgetting to bind a text measurement function with Clay_SetMeasureText.

image

Clay_GetElementData for querying bounding box data

A new function, Clay_GetElementData(Clay_ElementId id) is available for directly looking up the final calculated bounding box of an element by ID.

.textAlignment support for CLAY_TEXT_CONFIG

Wrapped text can now be aligned to LEFT, CENTER or RIGHT using the .textAlignment field of CLAY_TEXT_CONFIG.

Screenshot 2025-02-12 at 10 49 19β€―am

MSVC Support & C99 Compliance

Thanks to some tireless work from @FintasticMan, Clay can now be compiled using MSVC with C11, 17, and 23, has much closer strict C99 compliance, and compiles with -Wextra and -Wpedantic.

New Renderers

Thanks to some hard work from our contributors, there are a number of new example renderers in 0.13, including:

  • SDL2
  • SDL3
  • Cairo

New Language Bindings

Thanks to some hard work from our contributors, there are a number of new language bindings in 0.13, including:

Zig
https://codeberg.org/Zettexe/clay-zig
https://github.com/johan0A/clay-zig-bindings

Rust
https://github.com/clay-ui-rs/clay
https://crates.io/crates/clay-layout

C#
https://github.com/Orcolom/clay-cs

C++
https://github.com/TimothyHoytBSME/ClayMan

Deprecations

The preprocessor defines used for adding additional fields to element configuration structs such as CLAY_EXTEND_CONFIG_RECTANGLE have been removed in favour of the new .userData pointer field of Clay_ElementDeclaration. This was out of necessity and can be explained in more detail upon request πŸ˜…

Migration Guide

  1. Wrap the inside of the CLAY(...) macro in braces for the new form CLAY({ ... })
- CLAY(
+ CLAY({
    CLAY_ID(...),
    CLAY_RECTANGLE({ ... }),
    CLAY_BORDER({ ... }),
    CLAY_FLOATING({ ... })
- )
+ })
  1. Mass rename the following macros:
  • CLAY_BORDER({ ... })
  • CLAY_IMAGE({ ... })
  • CLAY_SCROLL({ ... })
  • CLAY_FLOATING({ ... })
  • CLAY_CUSTOM({ ... })

into struct fields of the form .border = { ... }, .floating = { ... } etc

CLAY({
    CLAY_ID(...),
    CLAY_RECTANGLE({ ... }),
-   CLAY_BORDER({ ... }),
+   .border = { ... },
-   CLAY_FLOATING({ ... })
+   .floating = { ... },
})
  1. Replace the CLAY_ID macro with the struct field .id = CLAY_ID
CLAY({
-   CLAY_ID(...),
+   .id = CLAY_ID(...),
    CLAY_RECTANGLE({ ... }),
    .border = { ... },
    .floating = { ... }
})
  1. Replace CLAY_RECTANGLE macros with two struct fields, .backgroundColor and .cornerRadius

Previous, fields like .cornerRadius would need to be repeated for rectangle, image, and border configuration macros. .backgroundColor and .cornerRadius are now shared fields that affect multiple render commands, and are declared once at the top level.

CLAY({
    .id = CLAY_ID(...),
-   CLAY_RECTANGLE({ .color = COLOR_RED, .cornerRadius = { 10, 10, 10, 10 } }),
+   .backgroundColor = COLOR_RED,
+   .cornerRadius = { 10, 10, 10, 10 },
    .border = { ... },
    .floating = { ... }
})
  1. Padding is now represented by four values, not just two

Padding in 0.12 was represented as a mirrored .x, .y pair. It's been changed in 0.13 to a more standard .left, .right, .top, .bottom.
You'll need to update your padding to match the new structure.

- CLAY({ .layout = { .padding = { 8, 12 } });
+ CLAY({ .layout = { .padding = { 8, 8, 12, 12 } });

// Designated initializers
- CLAY({ .layout = { .padding = { .x = 8, .y = 12 } });
+ CLAY({ .layout = { .padding = { .left = 8, .right = 8, .top = 12, .bottom = 12 } });
  1. Depending on your language and compiler, CLAY_SIZING_GROW might need to be passed 0 when you don't specify min and max size:
- CLAY({ .layout = { .width = CLAY_SIZING_GROW() } });
+ CLAY({ .layout = { .width = CLAY_SIZING_GROW(0) } });
  1. Update border declarations

Borders now share a single color, and use the shared .cornerRadius from the outer declaration.

- CLAY_BORDER({ .left = { 20, COLOR_BLUE }, .right = { 20, COLOR_BLUE }, .bottom = { 20, COLOR_BLUE }, .cornerRadius = { 10, 10, 10, 10 } })
+ .cornerRadius = { 10, 10, 10, 10 },
+ .border = { .width = { .left = 20, .right = 20, .bottom = 20 }, .color = COLOR_BLUE }
  1. Changes to Floating Elements

Replacing CLAY_FLOATING, The .floating config of the declaration struct now has a new field, .attachTo, which can be one of several values:

CLAY_ATTACH_TO_NONE (default)
CLAY_ATTACH_TO_PARENT
CLAY_ATTACH_TO_ELEMENT_ID
CLAY_ATTACH_TO_ROOT

Unlike previously where just calling CLAY_FLOATING was enough to switch an element to floating mode, now .attachTo has to be set to some value other than the default NONE. CLAY_ATTACH_TO_PARENT is the old default behaviour, CLAY_ATTACH_TO_ELEMENT_ID requires that you set a parentId to attach the floating element to, and the new CLAY_ATTACH_TO_ROOT option allows you to position the element relative to the root of the layout (i.e. the entire screen), by using the .offset field.

// Attach to parent
- CLAY_FLOATING({ .offset = { 12, 12 } });
+ .floating = { .attachTo = CLAY_ATTACH_TO_PARENT,  .offset = { 12, 12 } };

// Attach to specific ID
- CLAY_FLOATING({ .parentId = CLAY_ID("targetElement") });
+ .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_ID,  .parentId = CLAY_ID("targetElement") };
  1. Clay_SetMeasureTextFunction must now be called after Clay_Initialize

  2. Clay_Initialize now requires an error handler callback.

void HandleClayErrors(Clay_ErrorData errorData) {
    // See the Clay_ErrorData struct for more information
    printf("%s", errorData.errorText.chars);
    switch(errorData.errorType) {
        // etc
    }
}

- Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight });
+ Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });
  1. Update to new Clay_MeasureTextFunction signature
    Since 0.12, the function signature of the user provided Clay_MeasureText has changed for consistency and additional features.
- Clay_Dimensions Raylib_MeasureText(Clay_String *text, Clay_TextElementConfig *config) {
+ Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
  1. Update custom renderer implementations
    The structure of Clay_RenderCommand has changed significantly, and should offer better performance and convenience. Custom renderers will need to be updated to handle the new structure.

The easiest way to get started would be to look at the diff of the updated Raylib Renderer, and the new README documentation for Clay_RenderCommand

Bug Fixes

0.13 contains a large number of bug fixes both to edge case handling and the core layout algorithm, but it's worth specifically highlighting that elements now shrink much more gracefully & sensibly than they did in 0.12:

Before:
Screenshot 2025-01-08 at 7 20 11β€―pm

After:
Screenshot 2025-01-08 at 7 19 26β€―pm

Special Thanks

Special thanks to @emoon, @FintasticMan, @monodop, @bullno1 and @TimothyHoytBSME for their significant contributions to this release, not just from a code perspective but also for their tireless work helping people in the Discord and on Github issues. Couldn't have done it without you!

New Contributors

Full List of Changes

  • [Documentation] docs: remove some inconsistencies with current API by @27justin in #47
  • [Core] Forward declare Clay__OpenTextElement by @bullno1 in #49
  • [Renderers/Cairo] Include new cairo renderer by @27justin in #48
  • [Core] Fix text cache overflow by @nicbarker in #51
  • [Core] Improve overflow handling / CLAY_MAX_ELEMENT_COUNT exceeded by @nicbarker in #52
  • [Core] Fix: moved CLAY__MIN and CLAY__MAX to public macros by @OleksiiBulba in #55
  • [Renderers/HTML] Implement native scroll containers in HTML renderer by @nicbarker in #54
  • [Renderers/Raylib] Added window dimensions and title to Clay_Raylib_Initialize function by @OleksiiBulba in #56
  • [Documentation] Fix variable name in README.md by @AMurkin in #59
  • [Core] Allow floating configuration to capture pointer by @nicbarker in #66
  • [Core] fix: move internal types to stdint specific. by @mrneo240 in #78
  • [Core] Implement Error Handler / Callback by @nicbarker in #105
  • [Renderers/raylib] Update files for v5.5 by @CrackedPixel in #109
  • [Core] Fix a couple of standards-compliance issues with C99 by @FintasticMan in #81
  • [Renderers/SDL2] Create initial SDL2 renderer by @nicbarker in #115
  • [Examples/Intro] Fix NULL pointer deref due to huuge malloc by @FintasticMan in #120
  • [Core] Add Clay_IsDebugModeEnabled() by @juniorrantila in #130
  • [Core] Fix more C99 compliance issues by @FintasticMan in #118
  • [Compilers] C projects should use C flags rather than CXX flags by @SuperOptimizer in #123
  • [Compilers] C++ projects should use CXX flags by @SuperOptimizer in #136
  • [Renderers/Cairo] Add FindCairo.cmake by @SuperOptimizer in #122
  • [Compilers] Fixed compilation when using Clang on Windows by @St0wy in #134
  • [Bindings/Odin] Update Odin bindings to latest by @nicbarker in #151
  • [Core] Simplify CLAY macro by @FintasticMan in #119
  • [Core] Standardise number types to int32_t for array indices, lengths and capacities by @nicbarker in #152
  • [Core] Fix NULL pointer dereference when parent of floating container is invalid ID by @peter15914 in #153
  • [Renderers/Web] treat RenderCommand.commandType as uint8_t instead of uint32_t by @bullyingteen in #162
  • [Core] Bug in text wrapping at very narrow widths by @bullyingteen in #163
  • [Core] Fix local id calculation by @bullno1 in #50
  • [Core] Fix errors due to cast to same non-trivial type by @FintasticMan in #155
  • [Core] Fix default struct initialiser in C++ by @FintasticMan in #143
  • [Documentation] Updated example for Clay_SetPointerState by @davidstyrbjorn in #167
  • [Layout] Improve shrink size distribution by @nicbarker in #173
  • [Documentation] Updated example for Clay_SetPointerState by @davidstyrbjorn in #169
  • [Core] Add check for supported C/C++ versions by @FintasticMan in #144
  • [Core] Multi instance support by @monodop in #174
  • [Core] Remove ##__VA_ARGS__ by @FintasticMan in #150
  • [Compilers] Fix MSVC compilation with CMake by @Funto in #178
  • [Documentation] Summary & Readability improvement by @Mathys-Gasnier in #125
  • [Core] Fix text wrapping handling with explicit newline characters by @nicbarker in #192
  • [Core] Add a function to reset text measurement cache by @monodop in #181
  • [Core] [Breaking] Split padding values into left, right, top, bottom by @nicbarker in #195
  • [Core] Add API to query element bounding boxes by @nicbarker in #199
  • [Core] Don't divide zero by zero by @mikejsavage in #200
  • [Renderers/SDL2] Extend SDL2 Renderer and SDL2-video-demo by @ppebb in #208
  • [Bindings/Zig] Add external link to zig bindings by @Zettexe in #210
  • [Renderers/SDL3] Adds an example using SDL3 as a renderer by @LiquidityC in #107
  • [Core] SetMeasureText and SetQueryScrollOffset takes userData by @emoon in #212
  • [Core] Convert measureText pointer to value string slice by @nicbarker in #214
  • [Examples/clay-video-demo] fixed video demo padding by @TimothyHoytBSME in #205
  • [Renderers/SDL3] Add rounded corners rectangle functionality by @ArnauNau in #219
  • [Bindings/C++] Link and information for ClayMan, a C++ wrapper library for clay by @TimothyHoytBSME in #218
  • [Renderers/SDL3] Add borders and rounded borders functionality. by @ArnauNau in #220
  • [CMake] Make Examples Optional in CMAKE by @CJFEdu in #216
  • [Core] Add z-index and string base to Render Commands by @nicbarker in #227
  • [Core] Fix: Clay_MinMemorySize() and Clay_Initialize() now report same memory size when using non-default MaxElementCount by @noflashbang in #233
  • [Renderers/SDL2] Don't take addresses of temporaries. by @radiant64 in #232
  • [Core] Replace generated arrays with macro declarations, align cache lines to 64 bytes by @nicbarker in #235
  • [Core] Add option to hash text contents to text config by @nicbarker in #238
  • [Core] Copy elementId in Clay__AddHashMapItem() in case underlying stringId has changed by @monodop in #239
  • [Core] Fix int conversion errors in msvc by @monodop in #242
  • [Core] Replace config macros with a single unified configuration struct by @nicbarker in #240
  • [Core] add CLAY_DISABLE_SIMD flag to conditionally disable SIMD includes by @johan0A in #251
  • [Renderers/SDL2] Add rounded rectangle support to sdl2 renderer; feature-completes sdl2 renderer by @steviegt6 in #245
  • [Bindings/Odin] Add get/set current context method to Odin bindings by @nadako in #252
  • [Bindings/csharp] Create csharp bindings README by @Orcolom in #247
  • [Core] Fix a bug when trying to enable debug view with too many elements in the layout. by @FelixBreitweiser in #255