diff --git a/README.md b/README.md index d61ec9d..a7d66df 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Screenshot from 2025-01-07 17-05-01](https://github.com/user-attachments/assets/8f38e8bf-00aa-4e16-be96-b7a0d81f4313) > [!IMPORTANT] -> Zig 0.14.0 or higher is required. (tested with zig 0.14.0-dev.2851+b074fb7dd) +> Zig 0.14.0 or higher is required. (tested with zig 0.14.0-dev.3062+ff551374a) > [!NOTE] > This project is currently in beta. @@ -20,34 +20,32 @@ Some differences between the C API and the Zig bindings include: In C: ```C -// C macro for creating a scope -CLAY( - CLAY_ID("SideBar"), - CLAY_LAYOUT({ +CLAY({ // C macro for creating a scope + .id = CLAY_ID("SideBar"), + .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, - .sizing = { .height = CLAY_SIZING_GROW(), .width = CLAY_SIZING_FIXED(300) }, - .padding = {16, 16}, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP }, - .childGap = 16, - }), - CLAY_RECTANGLE({ .color = COLOR_LIGHT }) -) { + .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, + .padding = CLAY_PADDING_ALL(16), + .childAlignment = .{ .x = CLAY_ALIGN_X_CENTER , .y = .CLAY_ALIGN_Y_TOP }, + .childGap = 16 + }, + .backgroundColor = COLOR_LIGHT +}){ // Child elements here } ``` - In Zig: ```Zig -clay.UI()(&.{ // function call for creating a scope - .ID("SideBar"), - .layout(.{ - .direction = .TOP_TO_BOTTOM, - .sizing = .{ .h = .grow, .w = .fixed(300) }, +clay.UI()(.{ // function call for creating a scope + .id = .ID("SideBar"), + .layout = .{ + .direction = .top_to_bottom, + .sizing = .{ .w = .fixed(300), .h = .grow }, .padding = .all(16), - .child_alignment = .{ .x = .CENTER, .y = .TOP }, + .child_alignment = .{ .x = .center, .y = .top }, .child_gap = 16, - }), - .rectangle(.{ .color = light_grey }), + }, + .background_color = light_grey, })({ // Child elements here }); @@ -55,7 +53,7 @@ clay.UI()(&.{ // function call for creating a scope ## install -1. Add `zclay` to the depency list in `build.zig.zon`: +1. Add `zclay` to the dependency list in `build.zig.zon`: ```sh zig fetch --save https://github.com/johan0A/clay-zig-bindings/archive/.tar.gz @@ -78,25 +76,25 @@ compile_step.root_module.addImport("zclay", zclay_dep.module("zclay")); 2. Ask clay for how much static memory it needs using [clay.minMemorySize()](https://github.com/nicbarker/clay/blob/main/README.md#clay_minmemorysize), create an Arena for it to use with [clay.createArenaWithCapacityAndMemory(minMemorySize, memory)](https://github.com/nicbarker/clay/blob/main/README.md#clay_createarenawithcapacityandmemory), and initialize it with [clay.Initialize(arena)](https://github.com/nicbarker/clay/blob/main/README.md#clay_initialize). ```zig -const min_memory_size: u32 = cl.minMemorySize(); +const min_memory_size: u32 = clay.minMemorySize(); const memory = try allocator.alloc(u8, min_memory_size); defer allocator.free(memory); -const arena: cl.Arena = cl.createArenaWithCapacityAndMemory(memory); -_ = cl.initialize(arena, .{ .h = 1000, .w = 1000 }, .{}); -cl.setMeasureTextFunction(renderer.measureText); +const arena: clay.Arena = clay.createArenaWithCapacityAndMemory(memory); +_ = clay.initialize(arena, .{ .h = 1000, .w = 1000 }, .{}); +clay.setMeasureTextFunction(renderer.measureText); ``` 3. Provide a `measureText(text, config)` function with [clay.setMeasureTextFunction(function)](https://github.com/nicbarker/clay/blob/main/README.md#clay_setmeasuretextfunction) so that clay can measure and wrap text. ```zig // Example measure text function -pub fn measureText(clay_text: []const u8, config: *clay.TextElementConfig) clay.Dimensions { +pub fn measureText(clay_text: []const u8, config: *clay.TextElementConfig, user_data: void) clay.Dimensions { // clay.TextElementConfig contains members such as fontId, fontSize, letterSpacing etc // Note: clay.String.chars is not guaranteed to be null terminated } // Tell clay how to measure text -clay.setMeasureTextFunction(measureText) +clay.setMeasureTextFunction({}, measureText) ``` 4. **Optional** - Call [clay.setPointerPosition(pointerPosition)](https://github.com/nicbarker/clay/blob/main/README.md#clay_setpointerposition) if you want to use mouse interactions. @@ -112,82 +110,81 @@ clay.setPointerState(.{ 5. Call [clay.beginLayout()](https://github.com/nicbarker/clay/blob/main/README.md#clay_beginlayout) and declare your layout using the provided functions. ```Zig -const light_grey: cl.Color = .{ 224, 215, 210, 255 }; -const red: cl.Color = .{ 168, 66, 28, 255 }; -const orange: cl.Color = .{ 225, 138, 50, 255 }; -const white: cl.Color = .{ 250, 250, 255, 255 }; +const light_grey: clay.Color = .{ 224, 215, 210, 255 }; +const red: clay.Color = .{ 168, 66, 28, 255 }; +const orange: clay.Color = .{ 225, 138, 50, 255 }; +const white: clay.Color = .{ 250, 250, 255, 255 }; -const sidebar_item_layout: cl.LayoutConfig = .{ .sizing = .{ .w = .grow, .h = .fixed(50) } }; +const sidebar_item_layout: clay.LayoutConfig = .{ .sizing = .{ .w = .grow, .h = .fixed(50) } }; // Re-useable components are just normal functions -fn sidebarItemComponent(index: usize) void { - cl.UI()(&.{ - .IDI("SidebarBlob", @intCast(index)), - .layout(sidebar_item_layout), - .rectangle(.{ .color = orange }), +fn sidebarItemComponent(index: u32) void { + clay.UI()(.{ + .id = .IDI("SidebarBlob", index), + .layout = sidebar_item_layout, + .background_color = orange, })({}); } // An example function to begin the "root" of your layout tree -fn createLayout(profile_picture: *const rl.Texture2D) cl.ClayArray(cl.RenderCommand) { - cl.beginLayout(); - cl.UI()(&.{ - .ID("OuterContainer"), - .layout(.{ .direction = .LEFT_TO_RIGHT, .sizing = .grow, .padding = .all(16), .child_gap = 16 }), - .rectangle(.{ .color = white }), +fn createLayout(profile_picture: *const rl.Texture2D) clay.ClayArray(clay.RenderCommand) { + clay.beginLayout(); + clay.UI()(.{ + .id = .ID("OuterContainer"), + .layout = .{ .direction = .left_to_right, .sizing = .grow, .padding = .all(16), .child_gap = 16 }, + .background_color = white, })({ - cl.UI()(&.{ - .ID("SideBar"), - .layout(.{ - .direction = .TOP_TO_BOTTOM, + clay.UI()(.{ + .id = .ID("SideBar"), + .layout = .{ + .direction = .top_to_bottom, .sizing = .{ .h = .grow, .w = .fixed(300) }, .padding = .all(16), - .child_alignment = .{ .x = .CENTER, .y = .TOP }, + .child_alignment = .{ .x = .center, .y = .top }, .child_gap = 16, - }), - .rectangle(.{ .color = light_grey }), + }, + .background_color = light_grey, })({ - cl.UI()(&.{ - .ID("ProfilePictureOuter"), - .layout(.{ .sizing = .{ .w = .grow }, .padding = .all(16), .child_alignment = .{ .x = .LEFT, .y = .CENTER }, .child_gap = 16 }), - .rectangle(.{ .color = red }), + clay.UI()(.{ + .id = .ID("ProfilePictureOuter"), + .layout = .{ .sizing = .{ .w = .grow }, .padding = .all(16), .child_alignment = .{ .x = .left, .y = .center }, .child_gap = 16 }, + .background_color = red, })({ - cl.UI()(&.{ - .ID("ProfilePicture"), - .layout(.{ .sizing = .{ .h = .fixed(60), .w = .fixed(60) } }), - .image(.{ .source_dimensions = .{ .h = 60, .w = 60 }, .image_data = @ptrCast(profile_picture) }), + clay.UI()(.{ + .id = .ID("ProfilePicture"), + .layout = .{ .sizing = .{ .h = .fixed(60), .w = .fixed(60) } }, + .image = .{ .source_dimensions = .{ .h = 60, .w = 60 }, .image_data = @ptrCast(profile_picture) }, })({}); - cl.text("Clay - UI Library", .{ .font_size = 24, .color = light_grey }); + clay.text("Clay - UI Library", .{ .font_size = 24, .color = light_grey }); }); - for (0..5) |i| sidebarItemComponent(i); + for (0..5) |i| sidebarItemComponent(@intCast(i)); }); - cl.UI()(&.{ - .ID("MainContent"), - .layout(.{ .sizing = .grow }), - .rectangle(.{ .color = light_grey }), + clay.UI()(.{ + .id = .ID("MainContent"), + .layout = .{ .sizing = .grow }, + .background_color = light_grey, })({ //... }); }); - return cl.endLayout(); + return clay.endLayout(); } ``` 6. Call [clay.endLayout()](https://github.com/nicbarker/clay/blob/main/README.md#clay_endlayout) and process the resulting [clay.RenderCommandArray](https://github.com/nicbarker/clay/blob/main/README.md#clay_rendercommandarray) in your choice of renderer. ```zig -render_commands: clay.ClayArray(clay.RenderCommand) = clay.endLayout(window_width, window_height) - -var i: usize = 0; -while (i < render_commands.length) : (i += 1) { - const render_command = clay.renderCommandArrayGet(render_commands, @intCast(i)); - const bounding_box = render_command.bounding_box; - switch (render_command.command_type) { - .none => {}, - .text => { - ... +pub fn clayRaylibRender(render_commands: *clay.ClayArray(clay.RenderCommand), allocator: std.mem.Allocator) void { + var i: usize = 0; + while (i < render_commands.length) : (i += 1) { + const render_command = clay.renderCommandArrayGet(render_commands, @intCast(i)); + const bounding_box = render_command.bounding_box; + switch (render_command.command_type) { + .none => {}, + .text => { + ... ``` Please see the [full C documentation for clay](https://github.com/nicbarker/clay/blob/main/README.md) for API details and the example folder in this repo. All public C functions and Macros have Zig binding equivalents, generally of the form `Clay_BeginLayout` (C) -> `clay.beginLayout` (zig) diff --git a/build.zig b/build.zig index 03817e9..95980cc 100644 --- a/build.zig +++ b/build.zig @@ -6,6 +6,8 @@ pub fn build(b: *B) void { const optimize = b.standardOptimizeOption(.{}); const clay_lib = blk: { + var target_clay = target; + target_clay.result.os.tag = .freestanding; const clay_lib = b.addStaticLibrary(.{ .name = "clay", .target = target, @@ -19,6 +21,7 @@ pub fn build(b: *B) void { \\#define CLAY_IMPLEMENTATION \\#include ), + .flags = &.{"-ffreestanding"}, }); break :blk clay_lib; diff --git a/build.zig.zon b/build.zig.zon index 52d2e81..2315824 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,8 +3,8 @@ .version = "0.0.0", .dependencies = .{ .clay = .{ - .url = "https://github.com/nicbarker/clay/archive/afba9f0de668c0b63fd94fbd62adb66485b9bc13.tar.gz", - .hash = "122032ed1ca5afaf896850aae7e1f007bf6cbc1d81bc981ed32ccbe0d932674f25f7", + .url = "https://github.com/nicbarker/clay/archive/7a84facec968df81b9fd7dda10cee60c9bd40beb.tar.gz", + .hash = "12205b854b62ef63f65c19b979782d2332d5cc88bc7069ec158d584ba8d92d389265", }, }, .paths = .{ diff --git a/examples/clay-official-website/src/main.zig b/examples/clay-official-website/src/main.zig index f96cd59..f4f597d 100644 --- a/examples/clay-official-website/src/main.zig +++ b/examples/clay-official-website/src/main.zig @@ -56,69 +56,70 @@ var window_width: isize = 0; var mobile_screen: bool = false; fn landingPageBlob(index: u32, font_size: u16, font_id: u16, color: cl.Color, image_size: f32, max_width: f32, text: []const u8, image: *rl.Texture2D) void { - cl.UI()(&.{ - .IDI("HeroBlob", index), - .layout(.{ .sizing = .{ .w = .growMinMax(.{ .max = max_width }) }, .padding = .all(16), .child_gap = 16, .child_alignment = .{ .y = .CENTER } }), - .border(.outside(color, 2, 10)), + cl.UI()(.{ + .id = .IDI("HeroBlob", index), + .layout = .{ .sizing = .{ .w = .growMinMax(.{ .max = max_width }) }, .padding = .all(16), .child_gap = 16, .child_alignment = .{ .y = .center } }, + .border = .{ .width = .outside(2), .color = color }, + .corner_radius = .all(10), })({ - cl.UI()(&.{ - .IDI("CheckImage", index), - .layout(.{ .sizing = .{ .w = .fixed(image_size) } }), - .image(.{ .image_data = image, .source_dimensions = .{ .w = 128, .h = 128 } }), + cl.UI()(.{ + .id = .IDI("CheckImage", index), + .layout = .{ .sizing = .{ .w = .fixed(image_size) } }, + .image = .{ .image_data = image, .source_dimensions = .{ .w = 128, .h = 128 } }, })({}); cl.text(text, .{ .font_size = font_size, .font_id = font_id, .color = color }); }); } fn landingPageDesktop() void { - cl.UI()(&.{ - .ID("LandingPage1Desktop"), - .layout(.{ + cl.UI()(.{ + .id = .ID("LandingPage1Desktop"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 70) }) }, - .child_alignment = .{ .y = .CENTER }, - .padding = .{ .x = 50 }, + .child_alignment = .{ .y = .center }, + .padding = .{ .left = 50, .right = 50 }, .child_gap = 16, - }), + }, })({ - cl.UI()(&.{ - .ID("LandingPage1"), - .layout(.{ + cl.UI()(.{ + .id = .ID("LandingPage1"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 70) }) }, - .direction = .TOP_TO_BOTTOM, - .child_alignment = .{ .x = .CENTER }, + .direction = .top_to_bottom, + .child_alignment = .{ .x = .center }, .padding = .all(32), .child_gap = 32, - }), - .border(.{ .left = border_data, .right = border_data }), + }, + .border = .{ .width = .{ .left = 2, .right = 2 }, .color = COLOR_RED }, })({ landingPageBlob(0, 30, FONT_ID_BODY_30, COLOR_ZIG_LOGO, 64, 510, "The official Clay website recreated with zclay: clay-zig-bindings", &zig_logo_image6); - cl.UI()(&.{ - .ID("ClayPresentation"), - .layout(.{ + cl.UI()(.{ + .id = .ID("ClayPresentation"), + .layout = .{ .sizing = .grow, - .child_alignment = .{ .y = .CENTER }, + .child_alignment = .{ .y = .center }, .child_gap = 16, - }), + }, })({ - cl.UI()(&.{ - .ID("LeftText"), - .layout(.{ .sizing = .{ .w = .percent(0.55) }, .direction = .TOP_TO_BOTTOM, .child_gap = 8 }), + cl.UI()(.{ + .id = .ID("LeftText"), + .layout = .{ .sizing = .{ .w = .percent(0.55) }, .direction = .top_to_bottom, .child_gap = 8 }, })({ cl.text( "Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.", .{ .font_size = 56, .font_id = FONT_ID_TITLE_56, .color = COLOR_RED }, ); - cl.UI()(&.{.layout(.{ .sizing = .{ .w = .grow, .h = .fixed(32) } })})({}); + cl.UI()(.{ .layout = .{ .sizing = .{ .w = .grow, .h = .fixed(32) } } })({}); cl.text( "Clay is laying out this webpage right now!", .{ .font_size = 36, .font_id = FONT_ID_BODY_36, .color = COLOR_ORANGE }, ); }); - cl.UI()(&.{ - .ID("HeroImageOuter"), - .layout(.{ .sizing = .{ .w = .percent(0.45) }, .direction = .TOP_TO_BOTTOM, .child_alignment = .{ .x = .CENTER }, .child_gap = 16 }), + cl.UI()(.{ + .id = .ID("HeroImageOuter"), + .layout = .{ .sizing = .{ .w = .percent(0.45) }, .direction = .top_to_bottom, .child_alignment = .{ .x = .center }, .child_gap = 16 }, })({ landingPageBlob(1, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_5, 32, 480, "High performance", &checkImage5); landingPageBlob(2, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_4, 32, 480, "Flexbox-style responsive layout", &checkImage4); @@ -132,35 +133,35 @@ fn landingPageDesktop() void { } fn landingPageMobile() void { - cl.UI()(&.{ - .ID("LandingPage1Mobile"), - .layout(.{ + cl.UI()(.{ + .id = .ID("LandingPage1Mobile"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 70) }) }, - .direction = .TOP_TO_BOTTOM, - .child_alignment = .CENTER, - .padding = .{ .x = 16, .y = 32 }, + .direction = .top_to_bottom, + .child_alignment = .center, + .padding = .{ .left = 16, .right = 16, .top = 32, .bottom = 32 }, .child_gap = 16, - }), + }, })({ landingPageBlob(1, 30, FONT_ID_BODY_30, COLOR_ZIG_LOGO, 64, 510, "The official Clay website recreated with zclay: clay-zig-bindings", &zig_logo_image6); - cl.UI()(&.{ - .ID("LeftText"), - .layout(.{ .sizing = .{ .w = .grow }, .direction = .TOP_TO_BOTTOM, .child_gap = 8 }), + cl.UI()(.{ + .id = .ID("LeftText"), + .layout = .{ .sizing = .{ .w = .grow }, .direction = .top_to_bottom, .child_gap = 8 }, })({ cl.text( "Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.", .{ .font_size = 56, .font_id = FONT_ID_TITLE_56, .color = COLOR_RED }, ); - cl.UI()(&.{.layout(.{ .sizing = .{ .w = .grow, .h = .fixed(32) } })})({}); + cl.UI()(.{ .layout = .{ .sizing = .{ .w = .grow, .h = .fixed(32) } } })({}); cl.text( "Clay is laying out this webpage .right now!", .{ .font_size = 36, .font_id = FONT_ID_BODY_36, .color = COLOR_ORANGE }, ); }); - cl.UI()(&.{ - .ID("HeroImageOuter"), - .layout(.{ .sizing = .{ .w = .grow }, .direction = .TOP_TO_BOTTOM, .child_alignment = .{ .x = .CENTER }, .child_gap = 16 }), + cl.UI()(.{ + .id = .ID("HeroImageOuter"), + .layout = .{ .sizing = .{ .w = .grow }, .direction = .top_to_bottom, .child_alignment = .{ .x = .center }, .child_gap = 16 }, })({ landingPageBlob(1, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_5, 32, 480, "High performance", &checkImage5); landingPageBlob(2, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_4, 32, 480, "Flexbox-style responsive layout", &checkImage4); @@ -173,35 +174,36 @@ fn landingPageMobile() void { fn featureBlocks(width_sizing: cl.SizingAxis, outer_padding: u16) void { const text_config = cl.TextElementConfig{ .font_size = 24, .font_id = FONT_ID_BODY_24, .color = COLOR_RED }; - cl.UI()(&.{ - .ID("HFileBoxOuter"), - .layout(.{ - .direction = .TOP_TO_BOTTOM, + cl.UI()(.{ + .id = .ID("HFileBoxOuter"), + .layout = .{ + .direction = .top_to_bottom, .sizing = .{ .w = width_sizing }, - .child_alignment = .{ .y = .CENTER }, - .padding = .{ .x = outer_padding, .y = 32 }, + .child_alignment = .{ .y = .center }, + .padding = .{ .left = outer_padding, .right = outer_padding, .top = 32, .bottom = 32 }, .child_gap = 8, - }), + }, })({ - cl.UI()(&.{ - .ID("HFileIncludeOuter"), - .layout(.{ .padding = .{ .x = 8, .y = 4 } }), - .rectangle(.{ .color = COLOR_RED, .corner_radius = .all(8) }), + cl.UI()(.{ + .id = .ID("HFileIncludeOuter"), + .layout = .{ .padding = .{ .left = 8, .right = 8, .top = 4, .bottom = 4 } }, + .background_color = COLOR_RED, + .corner_radius = .all(8), })({ cl.text("#include cl.h", .{ .font_size = 24, .font_id = FONT_ID_BODY_24, .color = COLOR_LIGHT }); }); cl.text("~2000 lines of C99.", text_config); cl.text("Zero dependencies, including no C standard library", text_config); }); - cl.UI()(&.{ - .ID("BringYourOwnRendererOuter"), - .layout(.{ - .direction = .TOP_TO_BOTTOM, + cl.UI()(.{ + .id = .ID("BringYourOwnRendererOuter"), + .layout = .{ + .direction = .top_to_bottom, .sizing = .{ .w = width_sizing }, - .child_alignment = .{ .y = .CENTER }, - .padding = .{ .x = outer_padding, .y = 32 }, + .child_alignment = .{ .y = .center }, + .padding = .{ .left = outer_padding, .right = outer_padding, .top = 32, .bottom = 32 }, .child_gap = 8, - }), + }, })({ cl.text("Renderer agnostic.", .{ .font_size = 24, .font_id = FONT_ID_BODY_24, .color = COLOR_ORANGE }); cl.text("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML.", text_config); @@ -210,60 +212,60 @@ fn featureBlocks(width_sizing: cl.SizingAxis, outer_padding: u16) void { } fn featureBlocksDesktop() void { - cl.UI()(&.{ - .ID("FeatureBlocksOuter"), - .layout(.{ + cl.UI()(.{ + .id = .ID("FeatureBlocksOuter"), + .layout = .{ .sizing = .{ .w = .grow }, - .child_alignment = .{ .y = .CENTER }, - }), - .border(.{ .between_children = .{ .width = 2, .color = COLOR_RED } }), + .child_alignment = .{ .y = .center }, + }, + .border = .{ .width = .{ .between_children = 2 }, .color = COLOR_RED }, })({ featureBlocks(.percent(0.5), 50); }); } fn featureBlocksMobile() void { - cl.UI()(&.{ - .ID("FeatureBlocksOuter"), - .layout(.{ + cl.UI()(.{ + .id = .ID("FeatureBlocksOuter"), + .layout = .{ .sizing = .{ .w = .grow }, - .direction = .TOP_TO_BOTTOM, - }), - .border(.{ .between_children = .{ .width = 2, .color = COLOR_RED } }), + .direction = .top_to_bottom, + }, + .border = .{ .width = .{ .between_children = 2 }, .color = COLOR_RED }, })({ featureBlocks(.grow, 16); }); } fn declarativeSyntaxPage(title_text_config: cl.TextElementConfig, width_sizing: cl.SizingAxis) void { - cl.UI()(&.{ .ID("SyntaxPageLeftText"), .layout(.{ .sizing = .{ .w = width_sizing }, .direction = .TOP_TO_BOTTOM, .child_gap = 8 }) })({ + cl.UI()(.{ .id = .ID("SyntaxPageLeftText"), .layout = .{ .sizing = .{ .w = width_sizing }, .direction = .top_to_bottom, .child_gap = 8 } })({ cl.text("Declarative Syntax", title_text_config); - cl.UI()(&.{ - .layout(.{ .sizing = .{ .w = .growMinMax(.{ .max = 16 }) } }), + cl.UI()(.{ + .layout = .{ .sizing = .{ .w = .growMinMax(.{ .max = 16 }) } }, })({}); const text_conf = cl.TextElementConfig{ .font_size = 28, .font_id = FONT_ID_BODY_28, .color = COLOR_RED }; cl.text("Flexible and readable declarative syntax with nested UI element hierarchies.", text_conf); cl.text("Mix elements with standard C code like loops, conditionals and functions.", text_conf); cl.text("Create your own library of re-usable components from UI primitives like text, images and rectangles.", text_conf); }); - cl.UI()(&.{ .ID("SyntaxPageRightImageOuter"), .layout(.{ .sizing = .{ .w = width_sizing }, .child_alignment = .{ .x = .CENTER } }) })({ - cl.UI()(&.{ - .ID("SyntaxPageRightImage"), - .layout(.{ .sizing = .{ .w = .growMinMax(.{ .max = 568 }) } }), - .image(.{ .image_data = &syntaxImage, .source_dimensions = .{ .h = 1136, .w = 1194 } }), + cl.UI()(.{ .id = .ID("SyntaxPageRightImageOuter"), .layout = .{ .sizing = .{ .w = width_sizing }, .child_alignment = .{ .x = .center } } })({ + cl.UI()(.{ + .id = .ID("SyntaxPageRightImage"), + .layout = .{ .sizing = .{ .w = .growMinMax(.{ .max = 568 }) } }, + .image = .{ .image_data = &syntaxImage, .source_dimensions = .{ .h = 1136, .w = 1194 } }, })({}); }); } fn declarativeSyntaxPageDesktop() void { - cl.UI()(&.{ - .ID("SyntaxPageDesktop"), - .layout(.{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, .child_alignment = .{ .y = .CENTER }, .padding = .{ .x = 50 } }), + cl.UI()(.{ + .id = .ID("SyntaxPageDesktop"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, .child_alignment = .{ .y = .center }, .padding = .{ .left = 50, .right = 50 } }, })({ - cl.UI()(&.{ - .ID("SyntaxPage"), - .layout(.{ .sizing = .{ .w = .grow, .h = .grow }, .child_alignment = .{ .y = .CENTER }, .padding = .all(32), .child_gap = 32 }), - .border(.{ .left = .{ .width = 2, .color = COLOR_RED }, .right = .{ .width = 2, .color = COLOR_RED } }), + cl.UI()(.{ + .id = .ID("SyntaxPage"), + .layout = .{ .sizing = .{ .w = .grow, .h = .grow }, .child_alignment = .{ .y = .center }, .padding = .all(32), .child_gap = 32 }, + .border = .{ .width = .{ .left = 2, .right = 2 }, .color = COLOR_RED }, })({ declarativeSyntaxPage(.{ .font_size = 52, .font_id = FONT_ID_TITLE_52, .color = COLOR_RED }, .percent(0.5)); }); @@ -271,15 +273,15 @@ fn declarativeSyntaxPageDesktop() void { } fn declarativeSyntaxPageMobile() void { - cl.UI()(&.{ - .ID("SyntaxPageMobile"), - .layout(.{ - .direction = .TOP_TO_BOTTOM, + cl.UI()(.{ + .id = .ID("SyntaxPageMobile"), + .layout = .{ + .direction = .top_to_bottom, .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, - .child_alignment = .CENTER, - .padding = .{ .x = 16, .y = 32 }, + .child_alignment = .center, + .padding = .{ .left = 16, .right = 16, .top = 32, .bottom = 32 }, .child_gap = 16, - }), + }, })({ declarativeSyntaxPage(.{ .font_size = 48, .font_id = FONT_ID_TITLE_48, .color = COLOR_RED }, .grow); }); @@ -292,9 +294,9 @@ fn colorLerp(a: cl.Color, b: cl.Color, amount: f32) cl.Color { const LOREM_IPSUM_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; fn highPerformancePage(lerp_value: f32, title_text_tonfig: cl.TextElementConfig, width_sizing: cl.SizingAxis) void { - cl.UI()(&.{ .ID("PerformanceLeftText"), .layout(.{ .sizing = .{ .w = width_sizing }, .direction = .TOP_TO_BOTTOM, .child_gap = 8 }) })({ + cl.UI()(.{ .id = .ID("PerformanceLeftText"), .layout = .{ .sizing = .{ .w = width_sizing }, .direction = .top_to_bottom, .child_gap = 8 } })({ cl.text("High Performance", title_text_tonfig); - cl.UI()(&.{.layout(.{ .sizing = .{ .w = .growMinMax(.{ .max = 16 }) } })})({}); + cl.UI()(.{ .layout = .{ .sizing = .{ .w = .growMinMax(.{ .max = 16 }) } } })({}); cl.text( "Fast enough to recompute your entire UI every frame.", .{ .font_size = 28, .font_id = FONT_ID_BODY_36, .color = COLOR_LIGHT }, @@ -308,23 +310,23 @@ fn highPerformancePage(lerp_value: f32, title_text_tonfig: cl.TextElementConfig, .{ .font_size = 28, .font_id = FONT_ID_BODY_36, .color = COLOR_LIGHT }, ); }); - cl.UI()(&.{ .ID("PerformanceRightImageOuter"), .layout(.{ .sizing = .{ .w = width_sizing }, .child_alignment = .{ .x = .CENTER } }) })({ - cl.UI()(&.{ - .ID("PerformanceRightBorder"), - .layout(.{ .sizing = .{ .w = .grow, .h = .fixed(400) } }), - .border(.all(COLOR_LIGHT, 2, 0)), + cl.UI()(.{ .id = .ID("PerformanceRightImageOuter"), .layout = .{ .sizing = .{ .w = width_sizing }, .child_alignment = .{ .x = .center } } })({ + cl.UI()(.{ + .id = .ID("PerformanceRightBorder"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fixed(400) } }, + .border = .{ .width = .all(2), .color = COLOR_LIGHT }, })({ - cl.UI()(&.{ - .ID("AnimationDemoContainerLeft"), - .layout(.{ .sizing = .{ .w = .percent(0.35 + 0.3 * lerp_value), .h = .grow }, .child_alignment = .{ .y = .CENTER }, .padding = .all(16) }), - .rectangle(.{ .color = colorLerp(COLOR_RED, COLOR_ORANGE, lerp_value) }), + cl.UI()(.{ + .id = .ID("AnimationDemoContainerLeft"), + .layout = .{ .sizing = .{ .w = .percent(0.35 + 0.3 * lerp_value), .h = .grow }, .child_alignment = .{ .y = .center }, .padding = .all(16) }, + .background_color = colorLerp(COLOR_RED, COLOR_ORANGE, lerp_value), })({ cl.text(LOREM_IPSUM_TEXT, .{ .font_size = 16, .font_id = FONT_ID_BODY_16, .color = COLOR_LIGHT }); }); - cl.UI()(&.{ - .ID("AnimationDemoContainerRight"), - .layout(.{ .sizing = .{ .w = .grow, .h = .grow }, .child_alignment = .{ .y = .CENTER }, .padding = .all(16) }), - .rectangle(.{ .color = colorLerp(COLOR_ORANGE, COLOR_RED, lerp_value) }), + cl.UI()(.{ + .id = .ID("AnimationDemoContainerRight"), + .layout = .{ .sizing = .{ .w = .grow, .h = .grow }, .child_alignment = .{ .y = .center }, .padding = .all(16) }, + .background_color = colorLerp(COLOR_ORANGE, COLOR_RED, lerp_value), })({ cl.text(LOREM_IPSUM_TEXT, .{ .font_size = 16, .font_id = FONT_ID_BODY_16, .color = COLOR_LIGHT }); }); @@ -333,53 +335,53 @@ fn highPerformancePage(lerp_value: f32, title_text_tonfig: cl.TextElementConfig, } fn highPerformancePageDesktop(lerp_value: f32) void { - cl.UI()(&.{ - .ID("PerformanceDesktop"), - .layout(.{ + cl.UI()(.{ + .id = .ID("PerformanceDesktop"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, - .child_alignment = .{ .y = .CENTER }, - .padding = .{ .x = 82, .y = 32 }, + .child_alignment = .{ .y = .center }, + .padding = .{ .left = 82, .right = 82, .top = 32, .bottom = 32 }, .child_gap = 64, - }), - .rectangle(.{ .color = COLOR_RED }), + }, + .background_color = COLOR_RED, })({ highPerformancePage(lerp_value, .{ .font_size = 52, .font_id = FONT_ID_TITLE_52, .color = COLOR_LIGHT }, .percent(0.5)); }); } fn highPerformancePageMobile(lerp_value: f32) void { - cl.UI()(&.{ - .ID("PerformanceMobile"), - .layout( - .{ - .direction = .TOP_TO_BOTTOM, - .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, - .child_alignment = .CENTER, - .padding = .{ .x = 16, .y = 32 }, - .child_gap = 32, - }, - ), - .rectangle(.{ .color = COLOR_RED }), + cl.UI()(.{ + .id = .ID("PerformanceMobile"), + .layout = .{ + .direction = .top_to_bottom, + .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, + .child_alignment = .center, + .padding = .{ .left = 16, .right = 16, .top = 32, .bottom = 32 }, + .child_gap = 32, + }, + .background_color = COLOR_RED, })({ highPerformancePage(lerp_value, .{ .font_size = 48, .font_id = FONT_ID_TITLE_48, .color = COLOR_LIGHT }, .grow); }); } fn rendererButtonActive(text: []const u8) void { - cl.UI()(&.{ - .layout(.{ .sizing = .{ .w = .fixed(300) }, .padding = .all(16) }), - .rectangle(.{ .color = COLOR_RED, .corner_radius = .all(10) }), + cl.UI()(.{ + .layout = .{ .sizing = .{ .w = .fixed(300) }, .padding = .all(16) }, + .background_color = COLOR_RED, + .corner_radius = .all(10), })({ cl.text(text, .{ .font_size = 28, .font_id = FONT_ID_BODY_28, .color = COLOR_LIGHT }); }); } fn rendererButtonInactive(index: u32, text: []const u8) void { - cl.UI()(&.{ .layout(.{}), .outside(.{ 2, COLOR_RED }, 10) })({ - cl.UI()(&.{ - .ID("RendererButtonInactiveInner", index), - .layout(.{ .sizing = .{ .w = .fixed(300) }, .padding = .all(16) }), - .rectangle(.{ .color = COLOR_LIGHT, .corner_radius = .all(10) }), + cl.UI()(.{ .layout = .{}, .border = .outside(.{ 2, COLOR_RED }, 10) })({ + cl.UI()(.{ + .id = .ID("RendererButtonInactiveInner", index), + .layout = .{ .sizing = .{ .w = .fixed(300) }, .padding = .all(16) }, + .background_color = COLOR_LIGHT, + .corner_radius = .all(10), })({ cl.text(text, .{ .font_size = 28, .font_id = FONT_ID_BODY_28, .color = COLOR_RED }); }); @@ -387,9 +389,9 @@ fn rendererButtonInactive(index: u32, text: []const u8) void { } fn rendererPage(title_text_config: cl.TextElementConfig, width_sizing: cl.SizingAxis) void { - cl.UI()(&.{ .ID("RendererLeftText"), .layout(.{ .sizing = .{ .w = width_sizing }, .direction = .TOP_TO_BOTTOM, .child_gap = 8 }) })({ + cl.UI()(.{ .id = .ID("RendererLeftText"), .layout = .{ .sizing = .{ .w = width_sizing }, .direction = .top_to_bottom, .child_gap = 8 } })({ cl.text("Renderer & Platform Agnostic", title_text_config); - cl.UI()(&.{.layout(.{ .sizing = .{ .w = .growMinMax(.{ .max = 16 }) } })})({}); + cl.UI()(.{ .layout = .{ .sizing = .{ .w = .growMinMax(.{ .max = 16 }) } } })({}); cl.text( "Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE.", .{ .font_size = 28, .font_id = FONT_ID_BODY_36, .color = COLOR_RED }, @@ -403,29 +405,29 @@ fn rendererPage(title_text_config: cl.TextElementConfig, width_sizing: cl.Sizing .{ .font_size = 28, .font_id = FONT_ID_BODY_36, .color = COLOR_RED }, ); }); - cl.UI()(&.{ - .ID("RendererRightText"), - .layout(.{ .sizing = .{ .w = width_sizing }, .child_alignment = .{ .x = .CENTER }, .direction = .TOP_TO_BOTTOM, .child_gap = 16 }), + cl.UI()(.{ + .id = .ID("RendererRightText"), + .layout = .{ .sizing = .{ .w = width_sizing }, .child_alignment = .{ .x = .center }, .direction = .top_to_bottom, .child_gap = 16 }, })({ cl.text("Try changing renderer!", .{ .font_size = 36, .font_id = FONT_ID_BODY_36, .color = COLOR_ORANGE }); - cl.UI()(&.{.layout(.{ .sizing = .{ .w = .growMinMax(.{ .max = 32 }) } })})({}); + cl.UI()(.{ .layout = .{ .sizing = .{ .w = .growMinMax(.{ .max = 32 }) } } })({}); rendererButtonActive("Raylib Renderer"); }); } fn rendererPageDesktop() void { - cl.UI()(&.{ - .ID("RendererPageDesktop"), - .layout(.{ + cl.UI()(.{ + .id = .ID("RendererPageDesktop"), + .layout = .{ .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, - .child_alignment = .{ .y = .CENTER }, - .padding = .{ .x = 50 }, - }), + .child_alignment = .{ .y = .center }, + .padding = .{ .left = 50, .right = 50 }, + }, })({ - cl.UI()(&.{ - .ID("RendererPage"), - .layout(.{ .sizing = .grow, .child_alignment = .{ .y = .CENTER }, .padding = .all(32), .child_gap = 32 }), - .border(.{ .left = .{ .width = 2, .color = COLOR_RED }, .right = .{ .width = 2, .color = COLOR_RED } }), + cl.UI()(.{ + .id = .ID("RendererPage"), + .layout = .{ .sizing = .grow, .child_alignment = .{ .y = .center }, .padding = .all(32), .child_gap = 32 }, + .border = .{ .width = .{ .left = 2, .right = 2 }, .color = COLOR_RED }, })({ rendererPage(.{ .font_size = 52, .font_id = FONT_ID_TITLE_52, .color = COLOR_RED }, .percent(0.5)); }); @@ -433,18 +435,16 @@ fn rendererPageDesktop() void { } fn rendererPageMobile() void { - cl.UI()(&.{ - .ID("RendererMobile"), - .layout( - .{ - .direction = .TOP_TO_BOTTOM, - .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, - .child_alignment = .CENTER, - .padding = .{ .x = 16, .y = 32 }, - .child_gap = 32, - }, - ), - .rectangle(.{ .color = COLOR_LIGHT }), + cl.UI()(.{ + .id = .ID("RendererMobile"), + .layout = .{ + .direction = .top_to_bottom, + .sizing = .{ .w = .grow, .h = .fitMinMax(.{ .min = @floatFromInt(window_height - 50) }) }, + .child_alignment = .center, + .padding = .{ .left = 16, .right = 16, .top = 32, .bottom = 32 }, + .child_gap = 32, + }, + .background_color = COLOR_LIGHT, })({ rendererPage(.{ .font_size = 52, .font_id = FONT_ID_TITLE_52, .color = COLOR_RED }, .grow); }); @@ -452,43 +452,41 @@ fn rendererPageMobile() void { fn createLayout(lerp_value: f32) cl.ClayArray(cl.RenderCommand) { cl.beginLayout(); - cl.UI()(&.{ - .ID("OuterContainer"), - .layout(.{ .sizing = .grow, .direction = .TOP_TO_BOTTOM }), - .rectangle(.{ .color = COLOR_LIGHT }), + cl.UI()(.{ + .id = .ID("OuterContainer"), + .layout = .{ .sizing = .grow, .direction = .top_to_bottom }, + .background_color = COLOR_LIGHT, })({ - cl.UI()(&.{ - .ID("Header"), - .layout(.{ + cl.UI()(.{ + .id = .ID("Header"), + .layout = .{ .sizing = .{ .h = .fixed(50), .w = .grow }, - .child_alignment = .{ .y = .CENTER }, - .padding = .{ .x = 32 }, + .child_alignment = .{ .y = .center }, + .padding = .{ .left = 32, .right = 32 }, .child_gap = 24, - }), + }, })({ cl.text("Clay", .{ .font_id = FONT_ID_BODY_24, .font_size = 24, .color = .{ 61, 26, 5, 255 }, }); - cl.UI()(&.{.layout(.{ .sizing = .{ .w = .grow } })})({}); + cl.UI()(.{ .layout = .{ .sizing = .{ .w = .grow } } })({}); if (!mobile_screen) { - cl.UI()(&.{ .ID("LinkExamplesInner"), .layout(.{}), .rectangle(.{ .color = .{ 0, 0, 0, 0 } }) })({ + cl.UI()(.{ .id = .ID("LinkExamplesInner"), .layout = .{}, .background_color = .{ 0, 0, 0, 0 } })({ cl.text("Examples", .{ .font_id = FONT_ID_BODY_24, .font_size = 24, .color = .{ 61, 26, 5, 255 } }); }); - cl.UI()(&.{ .ID("LinkDocsOuter"), .layout(.{}), .rectangle(.{ .color = .{ 0, 0, 0, 0 } }) })({ + cl.UI()(.{ .id = .ID("LinkDocsOuter"), .layout = .{}, .background_color = .{ 0, 0, 0, 0 } })({ cl.text("Docs", .{ .font_id = FONT_ID_BODY_24, .font_size = 24, .color = .{ 61, 26, 5, 255 } }); }); } - cl.UI()(&.{ - .layout(.{ .padding = .{ .x = 32, .y = 6 } }), - .border(.outside(COLOR_RED, 2, 10)), - .rectangle(.{ - .corner_radius = .all(10), - .color = if (cl.hovered()) COLOR_LIGHT_HOVER else COLOR_LIGHT, - }), + cl.UI()(.{ + .layout = .{ .padding = .{ .left = 32, .right = 32, .top = 6, .bottom = 6 } }, + .border = .{ .width = .all(2), .color = COLOR_RED }, + .corner_radius = .all(10), + .background_color = if (cl.hovered()) COLOR_LIGHT_HOVER else COLOR_LIGHT, })({ cl.text( "Github", @@ -497,19 +495,19 @@ fn createLayout(lerp_value: f32) cl.ClayArray(cl.RenderCommand) { }); }); inline for (COLORS_TOP_BORDER, 0..) |color, i| { - cl.UI()(&.{ - .ID("TopBorder" ++ .{i}), - .layout(.{ .sizing = .{ .h = .fixed(4), .w = .grow } }), - .rectangle(.{ .color = color }), + cl.UI()(.{ + .id = .ID("TopBorder" ++ .{i}), + .layout = .{ .sizing = .{ .h = .fixed(4), .w = .grow } }, + .background_color = color, })({}); } - cl.UI()(&.{ - .ID("ScrollContainerBackgroundRectangle"), - .scroll(.{ .vertical = true }), - .layout(.{ .sizing = .grow, .direction = .TOP_TO_BOTTOM }), - .rectangle(.{ .color = COLOR_LIGHT }), - .border(.{ .between_children = .{ .width = 2, .color = COLOR_RED } }), + cl.UI()(.{ + .id = .ID("ScrollContainerBackgroundRectangle"), + .scroll = .{ .vertical = true }, + .layout = .{ .sizing = .grow, .direction = .top_to_bottom }, + .background_color = COLOR_LIGHT, + .border = .{ .width = .{ .between_children = 2 }, .color = COLOR_RED }, })({ if (!mobile_screen) { landingPageDesktop(); @@ -549,7 +547,7 @@ pub fn main() anyerror!void { defer allocator.free(memory); const arena: cl.Arena = cl.createArenaWithCapacityAndMemory(memory); _ = cl.initialize(arena, .{ .h = 1000, .w = 1000 }, .{}); - cl.setMeasureTextFunction(renderer.measureText); + cl.setMeasureTextFunction({}, renderer.measureText); // init raylib rl.setConfigFlags(.{ diff --git a/examples/clay-official-website/src/raylib_render_clay.zig b/examples/clay-official-website/src/raylib_render_clay.zig index 8e60616..160efbe 100644 --- a/examples/clay-official-website/src/raylib_render_clay.zig +++ b/examples/clay-official-website/src/raylib_render_clay.zig @@ -22,23 +22,27 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca switch (render_command.command_type) { .none => {}, .text => { - const text = render_command.text.chars[0..@intCast(render_command.text.length)]; + const config = render_command.render_data.text; + const text = config.string_contents.chars[0..@intCast(config.string_contents.length)]; + // Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator const cloned = allocator.dupeZ(u8, text) catch unreachable; defer allocator.free(cloned); - const fontToUse: rl.Font = raylib_fonts[render_command.config.text_config.font_id].?; - rl.setTextLineSpacing(render_command.config.text_config.line_height); + const fontToUse: rl.Font = raylib_fonts[config.font_id].?; + rl.setTextLineSpacing(config.line_height); rl.drawTextEx( fontToUse, @ptrCast(@alignCast(cloned.ptr)), rl.Vector2{ .x = bounding_box.x, .y = bounding_box.y }, - @floatFromInt(render_command.config.text_config.font_size), - @floatFromInt(render_command.config.text_config.letter_spacing), - clayColorToRaylibColor(render_command.config.text_config.color), + @floatFromInt(config.font_size), + @floatFromInt(config.letter_spacing), + clayColorToRaylibColor(config.text_color), ); }, .image => { + const config = render_command.render_data.image; + const image_texture: *const rl.Texture2D = @ptrCast( - @alignCast(render_command.config.image_config.image_data), + @alignCast(config.image_data), ); rl.drawTextureEx( image_texture.*, @@ -50,15 +54,15 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca }, .scissor_start => { rl.beginScissorMode( - @intFromFloat(math.round(bounding_box.x)), - @intFromFloat(math.round(bounding_box.y)), - @intFromFloat(math.round(bounding_box.width)), - @intFromFloat(math.round(bounding_box.height)), + @intFromFloat(@round(bounding_box.x)), + @intFromFloat(@round(bounding_box.y)), + @intFromFloat(@round(bounding_box.width)), + @intFromFloat(@round(bounding_box.height)), ); }, .scissor_end => rl.endScissorMode(), .rectangle => { - const config = render_command.config.rectangle_config; + const config = render_command.render_data.rectangle; if (config.corner_radius.top_left > 0) { const radius: f32 = (config.corner_radius.top_left * 2) / @min(bounding_box.width, bounding_box.height); rl.drawRectangleRounded( @@ -70,7 +74,7 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca }, radius, 8, - clayColorToRaylibColor(config.color), + clayColorToRaylibColor(config.background_color), ); } else { rl.drawRectangle( @@ -78,103 +82,106 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca @intFromFloat(bounding_box.y), @intFromFloat(bounding_box.width), @intFromFloat(bounding_box.height), - clayColorToRaylibColor(config.color), + clayColorToRaylibColor(config.background_color), ); } }, .border => { - const config = render_command.config.border_config; - if (config.left.width > 0) { + const config = render_command.render_data.border; + // Left border + if (config.width.left > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x)), - @intFromFloat(math.round(bounding_box.y + config.corner_radius.top_left)), - @intCast(config.left.width), - @intFromFloat(math.round(bounding_box.height - config.corner_radius.top_left - config.corner_radius.bottom_left)), - clayColorToRaylibColor(config.left.color), + @intFromFloat(@round(bounding_box.x)), + @intFromFloat(@round(bounding_box.y + config.corner_radius.top_left)), + @intCast(config.width.left), + @intFromFloat(@round(bounding_box.height - config.corner_radius.top_left - config.corner_radius.bottom_left)), + clayColorToRaylibColor(config.color), ); } - if (config.right.width > 0) { + // Right border + if (config.width.right > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x + bounding_box.width - @as(f32, @floatFromInt(config.right.width)))), - @intFromFloat(math.round(bounding_box.y + config.corner_radius.top_right)), - @intCast(config.right.width), - @intFromFloat(math.round(bounding_box.height - config.corner_radius.top_right - config.corner_radius.bottom_right)), - clayColorToRaylibColor(config.right.color), + @intFromFloat(@round(bounding_box.x + bounding_box.width - @as(f32, @floatFromInt(config.width.right)))), + @intFromFloat(@round(bounding_box.y + config.corner_radius.top_right)), + @intCast(config.width.right), + @intFromFloat(@round(bounding_box.height - config.corner_radius.top_right - config.corner_radius.bottom_right)), + clayColorToRaylibColor(config.color), ); } - if (config.top.width > 0) { + // Top border + if (config.width.top > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x + config.corner_radius.top_left)), - @intFromFloat(math.round(bounding_box.y)), - @intFromFloat(math.round(bounding_box.width - config.corner_radius.top_left - config.corner_radius.top_right)), - @intCast(config.top.width), - clayColorToRaylibColor(config.top.color), + @intFromFloat(@round(bounding_box.x + config.corner_radius.top_left)), + @intFromFloat(@round(bounding_box.y)), + @intFromFloat(@round(bounding_box.width - config.corner_radius.top_left - config.corner_radius.top_right)), + @intCast(config.width.top), + clayColorToRaylibColor(config.color), ); } - if (config.bottom.width > 0) { + // Bottom border + if (config.width.bottom > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x + config.corner_radius.bottom_left)), - @intFromFloat(math.round(bounding_box.y + bounding_box.height - @as(f32, @floatFromInt(config.bottom.width)))), - @intFromFloat(math.round(bounding_box.width - config.corner_radius.bottom_left - config.corner_radius.bottom_right)), - @intCast(config.bottom.width), - clayColorToRaylibColor(config.bottom.color), + @intFromFloat(@round(bounding_box.x + config.corner_radius.bottom_left)), + @intFromFloat(@round(bounding_box.y + bounding_box.height - @as(f32, @floatFromInt(config.width.bottom)))), + @intFromFloat(@round(bounding_box.width - config.corner_radius.bottom_left - config.corner_radius.bottom_right)), + @intCast(config.width.bottom), + clayColorToRaylibColor(config.color), ); } - if (config.corner_radius.top_left > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + config.corner_radius.top_left), - .y = math.round(bounding_box.y + config.corner_radius.top_left), + .x = @round(bounding_box.x + config.corner_radius.top_left), + .y = @round(bounding_box.y + config.corner_radius.top_left), }, - math.round(config.corner_radius.top_left - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.top_left - @as(f32, @floatFromInt(config.width.top))), config.corner_radius.top_left, 180, 270, 10, - clayColorToRaylibColor(config.top.color), + clayColorToRaylibColor(config.color), ); } if (config.corner_radius.top_right > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + bounding_box.width - config.corner_radius.top_right), - .y = math.round(bounding_box.y + config.corner_radius.top_right), + .x = @round(bounding_box.x + bounding_box.width - config.corner_radius.top_right), + .y = @round(bounding_box.y + config.corner_radius.top_right), }, - math.round(config.corner_radius.top_right - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.top_right - @as(f32, @floatFromInt(config.width.top))), config.corner_radius.top_right, 270, 360, 10, - clayColorToRaylibColor(config.top.color), + clayColorToRaylibColor(config.color), ); } if (config.corner_radius.bottom_left > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + config.corner_radius.bottom_left), - .y = math.round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_left), + .x = @round(bounding_box.x + config.corner_radius.bottom_left), + .y = @round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_left), }, - math.round(config.corner_radius.bottom_left - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.bottom_left - @as(f32, @floatFromInt(config.width.top))), config.corner_radius.bottom_left, 90, 180, 10, - clayColorToRaylibColor(config.bottom.color), + clayColorToRaylibColor(config.color), ); } if (config.corner_radius.bottom_right > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + bounding_box.width - config.corner_radius.bottom_right), - .y = math.round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_right), + .x = @round(bounding_box.x + bounding_box.width - config.corner_radius.bottom_right), + .y = @round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_right), }, - math.round(config.corner_radius.bottom_right - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.bottom_right - @as(f32, @floatFromInt(config.width.bottom))), config.corner_radius.bottom_right, 0.1, 90, 10, - clayColorToRaylibColor(config.bottom.color), + clayColorToRaylibColor(config.color), ); } }, @@ -185,7 +192,7 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca } } -pub fn measureText(clay_text: []const u8, config: *cl.TextElementConfig) cl.Dimensions { +pub fn measureText(clay_text: []const u8, config: *cl.TextElementConfig, _: void) cl.Dimensions { const font = raylib_fonts[config.font_id].?; const text: []const u8 = clay_text; const font_size: f32 = @floatFromInt(config.font_size); diff --git a/examples/raylib-sidebar-container/src/main.zig b/examples/raylib-sidebar-container/src/main.zig index f20d5eb..0002027 100644 --- a/examples/raylib-sidebar-container/src/main.zig +++ b/examples/raylib-sidebar-container/src/main.zig @@ -11,53 +11,53 @@ const white: cl.Color = .{ 250, 250, 255, 255 }; const sidebar_item_layout: cl.LayoutConfig = .{ .sizing = .{ .w = .grow, .h = .fixed(50) } }; // Re-useable components are just normal functions -fn sidebarItemComponent(index: usize) void { - cl.UI()(&.{ - .IDI("SidebarBlob", @intCast(index)), - .layout(sidebar_item_layout), - .rectangle(.{ .color = orange }), +fn sidebarItemComponent(index: u32) void { + cl.UI()(.{ + .id = .IDI("SidebarBlob", index), + .layout = sidebar_item_layout, + .background_color = orange, })({}); } // An example function to begin the "root" of your layout tree fn createLayout(profile_picture: *const rl.Texture2D) cl.ClayArray(cl.RenderCommand) { cl.beginLayout(); - cl.UI()(&.{ - .ID("OuterContainer"), - .layout(.{ .direction = .LEFT_TO_RIGHT, .sizing = .grow, .padding = .all(16), .child_gap = 16 }), - .rectangle(.{ .color = white }), + cl.UI()(.{ + .id = .ID("OuterContainer"), + .layout = .{ .direction = .left_to_right, .sizing = .grow, .padding = .all(16), .child_gap = 16 }, + .background_color = white, })({ - cl.UI()(&.{ - .ID("SideBar"), - .layout(.{ - .direction = .TOP_TO_BOTTOM, + cl.UI()(.{ + .id = .ID("SideBar"), + .layout = .{ + .direction = .top_to_bottom, .sizing = .{ .h = .grow, .w = .fixed(300) }, .padding = .all(16), - .child_alignment = .{ .x = .CENTER, .y = .TOP }, + .child_alignment = .{ .x = .center, .y = .top }, .child_gap = 16, - }), - .rectangle(.{ .color = light_grey }), + }, + .background_color = light_grey, })({ - cl.UI()(&.{ - .ID("ProfilePictureOuter"), - .layout(.{ .sizing = .{ .w = .grow }, .padding = .all(16), .child_alignment = .{ .x = .LEFT, .y = .CENTER }, .child_gap = 16 }), - .rectangle(.{ .color = red }), + cl.UI()(.{ + .id = .ID("ProfilePictureOuter"), + .layout = .{ .sizing = .{ .w = .grow }, .padding = .all(16), .child_alignment = .{ .x = .left, .y = .center }, .child_gap = 16 }, + .background_color = red, })({ - cl.UI()(&.{ - .ID("ProfilePicture"), - .layout(.{ .sizing = .{ .h = .fixed(60), .w = .fixed(60) } }), - .image(.{ .source_dimensions = .{ .h = 60, .w = 60 }, .image_data = @ptrCast(profile_picture) }), + cl.UI()(.{ + .id = .ID("ProfilePicture"), + .layout = .{ .sizing = .{ .h = .fixed(60), .w = .fixed(60) } }, + .image = .{ .source_dimensions = .{ .h = 60, .w = 60 }, .image_data = @ptrCast(profile_picture) }, })({}); cl.text("Clay - UI Library", .{ .font_size = 24, .color = light_grey }); }); - for (0..5) |i| sidebarItemComponent(i); + for (0..5) |i| sidebarItemComponent(@intCast(i)); }); - cl.UI()(&.{ - .ID("MainContent"), - .layout(.{ .sizing = .grow }), - .rectangle(.{ .color = light_grey }), + cl.UI()(.{ + .id = .ID("MainContent"), + .layout = .{ .sizing = .grow }, + .background_color = light_grey, })({ //... }); @@ -85,7 +85,7 @@ pub fn main() anyerror!void { defer allocator.free(memory); const arena: cl.Arena = cl.createArenaWithCapacityAndMemory(memory); _ = cl.initialize(arena, .{ .h = 1000, .w = 1000 }, .{}); - cl.setMeasureTextFunction(renderer.measureText); + cl.setMeasureTextFunction({}, renderer.measureText); // init raylib rl.setConfigFlags(.{ diff --git a/examples/raylib-sidebar-container/src/raylib_render_clay.zig b/examples/raylib-sidebar-container/src/raylib_render_clay.zig index 8e60616..160efbe 100644 --- a/examples/raylib-sidebar-container/src/raylib_render_clay.zig +++ b/examples/raylib-sidebar-container/src/raylib_render_clay.zig @@ -22,23 +22,27 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca switch (render_command.command_type) { .none => {}, .text => { - const text = render_command.text.chars[0..@intCast(render_command.text.length)]; + const config = render_command.render_data.text; + const text = config.string_contents.chars[0..@intCast(config.string_contents.length)]; + // Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator const cloned = allocator.dupeZ(u8, text) catch unreachable; defer allocator.free(cloned); - const fontToUse: rl.Font = raylib_fonts[render_command.config.text_config.font_id].?; - rl.setTextLineSpacing(render_command.config.text_config.line_height); + const fontToUse: rl.Font = raylib_fonts[config.font_id].?; + rl.setTextLineSpacing(config.line_height); rl.drawTextEx( fontToUse, @ptrCast(@alignCast(cloned.ptr)), rl.Vector2{ .x = bounding_box.x, .y = bounding_box.y }, - @floatFromInt(render_command.config.text_config.font_size), - @floatFromInt(render_command.config.text_config.letter_spacing), - clayColorToRaylibColor(render_command.config.text_config.color), + @floatFromInt(config.font_size), + @floatFromInt(config.letter_spacing), + clayColorToRaylibColor(config.text_color), ); }, .image => { + const config = render_command.render_data.image; + const image_texture: *const rl.Texture2D = @ptrCast( - @alignCast(render_command.config.image_config.image_data), + @alignCast(config.image_data), ); rl.drawTextureEx( image_texture.*, @@ -50,15 +54,15 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca }, .scissor_start => { rl.beginScissorMode( - @intFromFloat(math.round(bounding_box.x)), - @intFromFloat(math.round(bounding_box.y)), - @intFromFloat(math.round(bounding_box.width)), - @intFromFloat(math.round(bounding_box.height)), + @intFromFloat(@round(bounding_box.x)), + @intFromFloat(@round(bounding_box.y)), + @intFromFloat(@round(bounding_box.width)), + @intFromFloat(@round(bounding_box.height)), ); }, .scissor_end => rl.endScissorMode(), .rectangle => { - const config = render_command.config.rectangle_config; + const config = render_command.render_data.rectangle; if (config.corner_radius.top_left > 0) { const radius: f32 = (config.corner_radius.top_left * 2) / @min(bounding_box.width, bounding_box.height); rl.drawRectangleRounded( @@ -70,7 +74,7 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca }, radius, 8, - clayColorToRaylibColor(config.color), + clayColorToRaylibColor(config.background_color), ); } else { rl.drawRectangle( @@ -78,103 +82,106 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca @intFromFloat(bounding_box.y), @intFromFloat(bounding_box.width), @intFromFloat(bounding_box.height), - clayColorToRaylibColor(config.color), + clayColorToRaylibColor(config.background_color), ); } }, .border => { - const config = render_command.config.border_config; - if (config.left.width > 0) { + const config = render_command.render_data.border; + // Left border + if (config.width.left > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x)), - @intFromFloat(math.round(bounding_box.y + config.corner_radius.top_left)), - @intCast(config.left.width), - @intFromFloat(math.round(bounding_box.height - config.corner_radius.top_left - config.corner_radius.bottom_left)), - clayColorToRaylibColor(config.left.color), + @intFromFloat(@round(bounding_box.x)), + @intFromFloat(@round(bounding_box.y + config.corner_radius.top_left)), + @intCast(config.width.left), + @intFromFloat(@round(bounding_box.height - config.corner_radius.top_left - config.corner_radius.bottom_left)), + clayColorToRaylibColor(config.color), ); } - if (config.right.width > 0) { + // Right border + if (config.width.right > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x + bounding_box.width - @as(f32, @floatFromInt(config.right.width)))), - @intFromFloat(math.round(bounding_box.y + config.corner_radius.top_right)), - @intCast(config.right.width), - @intFromFloat(math.round(bounding_box.height - config.corner_radius.top_right - config.corner_radius.bottom_right)), - clayColorToRaylibColor(config.right.color), + @intFromFloat(@round(bounding_box.x + bounding_box.width - @as(f32, @floatFromInt(config.width.right)))), + @intFromFloat(@round(bounding_box.y + config.corner_radius.top_right)), + @intCast(config.width.right), + @intFromFloat(@round(bounding_box.height - config.corner_radius.top_right - config.corner_radius.bottom_right)), + clayColorToRaylibColor(config.color), ); } - if (config.top.width > 0) { + // Top border + if (config.width.top > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x + config.corner_radius.top_left)), - @intFromFloat(math.round(bounding_box.y)), - @intFromFloat(math.round(bounding_box.width - config.corner_radius.top_left - config.corner_radius.top_right)), - @intCast(config.top.width), - clayColorToRaylibColor(config.top.color), + @intFromFloat(@round(bounding_box.x + config.corner_radius.top_left)), + @intFromFloat(@round(bounding_box.y)), + @intFromFloat(@round(bounding_box.width - config.corner_radius.top_left - config.corner_radius.top_right)), + @intCast(config.width.top), + clayColorToRaylibColor(config.color), ); } - if (config.bottom.width > 0) { + // Bottom border + if (config.width.bottom > 0) { rl.drawRectangle( - @intFromFloat(math.round(bounding_box.x + config.corner_radius.bottom_left)), - @intFromFloat(math.round(bounding_box.y + bounding_box.height - @as(f32, @floatFromInt(config.bottom.width)))), - @intFromFloat(math.round(bounding_box.width - config.corner_radius.bottom_left - config.corner_radius.bottom_right)), - @intCast(config.bottom.width), - clayColorToRaylibColor(config.bottom.color), + @intFromFloat(@round(bounding_box.x + config.corner_radius.bottom_left)), + @intFromFloat(@round(bounding_box.y + bounding_box.height - @as(f32, @floatFromInt(config.width.bottom)))), + @intFromFloat(@round(bounding_box.width - config.corner_radius.bottom_left - config.corner_radius.bottom_right)), + @intCast(config.width.bottom), + clayColorToRaylibColor(config.color), ); } - if (config.corner_radius.top_left > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + config.corner_radius.top_left), - .y = math.round(bounding_box.y + config.corner_radius.top_left), + .x = @round(bounding_box.x + config.corner_radius.top_left), + .y = @round(bounding_box.y + config.corner_radius.top_left), }, - math.round(config.corner_radius.top_left - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.top_left - @as(f32, @floatFromInt(config.width.top))), config.corner_radius.top_left, 180, 270, 10, - clayColorToRaylibColor(config.top.color), + clayColorToRaylibColor(config.color), ); } if (config.corner_radius.top_right > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + bounding_box.width - config.corner_radius.top_right), - .y = math.round(bounding_box.y + config.corner_radius.top_right), + .x = @round(bounding_box.x + bounding_box.width - config.corner_radius.top_right), + .y = @round(bounding_box.y + config.corner_radius.top_right), }, - math.round(config.corner_radius.top_right - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.top_right - @as(f32, @floatFromInt(config.width.top))), config.corner_radius.top_right, 270, 360, 10, - clayColorToRaylibColor(config.top.color), + clayColorToRaylibColor(config.color), ); } if (config.corner_radius.bottom_left > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + config.corner_radius.bottom_left), - .y = math.round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_left), + .x = @round(bounding_box.x + config.corner_radius.bottom_left), + .y = @round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_left), }, - math.round(config.corner_radius.bottom_left - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.bottom_left - @as(f32, @floatFromInt(config.width.top))), config.corner_radius.bottom_left, 90, 180, 10, - clayColorToRaylibColor(config.bottom.color), + clayColorToRaylibColor(config.color), ); } if (config.corner_radius.bottom_right > 0) { rl.drawRing( rl.Vector2{ - .x = math.round(bounding_box.x + bounding_box.width - config.corner_radius.bottom_right), - .y = math.round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_right), + .x = @round(bounding_box.x + bounding_box.width - config.corner_radius.bottom_right), + .y = @round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_right), }, - math.round(config.corner_radius.bottom_right - @as(f32, @floatFromInt(config.top.width))), + @round(config.corner_radius.bottom_right - @as(f32, @floatFromInt(config.width.bottom))), config.corner_radius.bottom_right, 0.1, 90, 10, - clayColorToRaylibColor(config.bottom.color), + clayColorToRaylibColor(config.color), ); } }, @@ -185,7 +192,7 @@ pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), alloca } } -pub fn measureText(clay_text: []const u8, config: *cl.TextElementConfig) cl.Dimensions { +pub fn measureText(clay_text: []const u8, config: *cl.TextElementConfig, _: void) cl.Dimensions { const font = raylib_fonts[config.font_id].?; const text: []const u8 = clay_text; const font_size: f32 = @floatFromInt(config.font_size); diff --git a/src/root.zig b/src/root.zig index 28b5610..660f517 100644 --- a/src/root.zig +++ b/src/root.zig @@ -8,8 +8,9 @@ pub extern var Clay__debugViewWidth: u32; /// for direct calls to the clay c library pub const cdefs = struct { // TODO: should use @extern instead but zls does not yet support it well and that is more important + pub extern fn Clay_GetElementData(id: ElementId) ElementData; pub extern fn Clay_MinMemorySize() u32; - pub extern fn Clay_CreateArenaWithCapacityAndMemory(capacity: u32, offset: [*c]u8) Arena; + pub extern fn Clay_CreateArenaWithCapacityAndMemory(capacity: u32, offset: ?*anyopaque) Arena; pub extern fn Clay_SetPointerState(position: Vector2, pointerDown: bool) void; pub extern fn Clay_Initialize(arena: Arena, layoutDimensions: Dimensions, errorHandler: ErrorHandler) *Context; pub extern fn Clay_GetCurrentContext() *Context; @@ -21,11 +22,11 @@ pub const cdefs = struct { pub extern fn Clay_GetElementId(idString: String) ElementId; pub extern fn Clay_GetElementIdWithIndex(idString: String, index: u32) ElementId; pub extern fn Clay_Hovered() bool; - pub extern fn Clay_OnHover(onHoverFunction: *const fn (ElementId, PointerData, usize) callconv(.c) void, userData: usize) void; + pub extern fn Clay_OnHover(onHoverFunction: *const fn (ElementId, PointerData, ?*anyopaque) callconv(.c) void, userData: ?*anyopaque) void; pub extern fn Clay_PointerOver(elementId: ElementId) bool; pub extern fn Clay_GetScrollContainerData(id: ElementId) ScrollContainerData; - pub extern fn Clay_SetMeasureTextFunction(measureTextFunction: *const fn (*String, *TextElementConfig) callconv(.c) Dimensions) void; - pub extern fn Clay_SetQueryScrollOffsetFunction(queryScrollOffsetFunction: *const fn (u32) callconv(.c) Vector2) void; + pub extern fn Clay_SetMeasureTextFunction(measureTextFunction: *const fn (StringSlice, *TextElementConfig, ?*anyopaque) callconv(.c) Dimensions, userData: ?*anyopaque) void; + pub extern fn Clay_SetQueryScrollOffsetFunction(queryScrollOffsetFunction: *const fn (u32, ?*anyopaque) callconv(.c) Vector2, userData: ?*anyopaque) void; pub extern fn Clay_RenderCommandArray_Get(array: *ClayArray(RenderCommand), index: i32) *RenderCommand; pub extern fn Clay_SetDebugModeEnabled(enabled: bool) void; pub extern fn Clay_IsDebugModeEnabled() bool; @@ -36,20 +37,12 @@ pub const cdefs = struct { pub extern fn Clay_SetMaxMeasureTextCacheWordCount(maxMeasureTextCacheWordCount: i32) void; pub extern fn Clay_ResetMeasureTextCache() void; + pub extern fn Clay__ConfigureOpenElement(config: ElementDeclaration) void; pub extern fn Clay__OpenElement() void; pub extern fn Clay__CloseElement() void; pub extern fn Clay__StoreLayoutConfig(config: LayoutConfig) *LayoutConfig; - pub extern fn Clay__ElementPostConfiguration() void; - pub extern fn Clay__AttachId(id: ElementId) void; - pub extern fn Clay__AttachLayoutConfig(config: *LayoutConfig) void; - pub extern fn Clay__AttachElementConfig(config: ElementConfigUnion, @"type": ElementConfigType) void; - pub extern fn Clay__StoreRectangleElementConfig(config: RectangleElementConfig) *RectangleElementConfig; + pub extern fn Clay__AttachId(id: ElementId) ElementId; pub extern fn Clay__StoreTextElementConfig(config: TextElementConfig) *TextElementConfig; - pub extern fn Clay__StoreImageElementConfig(config: ImageElementConfig) *ImageElementConfig; - pub extern fn Clay__StoreFloatingElementConfig(config: FloatingElementConfig) *FloatingElementConfig; - pub extern fn Clay__StoreCustomElementConfig(config: CustomElementConfig) *CustomElementConfig; - pub extern fn Clay__StoreScrollElementConfig(config: ScrollElementConfig) *ScrollElementConfig; - pub extern fn Clay__StoreBorderElementConfig(config: BorderElementConfig) *BorderElementConfig; pub extern fn Clay__HashString(key: String, offset: u32, seed: u32) ElementId; pub extern fn Clay__OpenTextElement(text: String, textConfig: *TextElementConfig) void; pub extern fn Clay__GetParentElementId() u32; @@ -59,7 +52,13 @@ pub const EnumBackingType = u8; pub const String = extern struct { length: i32, - chars: [*:0]const u8, + chars: [*]const u8, +}; + +pub const StringSlice = extern struct { + length: i32 = 0, + chars: [*]const u8, + base_chars: [*]const u8, }; pub const Context = opaque {}; @@ -80,6 +79,7 @@ pub const Vector2 = extern struct { y: f32, }; +/// order: r, g, b, a pub const Color = [4]f32; pub const BoundingBox = extern struct { @@ -102,25 +102,25 @@ const SizingConstraint = extern union { pub const SizingAxis = extern struct { // Note: `min` is used for CLAY_SIZING_PERCENT, slightly different to clay.h due to lack of C anonymous unions size: SizingConstraint = .{ .minmax = .{} }, - type: SizingType = .FIT, + type: SizingType = .fit, - pub const grow = SizingAxis{ .type = .GROW, .size = .{ .minmax = .{ .min = 0, .max = 0 } } }; - pub const fit = SizingAxis{ .type = .FIT, .size = .{ .minmax = .{ .min = 0, .max = 0 } } }; + pub const grow = SizingAxis{ .type = .grow, .size = .{ .minmax = .{ .min = 0, .max = 0 } } }; + pub const fit = SizingAxis{ .type = .fit, .size = .{ .minmax = .{ .min = 0, .max = 0 } } }; pub fn growMinMax(size_minmax: SizingMinMax) SizingAxis { - return .{ .type = .GROW, .size = .{ .minmax = size_minmax } }; + return .{ .type = .grow, .size = .{ .minmax = size_minmax } }; } pub fn fitMinMax(size_minmax: SizingMinMax) SizingAxis { - return .{ .type = .FIT, .size = .{ .minmax = size_minmax } }; + return .{ .type = .fit, .size = .{ .minmax = size_minmax } }; } pub fn fixed(size: f32) SizingAxis { - return .{ .type = .FIXED, .size = .{ .minmax = .{ .max = size, .min = size } } }; + return .{ .type = .fixed, .size = .{ .minmax = .{ .max = size, .min = size } } }; } pub fn percent(size_percent: f32) SizingAxis { - return .{ .type = .PERCENT, .size = .{ .percent = size_percent } }; + return .{ .type = .percent, .size = .{ .percent = size_percent } }; } }; @@ -134,20 +134,33 @@ pub const Sizing = extern struct { }; pub const Padding = extern struct { - x: u16 = 0, - y: u16 = 0, + left: u16 = 0, + right: u16 = 0, + top: u16 = 0, + bottom: u16 = 0, + + pub fn xy(vertical_padding: u16, horizontal_padding: u16) Padding { + return .{ + .top = vertical_padding, + .bottom = vertical_padding, + .left = horizontal_padding, + .right = horizontal_padding, + }; + } pub fn all(size: u16) Padding { return Padding{ - .x = size, - .y = size, + .left = size, + .right = size, + .top = size, + .bottom = size, }; } }; -pub const TextElementConfigWrapMode = enum(c_uint) { +pub const TextElementConfigWrapMode = enum(EnumBackingType) { words = 0, - newlines = 1, + new_lines = 1, none = 2, }; @@ -158,18 +171,19 @@ pub const TextElementConfig = extern struct { letter_spacing: u16 = 0, line_height: u16 = 0, wrap_mode: TextElementConfigWrapMode = .words, + hash_string_contents: bool = false, }; -pub const FloatingAttachPointType = enum(u8) { - LEFT_TOP = 0, - LEFT_CENTER = 1, - LEFT_BOTTOM = 2, - CENTER_TOP = 3, - CENTER_CENTER = 4, - CENTER_BOTTOM = 5, - RIGHT_TOP = 6, - RIGHT_CENTER = 7, - RIGHT_BOTTOM = 8, +pub const FloatingAttachPointType = enum(EnumBackingType) { + left_top = 0, + left_center = 1, + left_bottom = 2, + center_top = 3, + center_center = 4, + center_bottom = 5, + right_top = 6, + right_center = 7, + right_bottom = 8, }; pub const FloatingAttachPoints = extern struct { @@ -177,41 +191,29 @@ pub const FloatingAttachPoints = extern struct { parent: FloatingAttachPointType, }; -pub const PointerCaptureMode = enum(c_uint) { - CAPTURE = 0, - PASSTHROUGH = 1, +pub const FloatingAttachToElement = enum(EnumBackingType) { + to_none = 0, + to_parent = 1, + to_element_with_id = 2, + to_root = 3, +}; + +pub const PointerCaptureMode = enum(EnumBackingType) { + capture = 0, + passthrough = 1, }; pub const FloatingElementConfig = extern struct { - offset: Vector2, - expand: Dimensions, - zIndex: u16, - parentId: u32, - attachment: FloatingAttachPoints, - pointerCaptureMode: PointerCaptureMode, + offset: Vector2 = .{ .x = 0, .y = 0 }, + expand: Dimensions = .{ .w = 0, .h = 0 }, + parentId: u32 = 0, + zIndex: i16 = 0, + attach_points: FloatingAttachPoints = .{ .element = .left_top, .parent = .left_top }, + pointer_capture_mode: PointerCaptureMode = .capture, + attach_to: FloatingAttachToElement = .to_none, }; -pub const Border = extern struct { - width: u32, - color: Color, -}; - -pub const ElementConfigUnion = extern union { - rectangle_config: *RectangleElementConfig, - text_config: *TextElementConfig, - image_config: *ImageElementConfig, - floating_config: *FloatingElementConfig, - custom_config: *CustomElementConfig, - scroll_config: *ScrollElementConfig, - border_config: *BorderElementConfig, -}; - -pub const ElementConfig = extern struct { - type: ElementConfigType, - config: ElementConfigUnion, -}; - -pub const RenderCommandType = enum(u8) { +pub const RenderCommandType = enum(EnumBackingType) { none = 0, rectangle = 1, border = 2, @@ -222,13 +224,7 @@ pub const RenderCommandType = enum(u8) { custom = 7, }; -pub const RenderCommandArray = extern struct { - capacity: i32, - length: i32, - internalArray: [*]RenderCommand, -}; - -pub const PointerDataInteractionState = enum(c_uint) { +pub const PointerDataInteractionState = enum(EnumBackingType) { pressed_this_frame = 0, pressed = 1, released_this_frame = 2, @@ -240,7 +236,7 @@ pub const PointerData = extern struct { state: PointerDataInteractionState, }; -pub const ErrorType = enum(c_uint) { +pub const ErrorType = enum(EnumBackingType) { text_measurement_function_not_provided = 0, arena_capacity_exceeded = 1, elements_capacity_exceeded = 2, @@ -251,14 +247,14 @@ pub const ErrorType = enum(c_uint) { }; pub const ErrorData = extern struct { - errorType: ErrorType, - errorText: String, - userData: usize, + error_type: ErrorType, + error_text: String, + user_data: ?*anyopaque, }; pub const ErrorHandler = extern struct { error_handler_function: ?*const fn (ErrorData) callconv(.c) void = null, - user_data: usize = 0, + user_data: ?*anyopaque = null, }; pub const CornerRadius = extern struct { @@ -277,26 +273,173 @@ pub const CornerRadius = extern struct { } }; -pub const BorderData = extern struct { - width: u32 = 0, - color: Color = .{ 0, 0, 0, 0 }, -}; - pub const ElementId = extern struct { id: u32, offset: u32, base_id: u32, string_id: String, + + pub fn ID(string: []const u8) ElementId { + return cdefs.Clay__HashString(makeClayString(string), 0, 0); + } + + pub fn IDI(string: []const u8, index: u32) ElementId { + return cdefs.Clay__HashString(makeClayString(string), index, 0); + } + + pub fn localID(string: []const u8) ElementId { + return cdefs.Clay__HashString(makeClayString(string), 0, cdefs.Clay__GetParentElementId()); + } + + pub fn localIDI(string: []const u8, index: u32) ElementId { + return cdefs.Clay__HashString(makeClayString(string), index, cdefs.Clay__GetParentElementId()); + } }; pub const RenderCommand = extern struct { bounding_box: BoundingBox, - config: ElementConfigUnion, - text: String, + render_data: RenderData, + user_data: *anyopaque, id: u32, + z_index: i16, command_type: RenderCommandType, }; +pub const SizingType = enum(EnumBackingType) { + fit = 0, + grow = 1, + percent = 2, + fixed = 3, +}; + +pub const LayoutDirection = enum(EnumBackingType) { + left_to_right = 0, + top_to_bottom = 1, +}; + +pub const LayoutAlignmentX = enum(EnumBackingType) { + left = 0, + right = 1, + center = 2, +}; + +pub const LayoutAlignmentY = enum(EnumBackingType) { + top = 0, + bottom = 1, + center = 2, +}; + +pub const ChildAlignment = extern struct { + x: LayoutAlignmentX = .left, + y: LayoutAlignmentY = .top, + + pub const center = ChildAlignment{ .x = .center, .y = .center }; +}; + +pub const LayoutConfig = extern struct { + /// sizing of the element + sizing: Sizing = .{}, + /// padding arround children + padding: Padding = .{}, + /// gap between the children + child_gap: u16 = 0, + /// alignment of the children + child_alignment: ChildAlignment = .{}, + /// direction of the children's layout + direction: LayoutDirection = .left_to_right, +}; + +pub fn ClayArray(comptime T: type) type { + return extern struct { + capacity: u32, + length: u32, + internal_array: [*]T, + }; +} + +pub const BorderWidth = extern struct { + left: u16 = 0, + right: u16 = 0, + top: u16 = 0, + bottom: u16 = 0, + between_children: u16 = 0, + + pub fn outside(width: u16) BorderWidth { + return .{ + .left = width, + .right = width, + .top = width, + .bottom = width, + .between_children = 0, + }; + } + + pub fn all(width: u16) BorderWidth { + return .{ + .left = width, + .right = width, + .top = width, + .bottom = width, + .between_children = width, + }; + } +}; + +pub const BorderElementConfig = extern struct { + color: Color = .{ 0, 0, 0, 255 }, + width: BorderWidth = .{}, +}; + +pub const TextRenderData = extern struct { + string_contents: StringSlice, + text_color: Color, + font_id: u16, + font_size: u16, + letter_spacing: u16, + line_height: u16, +}; + +pub const RectangleRenderData = extern struct { + background_color: Color, + corner_radius: CornerRadius, +}; + +pub const ImageRenderData = extern struct { + background_color: Color, + corner_radius: CornerRadius, + source_dimensions: Dimensions, + image_data: ?*anyopaque, +}; + +pub const CustomRenderData = extern struct { + background_color: Color, + corner_radius: CornerRadius, + custom_data: ?*anyopaque, +}; + +pub const ImageElementConfig = extern struct { + image_data: ?*const anyopaque, + source_dimensions: Dimensions, +}; + +pub const BorderRenderData = extern struct { + color: Color, + corner_radius: CornerRadius, + width: BorderWidth, +}; + +pub const RenderData = extern union { + rectangle: RectangleRenderData, + text: TextRenderData, + image: ImageRenderData, + custom: CustomRenderData, + border: BorderRenderData, +}; + +pub const CustomElementConfig = extern struct { + custom_data: ?*anyopaque = null, +}; + pub const ScrollContainerData = extern 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. @@ -308,108 +451,9 @@ pub const ScrollContainerData = extern struct { found: bool, }; -pub const SizingType = enum(EnumBackingType) { - FIT = 0, - GROW = 1, - PERCENT = 2, - FIXED = 3, -}; - -pub const SizingConstraints = extern union { - size_minmax: SizingMinMax, - size_percent: f32, -}; - -pub const LayoutDirection = enum(EnumBackingType) { - LEFT_TO_RIGHT = 0, - TOP_TO_BOTTOM = 1, -}; - -pub const LayoutAlignmentX = enum(EnumBackingType) { - LEFT = 0, - RIGHT = 1, - CENTER = 2, -}; - -pub const LayoutAlignmentY = enum(EnumBackingType) { - TOP = 0, - BOTTOM = 1, - CENTER = 2, -}; - -pub const ChildAlignment = extern struct { - x: LayoutAlignmentX = .LEFT, - y: LayoutAlignmentY = .TOP, - - pub const CENTER = ChildAlignment{ .x = .CENTER, .y = .CENTER }; -}; - -pub const LayoutConfig = extern struct { - /// sizing of the element - sizing: Sizing = .{}, - /// padding arround children - padding: Padding = .{}, - /// gap between the children - child_gap: u16 = 0, - /// alignement of the children - child_alignment: ChildAlignment = .{}, - /// direction of the children's layout - direction: LayoutDirection = .LEFT_TO_RIGHT, -}; - -pub fn ClayArray(comptime T: type) type { - return extern struct { - capacity: u32, - length: u32, - internal_array: [*]T, - }; -} - -pub const RectangleElementConfig = extern struct { - color: Color = .{ 255, 255, 255, 255 }, - corner_radius: CornerRadius = .{}, -}; - -pub const BorderElementConfig = extern struct { - left: BorderData = .{}, - right: BorderData = .{}, - top: BorderData = .{}, - bottom: BorderData = .{}, - between_children: BorderData = .{}, - corner_radius: CornerRadius = .{}, - - pub fn outside(color: Color, width: u32, radius: f32) BorderElementConfig { - const data = BorderData{ .color = color, .width = width }; - return BorderElementConfig{ - .left = data, - .right = data, - .top = data, - .bottom = data, - .between_children = .{}, - .corner_radius = .all(radius), - }; - } - - pub fn all(color: Color, width: u32, radius: f32) BorderElementConfig { - const data = BorderData{ .color = color, .width = width }; - return BorderElementConfig{ - .left = data, - .right = data, - .top = data, - .bottom = data, - .between_children = data, - .corner_radius = .all(radius), - }; - } -}; - -pub const ImageElementConfig = extern struct { - image_data: *const anyopaque, - source_dimensions: Dimensions, -}; - -pub const CustomElementConfig = extern struct { - custom_data: *anyopaque, +pub const ElementData = extern struct { + bounding_box: BoundingBox, + found: bool, }; pub const ScrollElementConfig = extern struct { @@ -417,65 +461,41 @@ pub const ScrollElementConfig = extern struct { vertical: bool = false, }; +pub const SharedElementConfig = extern struct { + backgroundColor: Color, + cornerRadius: CornerRadius, + userData: ?*anyopaque, +}; + pub const ElementConfigType = enum(EnumBackingType) { - rectangle_config = 1, - border_config = 2, - floating_config = 4, - scroll_config = 8, - image_config = 16, - text_config = 32, - custom_config = 64, + none = 0, + border = 1, + floating = 2, + scroll = 3, + image = 4, + text = 5, + custom = 6, + shared = 7, }; -pub const Attach = struct { - pub fn rectangle(config: RectangleElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .rectangle_config = cdefs.Clay__StoreRectangleElementConfig(config) }, .rectangle_config); - return .{}; - } - pub fn border(config: BorderElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .border_config = cdefs.Clay__StoreBorderElementConfig(config) }, .border_config); - return .{}; - } - pub fn floating(config: FloatingElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .floating_config = cdefs.Clay__StoreFloatingElementConfig(config) }, .floating_config); - return .{}; - } - pub fn scroll(config: ScrollElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .scroll_config = cdefs.Clay__StoreScrollElementConfig(config) }, .scroll_config); - return .{}; - } - pub fn image(config: ImageElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .image_config = cdefs.Clay__StoreImageElementConfig(config) }, .image_config); - return .{}; - } - pub fn text(config: TextElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .text_config = cdefs.Clay__StoreTextElementConfig(config) }, .text_config); - return .{}; - } - pub fn custom(config: CustomElementConfig) Attach { - cdefs.Clay__AttachElementConfig(ElementConfigUnion{ .custom_config = cdefs.Clay__StoreCustomElementConfig(config) }, .custom_config); - return .{}; - } - pub fn ID(string: []const u8) Attach { - cdefs.Clay__AttachId(cdefs.Clay__HashString(makeClayString(string), 0, 0)); - return .{}; - } - pub fn IDI(string: []const u8, index: u32) Attach { - cdefs.Clay__AttachId(cdefs.Clay__HashString(makeClayString(string), index, 0)); - return .{}; - } - pub fn layout(config: LayoutConfig) Attach { - cdefs.Clay__AttachLayoutConfig(cdefs.Clay__StoreLayoutConfig(config)); - return .{}; - } +pub const ElementDeclaration = extern struct { + id: ElementId = .{ .base_id = 0, .id = 0, .offset = 0, .string_id = .{ .chars = undefined, .length = 0 } }, + layout: LayoutConfig = .{}, + background_color: Color = .{ 0, 0, 0, 0 }, + corner_radius: CornerRadius = .{}, + image: ImageElementConfig = .{ .image_data = null, .source_dimensions = .{ .h = 0, .w = 0 } }, + floating: FloatingElementConfig = .{}, + custom: CustomElementConfig = .{}, + scroll: ScrollElementConfig = .{}, + border: BorderElementConfig = .{}, + user_data: ?*anyopaque = null, }; -pub inline fn UI() fn (configs: []const Attach) callconv(.@"inline") fn (void) void { +pub inline fn UI() fn (config: ElementDeclaration) callconv(.@"inline") fn (void) void { cdefs.Clay__OpenElement(); return struct { - inline fn f(configs: []const Attach) fn (void) void { - _ = configs; - cdefs.Clay__ElementPostConfiguration(); + inline fn f(config: ElementDeclaration) fn (void) void { + cdefs.Clay__ConfigureOpenElement(config); return struct { fn f(_: void) void { cdefs.Clay__CloseElement(); @@ -485,6 +505,7 @@ pub inline fn UI() fn (configs: []const Attach) callconv(.@"inline") fn (void) v }.f; } +pub const getElementData = cdefs.Clay_GetElementData; pub const minMemorySize = cdefs.Clay_MinMemorySize; pub const setPointerState = cdefs.Clay_SetPointerState; pub const initialize = cdefs.Clay_Initialize; @@ -494,10 +515,10 @@ pub const updateScrollContainers = cdefs.Clay_UpdateScrollContainers; pub const setLayoutDimensions = cdefs.Clay_SetLayoutDimensions; pub const beginLayout = cdefs.Clay_BeginLayout; pub const endLayout = cdefs.Clay_EndLayout; +pub const getElementIdWithIndex = cdefs.Clay_GetElementIdWithIndex; pub const hovered = cdefs.Clay_Hovered; pub const pointerOver = cdefs.Clay_PointerOver; pub const getScrollContainerData = cdefs.Clay_GetScrollContainerData; -pub const setQueryScrollOffsetFunction = cdefs.Clay_SetQueryScrollOffsetFunction; pub const renderCommandArrayGet = cdefs.Clay_RenderCommandArray_Get; pub const setDebugModeEnabled = cdefs.Clay_SetDebugModeEnabled; pub const isDebugModeEnabled = cdefs.Clay_IsDebugModeEnabled; @@ -508,43 +529,86 @@ pub const getMaxMeasureTextCacheWordCount = cdefs.Clay_GetMaxMeasureTextCacheWor pub const setMaxMeasureTextCacheWordCount = cdefs.Clay_SetMaxMeasureTextCacheWordCount; pub const resetMeasureTextCache = cdefs.Clay_ResetMeasureTextCache; -/// `context` must be of same size as a pointer +/// `context` must be of same size as a pointer or be of type `void` pub fn onHover( - context: anytype, + user_data: anytype, comptime onHoverFunction: fn ( - context: @TypeOf(context), element_id: ElementId, pointer_data: PointerData, + context: @TypeOf(user_data), ) void, ) void { - if (@sizeOf(@TypeOf(context)) != @sizeOf(usize)) @compileError("`context` must be of same size as a pointer"); - const ContextType = @TypeOf(context); - cdefs.Clay_OnHover(struct { - pub fn f(element_id: ElementId, pointer_data: PointerData, data: usize) callconv(.C) void { - onHoverFunction( - if (@typeInfo(ContextType) == .pointer) - @ptrFromInt(@as(usize, @bitCast(data))) - else - @bitCast(data), - element_id, - pointer_data, - ); - } - }.f, context); + if (!(@TypeOf(user_data) == void) and @sizeOf(@TypeOf(user_data)) != @sizeOf(usize)) + @compileError("`context` must be of same size as a pointer or be of type `void`"); + + cdefs.Clay_OnHover( + struct { + pub fn f(element_id: ElementId, pointer_data: PointerData, userData: ?*anyopaque) callconv(.C) void { + onHoverFunction( + element_id, + pointer_data, + AnyopaquePtrToAnytype(@TypeOf(user_data), userData), + ); + } + }.f, + anytypeToAnyopaquePtr(user_data), + ); +} + +/// `context` must be of same size as a pointer or be of type `void` +pub fn setMeasureTextFunction( + user_data: anytype, + comptime measureTextFunction: fn ( + []const u8, + *TextElementConfig, + context: @TypeOf(user_data), + ) Dimensions, +) void { + if (!(@TypeOf(user_data) == void) and @sizeOf(@TypeOf(user_data)) != @sizeOf(usize)) + @compileError("`context` must be of same size as a pointer or be of type `void`"); + + cdefs.Clay_SetMeasureTextFunction( + struct { + pub fn f(string: StringSlice, config: *TextElementConfig, userData: ?*anyopaque) callconv(.c) Dimensions { + return measureTextFunction( + string.chars[0..@intCast(string.length)], + config, + AnyopaquePtrToAnytype(@TypeOf(user_data), userData), + ); + } + }.f, + anytypeToAnyopaquePtr(user_data), + ); +} + +/// `context` must be of same size as a pointer or be of type `void` +pub fn setQueryScrollOffsetFunction( + user_data: anytype, + comptime queryScrollOffsetFunction: fn ( + u32, + @TypeOf(user_data), + ) Vector2, +) void { + if (!(@TypeOf(user_data) == void) and @sizeOf(@TypeOf(user_data)) != @sizeOf(usize)) + @compileError("`context` must be of same size as a pointer or be of type `void`"); + + cdefs.Clay_SetQueryScrollOffsetFunction( + struct { + pub fn f(scroll: u32, userData: ?*anyopaque) callconv(.c) Dimensions { + return queryScrollOffsetFunction( + scroll, + AnyopaquePtrToAnytype(@TypeOf(user_data), userData), + ); + } + }.f, + anytypeToAnyopaquePtr(user_data), + ); } pub fn createArenaWithCapacityAndMemory(buffer: []u8) Arena { return cdefs.Clay_CreateArenaWithCapacityAndMemory(@intCast(buffer.len), buffer.ptr); } -pub fn setMeasureTextFunction(comptime measureTextFunction: fn ([]const u8, *TextElementConfig) Dimensions) void { - cdefs.Clay_SetMeasureTextFunction(struct { - pub fn f(string: *String, config: *TextElementConfig) callconv(.C) Dimensions { - return measureTextFunction(@ptrCast(string.chars[0..@intCast(string.length)]), config); - } - }.f); -} - pub fn makeClayString(string: []const u8) String { return .{ .chars = @ptrCast(@constCast(string)), @@ -556,14 +620,26 @@ pub fn text(string: []const u8, config: TextElementConfig) void { cdefs.Clay__OpenTextElement(makeClayString(string), cdefs.Clay__StoreTextElementConfig(config)); } -pub fn ID(string: []const u8) ElementId { - return cdefs.Clay__HashString(makeClayString(string), 0, 0); -} - -pub fn IDI(string: []const u8, index: u32) ElementId { - return cdefs.Clay__HashString(makeClayString(string), index, 0); -} - pub fn getElementId(string: []const u8) ElementId { return cdefs.Clay_GetElementId(makeClayString(string)); } + +fn anytypeToAnyopaquePtr(user_data: anytype) ?*anyopaque { + if (@TypeOf(user_data) == void) { + return null; + } else if (@typeInfo(@TypeOf(user_data)) == .pointer) { + return @ptrCast(@alignCast(@constCast(user_data))); + } else { + return @ptrFromInt(@as(usize, @bitCast(user_data))); + } +} + +fn AnyopaquePtrToAnytype(T: type, userData: ?*anyopaque) T { + if (T == void) { + return {}; + } else if (@typeInfo(T) == .pointer) { + return @ptrCast(@alignCast(@constCast(userData))); + } else { + return @bitCast(@as(usize, @intFromPtr(userData))); + } +}