diff --git a/README.md b/README.md index 9a7b46a..4db8b6c 100644 --- a/README.md +++ b/README.md @@ -371,7 +371,7 @@ The supported directives are: - `CLAY_DISABLE_CULLING` - Disables [Visibility Culling](#visibility-culling) of render commands. - `CLAY_WASM` - Required when targeting Web Assembly. - `CLAY_OVERFLOW_TRAP` - By default, clay will continue to allow function calls without crashing even when it exhausts all its available pre-allocated memory. This can produce erroneous layout results that are difficult to interpret. If `CLAY_OVERFLOW_TRAP` is defined, clay will raise a `SIGTRAP` signal that will be caught by your debugger. Relies on `signal.h` being available in your environment. -- `CLAY_DEBUG` - Used for debugging clay's internal implementation. Useful if you want to modify or debug clay, or learn how things work. It enables a number of debug features such as preserving source strings for has IDs to make debugging easier. +- `CLAY_DEBUG` - Used for debugging clay's internal implementation. Useful if you want to modify or debug clay, or learn how things work. It enables a number of debug features such as preserving source strings for hash IDs to make debugging easier. - `CLAY_EXTEND_CONFIG_RECTANGLE` - Provide additional struct members to `CLAY_RECTANGLE_CONFIG` that will be passed through with output render commands. - `CLAY_EXTEND_CONFIG_TEXT` - Provide additional struct members to `CLAY_TEXT_CONFIG` that will be passed through with output render commands. - `CLAY_EXTEND_CONFIG_IMAGE` - Provide additional struct members to `CLAY_IMAGE_CONFIG` that will be passed through with output render commands. @@ -384,6 +384,20 @@ There are also supported bindings for other languages, including: - [Odin Bindings](https://github.com/nicbarker/clay/tree/main/bindings/odin) +### Debug Tools + +Clay includes built-in UI debugging tools, similar to the "inspector" in browsers such as Chrome or Firefox. These tools are included in `clay.h`, and work by injecting additional render commands into the output [Clay_RenderCommandArray](#clay_rendercommandarray). + +As long as the renderer that you're using works correctly, no additional setup or configuration is required to use the debug tools. + +To enable the debug tools, use the function `Clay_SetDebugModeEnabled(bool enabled)`. This boolean is persistent and does not need to be set every frame. + +The debug tooling by default will render as a panel to the right side of the screen, compressing your layout by its width. The default width is 400 and is currently configurable via the direct mutation of the internal variable `Clay__debugViewWidth`, however this is an internal API and is potentially subject to change. + +Screenshot 2024-09-12 at 12 54 03 PM + +_The official Clay website with debug tooling visible_ + # API ### Naming Conventions @@ -1422,13 +1436,13 @@ switch (renderCommand->commandType) { `uint32_t CLAY_ID(char *label)` -Generates a `uint32_t` string hash from the provided `char *label`. Used both to generate ids when defining element macros, as well as for referencing ids later when using utility functions such as [Clay_PointerOver](#clay-pointerover) +Generates a `uint32_t` string id from the provided `char *label`. Used both to generate ids when defining element macros, as well as for referencing ids later when using utility functions such as [Clay_PointerOver](#clay-pointerover) ### CLAY_IDI() `uint32_t CLAY_IDI(char *label, int index)` -Generates a `uint32_t` string hash from the provided `char *label`, combined with the `int index`. Used for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime. +Generates a `uint32_t` string id from the provided `char *label`, combined with the `int index`. Used for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime. ## Data Structures & Definitions diff --git a/bindings/odin/clay-odin/clay.odin b/bindings/odin/clay-odin/clay.odin index 18cbd59..4f62309 100644 --- a/bindings/odin/clay-odin/clay.odin +++ b/bindings/odin/clay-odin/clay.odin @@ -57,6 +57,13 @@ BorderData :: struct { color: Color, } +ElementId :: struct { + id: u32, + offset: u32, + baseId: u32, + stringId: String, +} + when ODIN_OS == .Windows { EnumBackingType :: u32 } else { @@ -79,12 +86,19 @@ RectangleElementConfig :: struct { cornerRadius: CornerRadius, } +TextWrapMode :: enum EnumBackingType { + Words, + Newlines, + None, +} + TextElementConfig :: struct { textColor: Color, fontId: u16, fontSize: u16, letterSpacing: u16, lineSpacing: u16, + wrapMode: TextWrapMode, } ImageElementConfig :: struct { @@ -234,15 +248,17 @@ ClayArray :: struct($type: typeid) { foreign Clay { MinMemorySize :: proc() -> u32 --- CreateArenaWithCapacityAndMemory :: proc(capacity: u32, offset: [^]u8) -> Arena --- - SetPointerPosition :: proc(position: Vector2) --- - Initialize :: proc(arena: Arena) --- + SetPointerState :: proc(position: Vector2, pointerDown: bool) --- + Initialize :: proc(arena: Arena, layoutDimensions: Dimensions) --- UpdateScrollContainers :: proc(isPointerActive: bool, scrollDelta: Vector2, deltaTime: c.float) --- - BeginLayout :: proc(screenWidth: c.int, screenHeight: c.int) --- - EndLayout :: proc(screenWidth: c.int, screenHeight: c.int) -> ClayArray(RenderCommand) --- - PointerOver :: proc(id: u32) -> bool --- - GetScrollContainerData :: proc(id: u32) -> ScrollContainerData --- + SetLayoutDimensions :: proc(dimensions: Dimensions) --- + BeginLayout :: proc() --- + EndLayout :: proc() -> ClayArray(RenderCommand) --- + PointerOver :: proc(id: ElementId) -> bool --- + GetScrollContainerData :: proc(id: ElementId) -> ScrollContainerData --- SetMeasureTextFunction :: proc(measureTextFunction: proc "c" (text: ^String, config: ^TextElementConfig) -> Dimensions) --- RenderCommandArray_Get :: proc(array: ^ClayArray(RenderCommand), index: i32) -> ^RenderCommand --- + SetDebugModeEnabled :: proc(enabled: bool) --- } @(private, link_prefix = "Clay_", default_calling_convention = "c") @@ -259,14 +275,14 @@ foreign _ { @(link_prefix = "Clay_", default_calling_convention = "c", private) foreign Clay { - _OpenContainerElement :: proc(id: u32, layoutConfig: ^LayoutConfig) --- - _OpenRectangleElement :: proc(id: u32, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) --- - _OpenTextElement :: proc(id: u32, text: String, textConfig: ^TextElementConfig) --- - _OpenImageElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) --- - _OpenScrollElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^ScrollElementConfig) -> rawptr --- - _OpenFloatingElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^FloatingElementConfig) -> rawptr --- - _OpenBorderElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^BorderElementConfig) --- - _OpenCustomElement :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^CustomElementConfig) --- + _OpenContainerElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig) --- + _OpenRectangleElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) --- + _OpenTextElement :: proc(id: ElementId, text: String, textConfig: ^TextElementConfig) --- + _OpenImageElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) --- + _OpenScrollElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ScrollElementConfig) --- + _OpenFloatingElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^FloatingElementConfig) --- + _OpenBorderElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^BorderElementConfig) --- + _OpenCustomElement :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^CustomElementConfig) --- _CloseElementWithChildren :: proc() --- _CloseScrollElement :: proc() --- _CloseFloatingElement :: proc() --- @@ -278,52 +294,52 @@ foreign Clay { _CustomElementConfigArray_Add :: proc(array: ^ClayArray(CustomElementConfig), config: CustomElementConfig) -> ^CustomElementConfig --- _ScrollElementConfigArray_Add :: proc(array: ^ClayArray(ScrollElementConfig), config: ScrollElementConfig) -> ^ScrollElementConfig --- _BorderElementConfigArray_Add :: proc(array: ^ClayArray(BorderElementConfig), config: BorderElementConfig) -> ^BorderElementConfig --- - _HashString :: proc(toHash: String, index: u32) -> u32 --- + _HashString :: proc(toHash: String, index: u32) -> ElementId --- } @(require_results, deferred_none = _CloseElementWithChildren) -Container :: proc(id: u32, layoutConfig: ^LayoutConfig) -> bool { +Container :: proc(id: ElementId, layoutConfig: ^LayoutConfig) -> bool { _OpenContainerElement(id, layoutConfig) return true } @(require_results, deferred_none = _CloseElementWithChildren) -Rectangle :: proc(id: u32, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) -> bool { +Rectangle :: proc(id: ElementId, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) -> bool { _OpenRectangleElement(id, layoutConfig, rectangleConfig) return true } -Text :: proc(id: u32, text: string, textConfig: ^TextElementConfig) -> bool { +Text :: proc(id: ElementId, text: string, textConfig: ^TextElementConfig) -> bool { _OpenTextElement(id, MakeString(text), textConfig) return true } @(require_results, deferred_none = _CloseElementWithChildren) -Image :: proc(id: u32, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) -> bool { +Image :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) -> bool { _OpenImageElement(id, layoutConfig, imageConfig) return true } @(require_results, deferred_none = _CloseScrollElement) -Scroll :: proc(id: u32, layoutConfig: ^LayoutConfig, scrollConfig: ^ScrollElementConfig) -> bool { +Scroll :: proc(id: ElementId, layoutConfig: ^LayoutConfig, scrollConfig: ^ScrollElementConfig) -> bool { _OpenScrollElement(id, layoutConfig, scrollConfig) return true } @(require_results, deferred_none = _CloseFloatingElement) -Floating :: proc(id: u32, layoutConfig: ^LayoutConfig, floatingConfig: ^FloatingElementConfig) -> bool { +Floating :: proc(id: ElementId, layoutConfig: ^LayoutConfig, floatingConfig: ^FloatingElementConfig) -> bool { _OpenFloatingElement(id, layoutConfig, floatingConfig) return true } @(require_results, deferred_none = _CloseElementWithChildren) -Border :: proc(id: u32, layoutConfig: ^LayoutConfig, borderConfig: ^BorderElementConfig) -> bool { +Border :: proc(id: ElementId, layoutConfig: ^LayoutConfig, borderConfig: ^BorderElementConfig) -> bool { _OpenBorderElement(id, layoutConfig, borderConfig) return true } @(require_results, deferred_none = _CloseElementWithChildren) -Custom :: proc(id: u32, layoutConfig: ^LayoutConfig, customConfig: ^CustomElementConfig) -> bool { +Custom :: proc(id: ElementId, layoutConfig: ^LayoutConfig, customConfig: ^CustomElementConfig) -> bool { _OpenCustomElement(id, layoutConfig, customConfig) return true } @@ -412,6 +428,6 @@ MakeString :: proc(label: string) -> String { return String{chars = raw_data(label), length = cast(c.int)len(label)} } -ID :: proc(label: string, index: u32 = 0) -> u32 { +ID :: proc(label: string, index: u32 = 0) -> ElementId { return _HashString(MakeString(label), index) } diff --git a/bindings/odin/clay-odin/linux/clay.a b/bindings/odin/clay-odin/linux/clay.a index f6a59c7..fb1b44b 100644 Binary files a/bindings/odin/clay-odin/linux/clay.a and b/bindings/odin/clay-odin/linux/clay.a differ diff --git a/bindings/odin/clay-odin/macos-arm64/clay.a b/bindings/odin/clay-odin/macos-arm64/clay.a index 37f765b..0ea7d09 100644 Binary files a/bindings/odin/clay-odin/macos-arm64/clay.a and b/bindings/odin/clay-odin/macos-arm64/clay.a differ diff --git a/bindings/odin/clay-odin/macos/clay.a b/bindings/odin/clay-odin/macos/clay.a index 0608ed1..7419c67 100644 Binary files a/bindings/odin/clay-odin/macos/clay.a and b/bindings/odin/clay-odin/macos/clay.a differ diff --git a/bindings/odin/clay-odin/wasm/clay.o b/bindings/odin/clay-odin/wasm/clay.o index c814d34..2c5a79b 100644 Binary files a/bindings/odin/clay-odin/wasm/clay.o and b/bindings/odin/clay-odin/wasm/clay.o differ diff --git a/bindings/odin/clay-odin/windows/clay.lib b/bindings/odin/clay-odin/windows/clay.lib index c4a727b..d8f5aff 100644 Binary files a/bindings/odin/clay-odin/windows/clay.lib and b/bindings/odin/clay-odin/windows/clay.lib differ diff --git a/bindings/odin/clay-official-website b/bindings/odin/clay-official-website new file mode 100755 index 0000000..8f8f574 Binary files /dev/null and b/bindings/odin/clay-official-website differ diff --git a/bindings/odin/examples/clay-official-website/clay-official-website.odin b/bindings/odin/examples/clay-official-website/clay-official-website.odin index 5c973f6..8b64d70 100644 --- a/bindings/odin/examples/clay-official-website/clay-official-website.odin +++ b/bindings/odin/examples/clay-official-website/clay-official-website.odin @@ -15,7 +15,8 @@ checkImage3: raylib.Texture2D = {} checkImage4: raylib.Texture2D = {} checkImage5: raylib.Texture2D = {} -FONT_ID_TITLE_56 :: 0 +FONT_ID_BODY_16 :: 0 +FONT_ID_TITLE_56 :: 9 FONT_ID_TITLE_52 :: 1 FONT_ID_TITLE_48 :: 2 FONT_ID_TITLE_36 :: 3 @@ -24,7 +25,6 @@ FONT_ID_BODY_36 :: 5 FONT_ID_BODY_30 :: 6 FONT_ID_BODY_28 :: 7 FONT_ID_BODY_24 :: 8 -FONT_ID_BODY_16 :: 9 COLOR_LIGHT :: clay.Color{244, 235, 230, 255} COLOR_LIGHT_HOVER :: clay.Color{224, 215, 210, 255} @@ -112,13 +112,15 @@ LandingPageDesktop :: proc() { LandingPageMobile :: proc() { if clay.Container( clay.ID("LandingPage1Mobile"), - clay.Layout({ - layoutDirection = .TOP_TO_BOTTOM, - sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})}, - childAlignment = {x = .CENTER, y = .CENTER}, - padding = {16, 32}, - childGap = 32, - }), + clay.Layout( + { + layoutDirection = .TOP_TO_BOTTOM, + sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})}, + childAlignment = {x = .CENTER, y = .CENTER}, + padding = {16, 32}, + childGap = 32, + }, + ), ) { if clay.Container(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingGrow({})}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { clay.Text( @@ -331,7 +333,7 @@ HighPerformancePageMobile :: proc(lerpValue: f32) { } } -RendererButtonActive :: proc(id: u32, index: i32, text: string) { +RendererButtonActive :: proc(id: clay.ElementId, index: i32, text: string) { if clay.Rectangle( id, clay.Layout({sizing = {width = clay.SizingFixed(300)}, padding = {16, 16}}), @@ -341,7 +343,7 @@ RendererButtonActive :: proc(id: u32, index: i32, text: string) { } } -RendererButtonInactive :: proc(id: u32, index: u32, text: string) { +RendererButtonInactive :: proc(id: clay.ElementId, index: u32, text: string) { if clay.Border(id, clay.Layout({}), clay.BorderConfigOutsideRadius({2, COLOR_RED}, 10)) { if clay.Rectangle( clay.ID("RendererButtonInactiveInner", index), @@ -377,11 +379,7 @@ RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay. clay.ID("RendererRightText"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}, layoutDirection = .TOP_TO_BOTTOM, childGap = 16}), ) { - clay.Text( - clay.ID("RendererTextRightTitle"), - "Try changing renderer!", - clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE}), - ) + clay.Text(clay.ID("RendererTextRightTitle"), "Try changing renderer!", clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE})) if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 32})}})) {} RendererButtonActive(clay.ID("RendererSelectButtonActive", 0), 0, "Raylib Renderer") } @@ -405,13 +403,15 @@ RendererPageDesktop :: proc() { RendererPageMobile :: proc() { if clay.Rectangle( clay.ID("RendererMobile"), - clay.Layout({ - layoutDirection = .TOP_TO_BOTTOM, - sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, - childAlignment = {x = .CENTER, y = .CENTER}, - padding = {x = 16, y = 32}, - childGap = 32, - }), + clay.Layout( + { + layoutDirection = .TOP_TO_BOTTOM, + sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, + childAlignment = {x = .CENTER, y = .CENTER}, + padding = {x = 16, y = 32}, + childGap = 32, + }, + ), clay.RectangleConfig({color = COLOR_LIGHT}), ) { RendererPage({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}, clay.SizingGrow({})) @@ -429,7 +429,7 @@ animationLerpValue: f32 = -1.0 createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) { mobileScreen := windowWidth < 750 - clay.BeginLayout(windowWidth, windowHeight) + clay.BeginLayout() if clay.Rectangle( clay.ID("OuterContainer"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {clay.SizingGrow({}), clay.SizingGrow({})}}), @@ -450,7 +450,7 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) { clay.Text(clay.ID("LinkDocsText"), "Docs", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) } } - githubButtonId: u32 = clay.ID("HeaderButtonGithub") + githubButtonId: clay.ElementId = clay.ID("HeaderButtonGithub") if clay.Border(clay.ID("LinkGithubOuter"), clay.Layout({}), clay.BorderConfigOutsideRadius({2, COLOR_RED}, 10)) { if clay.Rectangle( githubButtonId, @@ -494,7 +494,7 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) { } } } - return clay.EndLayout(windowWidth, windowHeight) + return clay.EndLayout() } loadFont :: proc(fontId: u16, fontSize: u16, path: cstring) { @@ -510,7 +510,7 @@ main :: proc() { memory := make([^]u8, minMemorySize) arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory) clay.SetMeasureTextFunction(measureText) - clay.Initialize(arena) + clay.Initialize(arena, {cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}) raylib.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE, .WINDOW_HIGHDPI, .MSAA_4X_HINT}) raylib.InitWindow(windowWidth, windowHeight, "Raylib Odin Example") @@ -533,17 +533,24 @@ main :: proc() { checkImage4 = raylib.LoadTextureFromImage(raylib.LoadImage("resources/check_4.png")) checkImage5 = raylib.LoadTextureFromImage(raylib.LoadImage("resources/check_5.png")) + debugModeEnabled: bool = false + for !raylib.WindowShouldClose() { defer free_all(context.temp_allocator) - + animationLerpValue += raylib.GetFrameTime() if animationLerpValue > 1 { animationLerpValue = animationLerpValue - 2 } windowWidth = raylib.GetScreenWidth() windowHeight = raylib.GetScreenHeight() - clay.SetPointerPosition(transmute(clay.Vector2)raylib.GetMousePosition()) + if (raylib.IsKeyPressed(.D)) { + debugModeEnabled = !debugModeEnabled + clay.SetDebugModeEnabled(debugModeEnabled) + } + clay.SetPointerState(transmute(clay.Vector2)raylib.GetMousePosition(), raylib.IsMouseButtonDown(raylib.MouseButton.LEFT)) clay.UpdateScrollContainers(false, transmute(clay.Vector2)raylib.GetMouseWheelMoveV(), raylib.GetFrameTime()) + clay.SetLayoutDimensions({cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}) renderCommands: clay.ClayArray(clay.RenderCommand) = createLayout(animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue)) raylib.BeginDrawing() clayRaylibRender(&renderCommands) diff --git a/bindings/odin/examples/clay-official-website/clay_renderer_raylib.odin b/bindings/odin/examples/clay-official-website/clay_renderer_raylib.odin index 1eb18eb..e704411 100644 --- a/bindings/odin/examples/clay-official-website/clay_renderer_raylib.odin +++ b/bindings/odin/examples/clay-official-website/clay_renderer_raylib.odin @@ -26,7 +26,7 @@ measureText :: proc "c" (text: ^clay.String, config: ^clay.TextElementConfig) -> textHeight := cast(f32)config.fontSize fontToUse := raylibFonts[config.fontId].font - for i in 0.. } clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), allocator := context.temp_allocator) { - for i in 0.. 0) { radius: f32 = (config.cornerRadius.topLeft * 2) / min(boundingBox.width, boundingBox.height) - raylib.DrawRectangleRounded( - raylib.Rectangle{boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height}, - radius, - 8, - clayColorToRaylibColor(config.color), - ) + raylib.DrawRectangleRounded(raylib.Rectangle{boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height}, radius, 8, clayColorToRaylibColor(config.color)) } else { - raylib.DrawRectangle( - cast(i32)boundingBox.x, - cast(i32)boundingBox.y, - cast(i32)boundingBox.width, - cast(i32)boundingBox.height, - clayColorToRaylibColor(config.color), - ) + raylib.DrawRectangle(cast(i32)boundingBox.x, cast(i32)boundingBox.y, cast(i32)boundingBox.width, cast(i32)boundingBox.height, clayColorToRaylibColor(config.color)) } case clay.RenderCommandType.Border: config := renderCommand.config.borderElementConfig @@ -166,10 +155,7 @@ clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), al } if (config.cornerRadius.bottomLeft > 0) { raylib.DrawRing( - raylib.Vector2 { - math.round(boundingBox.x + config.cornerRadius.bottomLeft), - math.round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomLeft), - }, + raylib.Vector2{math.round(boundingBox.x + config.cornerRadius.bottomLeft), math.round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomLeft)}, math.round(config.cornerRadius.bottomLeft - cast(f32)config.top.width), config.cornerRadius.bottomLeft, 90, @@ -193,7 +179,7 @@ clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), al ) } case clay.RenderCommandType.Custom: - // Implement custom element rendering here + // Implement custom element rendering here } } } diff --git a/clay.h b/clay.h index 831d80a..993e1a2 100644 --- a/clay.h +++ b/clay.h @@ -74,6 +74,8 @@ #define CLAY_IDI(label, index) Clay__HashString(CLAY_STRING(label), index) +#define CLAY_ID_AUTO (Clay_ElementId) { .stringId = CLAY_STRING("Auto Generated ID"), .id = Clay__RehashWithNumber(Clay__dynamicElementIndexBaseHash.id, Clay__dynamicElementIndex++) } + #define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof(s[0])) - sizeof(s[0])) #define CLAY_STRING(string) (Clay_String) { .length = CLAY__STRING_LENGTH(string), .chars = string } @@ -116,6 +118,8 @@ children \ Clay__CloseElementWithChildren() +bool Clay__warningsEnabled = true; + // Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string, // but it is also used to represent slices. typedef struct { @@ -135,14 +139,41 @@ typedef struct { typedef struct { - uint32_t capacity; - uint32_t length; - Clay_String *internalArray; -} Clay_StringArray; + Clay_String baseMessage; + Clay_String dynamicMessage; +} Clay__Warning; -Clay_StringArray Clay_warnings = (Clay_StringArray) {}; +Clay__Warning CLAY__WARNING_DEFAULT = (Clay__Warning) {}; -Clay_String *Clay__StringArray_Add(Clay_StringArray *array, Clay_String item) +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay__Warning *internalArray; +} Clay__WarningArray; + +Clay__WarningArray Clay__WarningArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + uint64_t totalSizeBytes = capacity * sizeof(Clay_String); + Clay__WarningArray array = (Clay__WarningArray){.capacity = capacity, .length = 0}; + uint64_t nextAllocAddress = (uint64_t)(arena->nextAllocation + arena->memory); + uint64_t arenaOffsetAligned = nextAllocAddress + (CLAY__ALIGNMENT(Clay_String) - (nextAllocAddress % CLAY__ALIGNMENT(Clay_String))); + arenaOffsetAligned -= (uint64_t)arena->memory; + if (arenaOffsetAligned + totalSizeBytes <= arena->capacity) { + array.internalArray = (Clay__Warning*)(arena->memory + arenaOffsetAligned); + arena->nextAllocation = arenaOffsetAligned + totalSizeBytes; + } + else { + #ifdef CLAY_OVERFLOW_TRAP + raise(SIGTRAP); + #endif + } + return array; +} + +Clay__WarningArray Clay_warnings = (Clay__WarningArray) {}; + +Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item) { if (array->length < array->capacity) { array->internalArray[array->length++] = item; @@ -153,27 +184,7 @@ Clay_String *Clay__StringArray_Add(Clay_StringArray *array, Clay_String item) raise(SIGTRAP); #endif } - return &CLAY__STRING_DEFAULT; -} - -Clay_StringArray Clay__StringArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) -{ - uint64_t totalSizeBytes = capacity * sizeof(Clay_String); - Clay_StringArray array = (Clay_StringArray){.capacity = capacity, .length = 0}; - uint64_t nextAllocAddress = (uint64_t)(arena->nextAllocation + arena->memory); - uint64_t arenaOffsetAligned = nextAllocAddress + (CLAY__ALIGNMENT(Clay_String) - (nextAllocAddress % CLAY__ALIGNMENT(Clay_String))); - arenaOffsetAligned -= (uint64_t)arena->memory; - if (arenaOffsetAligned + totalSizeBytes <= arena->capacity) { - array.internalArray = (Clay_String*)(arena->memory + arenaOffsetAligned); - arena->nextAllocation = arenaOffsetAligned + totalSizeBytes; - } - else { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.")); - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif - } - return array; + return &CLAY__WARNING_DEFAULT; } void* Clay__Array_Allocate_Arena(uint32_t capacity, uint32_t itemSize, uint32_t alignment, Clay_Arena *arena) @@ -187,7 +198,9 @@ void* Clay__Array_Allocate_Arena(uint32_t capacity, uint32_t itemSize, uint32_t return (void*)(arena->memory + arenaOffsetAligned); } else { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.")); + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); + } #ifdef CLAY_OVERFLOW_TRAP raise(SIGTRAP); #endif @@ -200,7 +213,9 @@ bool Clay__Array_RangeCheck(int index, uint32_t length) if (index < length && index >= 0) { return true; } - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Array access out of bounds.")); + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Array access out of bounds.") }); + } #ifdef CLAY_OVERFLOW_TRAP raise(SIGTRAP); #endif @@ -212,7 +227,9 @@ bool Clay__Array_IncrementCapacityCheck(uint32_t length, uint32_t capacity) if (length < capacity) { return true; } - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to add to array that is already at capacity.")); + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Attempting to add to array that is already at capacity.") }); + } #ifdef CLAY_OVERFLOW_TRAP raise(SIGTRAP); #endif @@ -236,6 +253,41 @@ Clay__BoolArray Clay__BoolArray_Allocate_Arena(uint32_t capacity, Clay_Arena *ar #pragma endregion // __GENERATED__ template +// baseId + offset = id +typedef struct { + uint32_t id; + uint32_t offset; + uint32_t baseId; + Clay_String stringId; +} Clay_ElementId; + +Clay_ElementId CLAY__ELEMENT_ID_DEFAULT = (Clay_ElementId) {}; + +// __GENERATED__ template array_define,array_get,array_add TYPE=Clay_ElementId NAME=Clay__ElementIdArray DEFAULT_VALUE=&CLAY__ELEMENT_ID_DEFAULT +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay_ElementId *internalArray; +} Clay__ElementIdArray; + +Clay__ElementIdArray Clay__ElementIdArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return (Clay__ElementIdArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ElementId *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ElementId), CLAY__ALIGNMENT(Clay_ElementId), arena)}; +} +Clay_ElementId *Clay__ElementIdArray_Get(Clay__ElementIdArray *array, int index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__ELEMENT_ID_DEFAULT; +} +Clay_ElementId *Clay__ElementIdArray_Add(Clay__ElementIdArray *array, Clay_ElementId item) { + if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__ELEMENT_ID_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + typedef struct { float r, g, b, a; } Clay_Color; @@ -425,6 +477,13 @@ Clay_RectangleElementConfig *Clay__RectangleElementConfigArray_Add(Clay__Rectang #pragma endregion // __GENERATED__ template +typedef enum +{ + CLAY_TEXT_WRAP_WORDS, + CLAY_TEXT_WRAP_NEWLINES, + CLAY_TEXT_WRAP_NONE, +} Clay_TextElementConfigWrapMode; + typedef struct { Clay_Color textColor; @@ -432,6 +491,7 @@ typedef struct uint16_t fontSize; uint16_t letterSpacing; uint16_t lineSpacing; + Clay_TextElementConfigWrapMode wrapMode; #ifdef CLAY_EXTEND_CONFIG_TEXT CLAY_EXTEND_CONFIG_TEXT #endif @@ -590,6 +650,39 @@ Clay_ScrollElementConfig *Clay__ScrollElementConfigArray_Add(Clay__ScrollElement #pragma endregion // __GENERATED__ template +typedef struct +{ + uint32_t elementIndex; + Clay_Dimensions preferredDimensions; +} Clay__TextElementData; + +Clay__TextElementData CLAY__TEXT_ELEMENT_DATA_DEFAULT = (Clay__TextElementData) {}; + +// __GENERATED__ template array_define,array_get,array_add TYPE=Clay__TextElementData NAME=Clay__TextElementDataArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_DATA_DEFAULT +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay__TextElementData *internalArray; +} Clay__TextElementDataArray; + +Clay__TextElementDataArray Clay__TextElementDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return (Clay__TextElementDataArray){.capacity = capacity, .length = 0, .internalArray = (Clay__TextElementData *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__TextElementData), CLAY__ALIGNMENT(Clay__TextElementData), arena)}; +} +Clay__TextElementData *Clay__TextElementDataArray_Get(Clay__TextElementDataArray *array, int index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__TEXT_ELEMENT_DATA_DEFAULT; +} +Clay__TextElementData *Clay__TextElementDataArray_Add(Clay__TextElementDataArray *array, Clay__TextElementData item) { + if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__TEXT_ELEMENT_DATA_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + typedef struct { uint32_t width; @@ -632,7 +725,7 @@ Clay_BorderElementConfig *Clay__BorderElementConfigArray_Add(Clay__BorderElement typedef struct { - struct t_Clay_LayoutElement **elements; + int32_t *elements; uint16_t length; } Clay__LayoutElementChildren; @@ -828,17 +921,52 @@ Clay__ScrollContainerDataInternal Clay__ScrollContainerDataInternalArray_RemoveS #pragma endregion // __GENERATED__ template +typedef struct +{ + bool collision; + bool collapsed; +} Clay__DebugElementData; + +Clay__DebugElementData CLAY__DEBUG_ELEMENT_DATA_DEFAULT = (Clay__DebugElementData) {}; + +// __GENERATED__ template array_define,array_add,array_get TYPE=Clay__DebugElementData NAME=Clay__DebugElementDataArray DEFAULT_VALUE=&CLAY__DEBUG_ELEMENT_DATA_DEFAULT +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay__DebugElementData *internalArray; +} Clay__DebugElementDataArray; + +Clay__DebugElementDataArray Clay__DebugElementDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return (Clay__DebugElementDataArray){.capacity = capacity, .length = 0, .internalArray = (Clay__DebugElementData *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__DebugElementData), CLAY__ALIGNMENT(Clay__DebugElementData), arena)}; +} +Clay__DebugElementData *Clay__DebugElementDataArray_Add(Clay__DebugElementDataArray *array, Clay__DebugElementData item) { + if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__DEBUG_ELEMENT_DATA_DEFAULT; +} +Clay__DebugElementData *Clay__DebugElementDataArray_Get(Clay__DebugElementDataArray *array, int index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__DEBUG_ELEMENT_DATA_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + typedef struct { Clay_BoundingBox boundingBox; - uint32_t id; + Clay_ElementId elementId; Clay_LayoutElement* layoutElement; int32_t nextIndex; + uint32_t generation; + Clay__DebugElementData *debugData; } Clay_LayoutElementHashMapItem; Clay_LayoutElementHashMapItem CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT = (Clay_LayoutElementHashMapItem) { .layoutElement = &CLAY__LAYOUT_ELEMENT_DEFAULT }; -// __GENERATED__ template array_define,array_get,array_add,array_set TYPE=Clay_LayoutElementHashMapItem NAME=Clay__LayoutElementHashMapItemArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT +// __GENERATED__ template array_define,array_get,array_add TYPE=Clay_LayoutElementHashMapItem NAME=Clay__LayoutElementHashMapItemArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT #pragma region generated typedef struct { @@ -860,17 +988,6 @@ Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Add(Clay__Lay } return &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT; } -void Clay__LayoutElementHashMapItemArray_Set(Clay__LayoutElementHashMapItemArray *array, int index, Clay_LayoutElementHashMapItem value) { - if (index < array->capacity && index >= 0) { - array->internalArray[index] = value; - array->length = index < array->length ? array->length : index + 1; - } else { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.")); - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif - } -} #pragma endregion // __GENERATED__ template @@ -910,7 +1027,9 @@ void Clay__MeasureTextCacheItemArray_Set(Clay__MeasureTextCacheItemArray *array, array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; } else { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.")); + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); + } #ifdef CLAY_OVERFLOW_TRAP raise(SIGTRAP); #endif @@ -944,7 +1063,9 @@ void Clay__int32_tArray_Set(Clay__int32_tArray *array, int index, int32_t value) array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; } else { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.")); + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); + } #ifdef CLAY_OVERFLOW_TRAP raise(SIGTRAP); #endif @@ -991,7 +1112,7 @@ Clay__LayoutElementTreeNode *Clay__LayoutElementTreeNodeArray_Get(Clay__LayoutEl typedef struct { - Clay_LayoutElement *layoutElement; + uint32_t layoutElementIndex; uint32_t parentId; // This can be zero in the case of the root layout tree uint32_t clipElementId; // This can be zero if there is no clip element uint32_t zIndex; @@ -1024,16 +1145,83 @@ Clay__LayoutElementTreeRoot *Clay__LayoutElementTreeRootArray_Get(Clay__LayoutEl #pragma endregion // __GENERATED__ template -Clay_Vector2 Clay__pointerPosition = (Clay_Vector2) { -1, -1 }; +// __GENERATED__ template array_define TYPE=uint8_t NAME=Clay__CharArray DEFAULT_VALUE=0 +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + uint8_t *internalArray; +} Clay__CharArray; + +Clay__CharArray Clay__CharArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return (Clay__CharArray){.capacity = capacity, .length = 0, .internalArray = (uint8_t *)Clay__Array_Allocate_Arena(capacity, sizeof(uint8_t), CLAY__ALIGNMENT(uint8_t), arena)}; +} +#pragma endregion +// __GENERATED__ template + +Clay_String Clay__WriteStringToCharBuffer(Clay__CharArray *buffer, Clay_String string) { + for (int i = 0; i < string.length; i++) { + buffer->internalArray[buffer->length + i] = string.chars[i]; + } + buffer->length += string.length; + return (Clay_String) { .length = string.length, .chars = (const char *)(buffer->internalArray + buffer->length - string.length) }; +} + +// __GENERATED__ template array_define,array_add TYPE=Clay_String NAME=Clay__StringArray DEFAULT_VALUE=&CLAY__STRING_DEFAULT +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay_String *internalArray; +} Clay__StringArray; + +Clay__StringArray Clay__StringArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return (Clay__StringArray){.capacity = capacity, .length = 0, .internalArray = (Clay_String *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_String), CLAY__ALIGNMENT(Clay_String), arena)}; +} +Clay_String *Clay__StringArray_Add(Clay__StringArray *array, Clay_String item) { + if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__STRING_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +typedef enum +{ + CLAY__POINTER_INFO_PRESSED_THIS_FRAME, + CLAY__POINTER_INFO_PRESSED, + CLAY__POINTER_INFO_RELEASED_THIS_FRAME, + CLAY__POINTER_INFO_RELEASED, +} Clay__PointerInfoMouseDownState; + +typedef struct +{ + Clay_Vector2 position; + Clay__PointerInfoMouseDownState state; +} Clay__PointerInfo; + +Clay__PointerInfo Clay__pointerInfo = (Clay__PointerInfo) { -1, -1 }; +Clay_Dimensions Clay__layoutDimensions = (Clay_Dimensions){}; +Clay_ElementId Clay__dynamicElementIndexBaseHash = (Clay_ElementId) { .id = 128476991, .stringId = CLAY_STRING("Auto ID") }; +uint32_t Clay__dynamicElementIndex = 0; +bool Clay__debugModeEnabled = false; +uint32_t Clay__debugSelectedElementId = 0; +uint32_t Clay__debugViewWidth = 400; +Clay_Color Clay__debugViewHighlightColor = (Clay_Color) { 168, 66, 28, 100 }; +uint32_t Clay__generation = 0; uint64_t Clay__arenaResetOffset = 0; Clay_Arena Clay__internalArena; // Layout Elements / Render Commands Clay_LayoutElementArray Clay__layoutElements; Clay_RenderCommandArray Clay__renderCommands; Clay__LayoutElementPointerArray Clay__openLayoutElementStack; -Clay__LayoutElementPointerArray Clay__layoutElementChildren; -Clay__LayoutElementPointerArray Clay__layoutElementChildrenBuffer; -Clay__LayoutElementPointerArray Clay__textElementPointers; +Clay__int32_tArray Clay__layoutElementChildren; +Clay__int32_tArray Clay__layoutElementChildrenBuffer; +Clay__TextElementDataArray Clay__textElementData; Clay__LayoutElementPointerArray Clay__imageElementPointers; Clay__LayoutElementPointerArray Clay__layoutElementReusableBuffer; // Configs @@ -1046,6 +1234,7 @@ Clay__ScrollElementConfigArray Clay__scrollElementConfigs; Clay__CustomElementConfigArray Clay__customElementConfigs; Clay__BorderElementConfigArray Clay__borderElementConfigs; // Misc Data Structures +Clay__StringArray Clay__layoutElementIdStrings; Clay__LayoutElementTreeNodeArray Clay__layoutElementTreeNodeArray1; Clay__LayoutElementTreeRootArray Clay__layoutElementTreeRoots; Clay__LayoutElementHashMapItemArray Clay__layoutElementsHashMapInternal; @@ -1053,9 +1242,12 @@ Clay__int32_tArray Clay__layoutElementsHashMap; Clay__MeasureTextCacheItemArray Clay__measureTextHashMapInternal; Clay__int32_tArray Clay__measureTextHashMap; Clay__int32_tArray Clay__openClipElementStack; -Clay__int32_tArray Clay__pointerOverIds; +Clay__ElementIdArray Clay__pointerOverIds; +Clay__int32_tArray Clay__reusableElementIndexBuffer; Clay__ScrollContainerDataInternalArray Clay__scrollContainerDatas; Clay__BoolArray Clay__treeNodeVisited; +Clay__CharArray Clay__dynamicStringData; +Clay__DebugElementDataArray Clay__debugElementData; #if CLAY_WASM __attribute__((import_module("clay"), import_name("measureTextFunction"))) Clay_Dimensions Clay__MeasureText(Clay_String *text, Clay_TextElementConfig *config); @@ -1065,26 +1257,43 @@ Clay__BoolArray Clay__treeNodeVisited; Clay_String LAST_HASH; -uint32_t Clay__HashString(Clay_String key, const uint32_t offset) { +Clay_ElementId Clay__HashString(Clay_String key, const uint32_t offset) { uint32_t hash = 0; + uint32_t base = 0; for (int i = 0; i < key.length; i++) { - hash += key.chars[i]; - hash += (hash << 10); - hash ^= (hash >> 6); + base += key.chars[i]; + base += (base << 10); + base ^= (base >> 6); } + hash = base; hash += offset; hash += (hash << 10); hash ^= (hash >> 6); hash += (hash << 3); + base += (base << 3); hash ^= (hash >> 11); + base ^= (base >> 11); hash += (hash << 15); + base += (base << 15); #ifdef CLAY_DEBUG LAST_HASH = key; LAST_HASH.length = (int)offset; #endif - return hash + 1; // Reserve the hash result of zero as "null id" + return (Clay_ElementId) { .stringId = key, .id = hash + 1, .offset = offset, .baseId = base + 1 }; // Reserve the hash result of zero as "null id" +} + +Clay_ElementId Clay__Rehash(Clay_ElementId elementId, uint32_t number) { + uint32_t id = elementId.baseId; + id += number; + id += (id << 10); + id ^= (id >> 6); + + id += (id << 3); + id ^= (id >> 11); + id += (id << 15); + return (Clay_ElementId) { .stringId = elementId.stringId, .id = id, .offset = number, .baseId = elementId.baseId }; } uint32_t Clay__RehashWithNumber(uint32_t id, uint32_t number) { @@ -1155,26 +1364,39 @@ bool Clay__PointIsInsideRect(Clay_Vector2 point, Clay_BoundingBox rect) { return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height; } -Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(uint32_t id, Clay_LayoutElement* layoutElement) { - Clay_LayoutElementHashMapItem item = (Clay_LayoutElementHashMapItem) { .id = id, .layoutElement = layoutElement, .nextIndex = -1 }; - uint32_t hashBucket = id % Clay__layoutElementsHashMap.capacity; +Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Clay_LayoutElement* layoutElement) { + Clay_LayoutElementHashMapItem item = (Clay_LayoutElementHashMapItem) { .elementId = elementId, .layoutElement = layoutElement, .nextIndex = -1, .generation = Clay__generation + 1 }; + uint32_t hashBucket = elementId.id % Clay__layoutElementsHashMap.capacity; int32_t hashItemPrevious = -1; int32_t hashItemIndex = Clay__layoutElementsHashMap.internalArray[hashBucket]; while (hashItemIndex != -1) { // Just replace collision, not a big deal - leave it up to the end user Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemIndex); - if (hashItem->id == id) { // Collision - swap out linked list item + if (hashItem->elementId.id == elementId.id) { // Collision - resolve based on generation item.nextIndex = hashItem->nextIndex; - Clay__LayoutElementHashMapItemArray_Set(&Clay__layoutElementsHashMapInternal, hashItemIndex, item); - return Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemIndex); + if (hashItem->generation <= Clay__generation) { // First collision - assume this is the "same" element + hashItem->generation = Clay__generation + 1; + hashItem->layoutElement = layoutElement; + hashItem->debugData->collision = false; + } else { // Multiple collisions this frame - two elements have the same ID + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Duplicate ID detected for element: "), Clay__WriteStringToCharBuffer(&Clay__dynamicStringData, elementId.stringId) }); + } + if (Clay__debugModeEnabled) { + hashItem->debugData->collision = true; + } + } + return hashItem; } hashItemPrevious = hashItemIndex; hashItemIndex = hashItem->nextIndex; } - if (hashItemPrevious != -1) { - Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemPrevious)->nextIndex = (int32_t)Clay__layoutElementsHashMapInternal.length; - } Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Add(&Clay__layoutElementsHashMapInternal, item); - Clay__layoutElementsHashMap.internalArray[hashBucket] = (int32_t)Clay__layoutElementsHashMapInternal.length - 1; + hashItem->debugData = Clay__DebugElementDataArray_Add(&Clay__debugElementData, (Clay__DebugElementData) {}); + if (hashItemPrevious != -1) { + Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemPrevious)->nextIndex = (int32_t)Clay__layoutElementsHashMapInternal.length - 1; + } else { + Clay__layoutElementsHashMap.internalArray[hashBucket] = (int32_t)Clay__layoutElementsHashMapInternal.length - 1; + } return hashItem; } @@ -1182,8 +1404,8 @@ Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) { uint32_t hashBucket = id % Clay__layoutElementsHashMap.capacity; int32_t elementIndex = Clay__layoutElementsHashMap.internalArray[hashBucket]; while (elementIndex != -1) { - Clay_LayoutElementHashMapItem *hashEntry = Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, elementIndex); - if (hashEntry->id == id) { + Clay_LayoutElementHashMapItem *hashEntry = Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, elementIndex); + if (hashEntry->elementId.id == id) { return hashEntry; } elementIndex = hashEntry->nextIndex; @@ -1191,12 +1413,12 @@ Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) { return CLAY__NULL; } -Clay_LayoutElement *Clay__OpenElementWithParent(uint32_t id, Clay__LayoutElementType commandType, Clay_LayoutConfig* layoutConfig, Clay_ElementConfigUnion elementConfig) { +Clay_LayoutElement *Clay__OpenElementWithParent(Clay_ElementId elementId, Clay__LayoutElementType commandType, Clay_LayoutConfig* layoutConfig, Clay_ElementConfigUnion elementConfig) { Clay_LayoutElement layoutElement = (Clay_LayoutElement) { #ifdef CLAY_DEBUG - .name = LAST_HASH, + .name = elementId.stringId, #endif - .id = id, + .id = elementId.id, .elementType = commandType, .minDimensions = (Clay_Dimensions) { (float)layoutConfig->padding.x * 2, (float)layoutConfig->padding.y * 2 }, .children = (Clay__LayoutElementChildren) { .length = 0 }, @@ -1221,74 +1443,78 @@ Clay_LayoutElement *Clay__OpenElementWithParent(uint32_t id, Clay__LayoutElement Clay__openLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, layoutElement); Clay__LayoutElementPointerArray_Add(&Clay__openLayoutElementStack, Clay__openLayoutElement); - Clay__AddHashMapItem(id, Clay__openLayoutElement); + Clay__AddHashMapItem(elementId, Clay__openLayoutElement); + Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); return Clay__openLayoutElement; } -Clay_LayoutElement *Clay__OpenElement(uint32_t id, Clay__LayoutElementType commandType, Clay_LayoutConfig *layoutConfig, Clay_ElementConfigUnion elementConfig) { +Clay_LayoutElement *Clay__OpenElement(Clay_ElementId id, Clay__LayoutElementType commandType, Clay_LayoutConfig *layoutConfig, Clay_ElementConfigUnion elementConfig) { Clay__openLayoutElement->children.length++; Clay_LayoutElement *element = Clay__OpenElementWithParent(id, commandType, layoutConfig, elementConfig); - Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildrenBuffer, element); + Clay__int32_tArray_Add(&Clay__layoutElementChildrenBuffer, (int32_t)Clay__layoutElements.length - 1); return element; } -void Clay__OpenContainerElement(int id, Clay_LayoutConfig *layoutConfig) { +void Clay__OpenContainerElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig) { Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ CLAY__NULL }); } -void Clay__OpenRectangleElement(int id, Clay_LayoutConfig *layoutConfig, Clay_RectangleElementConfig *rectangleConfig) { +void Clay__OpenRectangleElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_RectangleElementConfig *rectangleConfig) { Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE, layoutConfig, (Clay_ElementConfigUnion) { .rectangleElementConfig = rectangleConfig }); } -void Clay__OpenImageElement(int id, Clay_LayoutConfig *layoutConfig, Clay_ImageElementConfig *imageConfig) { +void Clay__OpenImageElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_ImageElementConfig *imageConfig) { Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_IMAGE, layoutConfig, (Clay_ElementConfigUnion) { .imageElementConfig = imageConfig }); Clay__LayoutElementPointerArray_Add(&Clay__imageElementPointers, Clay__openLayoutElement); } -void Clay__OpenBorderElement(int id, Clay_LayoutConfig *layoutConfig, Clay_BorderElementConfig *borderConfig) { +void Clay__OpenBorderElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_BorderElementConfig *borderConfig) { Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ .borderElementConfig = borderConfig }); } -void Clay__OpenCustomElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customElementConfig) { +void Clay__OpenCustomElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customElementConfig) { Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM, layoutConfig, (Clay_ElementConfigUnion) { .customElementConfig = customElementConfig }); } -Clay_LayoutElement *Clay__OpenScrollElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_ScrollElementConfig *scrollConfig) { - Clay_LayoutElement *scrollElement = Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ .scrollElementConfig = scrollConfig }); +void Clay__OpenScrollElement(Clay_ElementId elementId, Clay_LayoutConfig *layoutConfig, Clay_ScrollElementConfig *scrollConfig) { + Clay_LayoutElement *scrollElement = Clay__OpenElement(elementId, CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ .scrollElementConfig = scrollConfig }); Clay__int32_tArray_Add(&Clay__openClipElementStack, (int)scrollElement->id); Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL; for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); - if (id == mapping->elementId) { + if (elementId.id == mapping->elementId) { scrollOffset = mapping; scrollOffset->layoutElement = scrollElement; scrollOffset->openThisFrame = true; } } if (!scrollOffset) { - Clay__ScrollContainerDataInternalArray_Add(&Clay__scrollContainerDatas, (Clay__ScrollContainerDataInternal){.elementId = id, .layoutElement = scrollElement, .scrollOrigin = {-1,-1}, .openThisFrame = true}); + Clay__ScrollContainerDataInternalArray_Add(&Clay__scrollContainerDatas, (Clay__ScrollContainerDataInternal){.elementId = elementId.id, .layoutElement = scrollElement, .scrollOrigin = {-1,-1}, .openThisFrame = true}); } - return scrollElement; } -Clay_LayoutElement *Clay__OpenFloatingElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingElementConfig) { +void Clay__OpenFloatingElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingElementConfig) { Clay_LayoutElement *parent = Clay__openLayoutElement; - if (floatingElementConfig->parentId > 0) { + uint32_t originalParentId = floatingElementConfig->parentId; + if (floatingElementConfig->parentId == 0) { + Clay_FloatingElementConfig newConfig = *floatingElementConfig; + newConfig.parentId = Clay__openLayoutElement->id; + floatingElementConfig = Clay__FloatingElementConfigArray_Add(&Clay__floatingElementConfigs, newConfig); + } else { Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId); if (!parentItem) { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Clay Warning: Couldn't find parent container to attach floating container to.")); + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Clay Warning: Couldn't find parent container to attach floating container to.") }); } else { parent = parentItem->layoutElement; } } Clay__OpenElementWithParent(id, CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER, layoutConfig, (Clay_ElementConfigUnion) { .floatingElementConfig = floatingElementConfig }); Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, (Clay__LayoutElementTreeRoot) { - .layoutElement = Clay__openLayoutElement, + .layoutElementIndex = Clay__layoutElements.length - 1, .parentId = parent->id, .zIndex = floatingElementConfig->zIndex, - .clipElementId = Clay__openClipElementStack.length > 0 ? Clay__int32_tArray_Get(&Clay__openClipElementStack, (int)Clay__openClipElementStack.length - 1) : 0, + .clipElementId = originalParentId == 0 ? (Clay__openClipElementStack.length > 0 ? Clay__int32_tArray_Get(&Clay__openClipElementStack, (int)Clay__openClipElementStack.length - 1) : 0) : 0, }); - return Clay__openLayoutElement; } void Clay__AttachContainerChildren() { @@ -1297,7 +1523,8 @@ void Clay__AttachContainerChildren() { if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { for (int i = 0; i < Clay__openLayoutElement->children.length; i++) { - Clay_LayoutElement *child = *Clay__LayoutElementPointerArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - Clay__openLayoutElement->children.length + i); + int32_t childIndex = Clay__int32_tArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - Clay__openLayoutElement->children.length + i); + Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&Clay__layoutElements, childIndex); Clay__openLayoutElement->dimensions.width += child->dimensions.width; Clay__openLayoutElement->dimensions.height = CLAY__MAX(Clay__openLayoutElement->dimensions.height, child->dimensions.height + layoutConfig->padding.y * 2); // Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents @@ -1307,7 +1534,7 @@ void Clay__AttachContainerChildren() { if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->vertical) { Clay__openLayoutElement->minDimensions.height = CLAY__MAX(Clay__openLayoutElement->minDimensions.height, child->minDimensions.height + layoutConfig->padding.y * 2); } - Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildren, child); + Clay__int32_tArray_Add(&Clay__layoutElementChildren, childIndex); } float childGap = (float)(CLAY__MAX(Clay__openLayoutElement->children.length - 1, 0) * layoutConfig->childGap); Clay__openLayoutElement->dimensions.width += childGap; @@ -1315,7 +1542,8 @@ void Clay__AttachContainerChildren() { } else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) { for (int i = 0; i < Clay__openLayoutElement->children.length; i++) { - Clay_LayoutElement *child = *Clay__LayoutElementPointerArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - Clay__openLayoutElement->children.length + i); + int32_t childIndex = Clay__int32_tArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - Clay__openLayoutElement->children.length + i); + Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&Clay__layoutElements, childIndex); Clay__openLayoutElement->dimensions.height += child->dimensions.height; Clay__openLayoutElement->dimensions.width = CLAY__MAX(Clay__openLayoutElement->dimensions.width, child->dimensions.width + layoutConfig->padding.x * 2); // Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents @@ -1325,7 +1553,7 @@ void Clay__AttachContainerChildren() { if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->horizontal) { Clay__openLayoutElement->minDimensions.width = CLAY__MAX(Clay__openLayoutElement->minDimensions.width, child->minDimensions.width + layoutConfig->padding.x * 2); } - Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildren, child); + Clay__int32_tArray_Add(&Clay__layoutElementChildren, childIndex); } float childGap = (float)(CLAY__MAX(Clay__openLayoutElement->children.length - 1, 0) * layoutConfig->childGap); Clay__openLayoutElement->dimensions.height += childGap; @@ -1354,14 +1582,14 @@ void Clay__CloseElement() { Clay__openLayoutElement = *Clay__LayoutElementPointerArray_Get(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1); } -void Clay__OpenTextElement(int id, Clay_String text, Clay_TextElementConfig *textConfig) { +void Clay__OpenTextElement(Clay_ElementId id, Clay_String text, Clay_TextElementConfig *textConfig) { Clay_LayoutElement *internalElement = Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_TEXT, &CLAY_LAYOUT_DEFAULT, (Clay_ElementConfigUnion) { .textElementConfig = textConfig }); Clay_Dimensions textMeasured = Clay__MeasureTextCached(&text, textConfig); internalElement->dimensions.width = textMeasured.width; internalElement->dimensions.height = textMeasured.height; internalElement->text = text; internalElement->minDimensions = (Clay_Dimensions) { .width = textMeasured.height, .height = textMeasured.height }; // TODO not sure this is the best way to decide min width for text - Clay__LayoutElementPointerArray_Add(&Clay__textElementPointers, internalElement); + Clay__TextElementDataArray_Add(&Clay__textElementData, (Clay__TextElementData) { .preferredDimensions = textMeasured, .elementIndex = Clay__layoutElements.length - 1}); Clay__CloseElement(); } @@ -1384,9 +1612,9 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { // Ephemeral Memory - reset every frame Clay__internalArena.nextAllocation = Clay__arenaResetOffset; - Clay__layoutElementChildrenBuffer = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementChildrenBuffer = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__layoutElements = Clay_LayoutElementArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay_warnings = Clay__StringArray_Allocate_Arena(100, arena); + Clay_warnings = Clay__WarningArray_Allocate_Arena(100, arena); Clay__layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__rectangleElementConfigs = Clay__RectangleElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); @@ -1397,17 +1625,20 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { Clay__customElementConfigs = Clay__CustomElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementIdStrings = Clay__StringArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementChildren = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementChildren = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__openLayoutElementStack = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__textElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__textElementData = Clay__TextElementDataArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__imageElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementReusableBuffer = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementReusableBuffer = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); // TODO convert this to indexes instead of pointers Clay__renderCommands = Clay_RenderCommandArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__treeNodeVisited = Clay__BoolArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__treeNodeVisited.length = Clay__treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list Clay__openClipElementStack = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__reusableElementIndexBuffer = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__dynamicStringData = Clay__CharArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); } void Clay__InitializePersistentMemory(Clay_Arena *arena) { @@ -1416,7 +1647,8 @@ void Clay__InitializePersistentMemory(Clay_Arena *arena) { Clay__layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__measureTextHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__pointerOverIds = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__pointerOverIds = Clay__ElementIdArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__debugElementData = Clay__DebugElementDataArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__arenaResetOffset = arena->nextAllocation; } @@ -1481,13 +1713,13 @@ float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay } void Clay__SizeContainersAlongAxis(bool xAxis) { - Clay__LayoutElementPointerArray bfsBuffer = Clay__layoutElementChildrenBuffer; + Clay__int32_tArray bfsBuffer = Clay__layoutElementChildrenBuffer; Clay__LayoutElementPointerArray resizableContainerBuffer = Clay__openLayoutElementStack; for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) { bfsBuffer.length = 0; Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); - Clay_LayoutElement *rootElement = root->layoutElement; - Clay__LayoutElementPointerArray_Add(&bfsBuffer, root->layoutElement); + Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, (int)root->layoutElementIndex); + Clay__int32_tArray_Add(&bfsBuffer, (int32_t)root->layoutElementIndex); // Size floating containers to their parents if (rootElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER) { @@ -1495,10 +1727,10 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { if (parentItem) { Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement; if (rootElement->layoutConfig->sizing.width.type == CLAY__SIZING_TYPE_GROW) { - rootElement->dimensions.width = parentLayoutElement->dimensions.width - (float)parentLayoutElement->layoutConfig->padding.x * 2; + rootElement->dimensions.width = parentLayoutElement->dimensions.width; } if (rootElement->layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_GROW) { - rootElement->dimensions.height = parentLayoutElement->dimensions.height - (float)parentLayoutElement->layoutConfig->padding.x * 2; + rootElement->dimensions.height = parentLayoutElement->dimensions.height; } } } @@ -1507,7 +1739,8 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.sizeMinMax.min), rootElement->layoutConfig->sizing.height.sizeMinMax.max); for (int i = 0; i < bfsBuffer.length; ++i) { - Clay_LayoutElement *parent = *Clay__LayoutElementPointerArray_Get(&bfsBuffer, i); + int32_t parentIndex = Clay__int32_tArray_Get(&bfsBuffer, i); + Clay_LayoutElement *parent = Clay_LayoutElementArray_Get(&Clay__layoutElements, parentIndex); Clay_LayoutConfig *parentStyleConfig = parent->layoutConfig; float parentSize = xAxis ? parent->dimensions.width : parent->dimensions.height; float parentPadding = (float)(xAxis ? parent->layoutConfig->padding.x : parent->layoutConfig->padding.y); @@ -1517,15 +1750,16 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { float parentChildGap = parentStyleConfig->childGap; for (int childOffset = 0; childOffset < parent->children.length; childOffset++) { - Clay_LayoutElement *childElement = parent->children.elements[childOffset]; + int32_t childElementIndex = parent->children.elements[childOffset]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, childElementIndex); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height; if (childElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT && childElement->children.length > 0) { - Clay__LayoutElementPointerArray_Add(&bfsBuffer, childElement); + Clay__int32_tArray_Add(&bfsBuffer, childElementIndex); } - if (childSizing.type != CLAY__SIZING_TYPE_PERCENT) { + if (childSizing.type != CLAY__SIZING_TYPE_PERCENT && (childElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT || (childElement->elementConfig.textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS))) { Clay__LayoutElementPointerArray_Add(&resizableContainerBuffer, childElement); } @@ -1542,7 +1776,8 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { // Expand percentage containers to size for (int childOffset = 0; childOffset < parent->children.length; childOffset++) { - Clay_LayoutElement *childElement = parent->children.elements[childOffset]; + int32_t childElementIndex = parent->children.elements[childOffset]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, childElementIndex); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; if (childSizing.type == CLAY__SIZING_TYPE_PERCENT) { @@ -1607,14 +1842,15 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { } } -void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { +void Clay__CalculateFinalLayout() { // Calculate sizing along the X axis Clay__SizeContainersAlongAxis(true); // Wrap text - uint32_t originalTextLayoutElementDataLength = Clay__textElementPointers.length; + uint32_t originalTextLayoutElementDataLength = Clay__textElementData.length; for (int i = 0; i < originalTextLayoutElementDataLength; ++i) { - Clay_LayoutElement *containerElement = *Clay__LayoutElementPointerArray_Get(&Clay__textElementPointers, i); + Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&Clay__textElementData, i); + Clay_LayoutElement *containerElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, (int)textElementData->elementIndex); Clay_String text = containerElement->text; Clay_TextElementConfig *textConfig = containerElement->elementConfig.textElementConfig; containerElement->elementType = CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER; @@ -1622,19 +1858,33 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { containerElement->layoutConfig = Clay__LayoutConfigArray_Add(&Clay__layoutConfigs, *containerElement->layoutConfig); containerElement->layoutConfig->layoutDirection = CLAY_TOP_TO_BOTTOM; containerElement->layoutConfig->childGap = textConfig->lineSpacing; + containerElement->children = (Clay__LayoutElementChildren) { // Note: this overwrites the text property + .length = 0, + .elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length] + }; + // Short circuit all wrap calculations if wrap mode is none + if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE || (containerElement->dimensions.width == textElementData->preferredDimensions.width)) { + Clay_LayoutElement *newTextLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, (Clay_LayoutElement) { + .id = Clay__RehashWithNumber(containerElement->id, containerElement->children.length), + .elementType = CLAY__LAYOUT_ELEMENT_TYPE_TEXT, + .text = text, + .layoutConfig = &CLAY_LAYOUT_DEFAULT, + .elementConfig.textElementConfig = containerElement->elementConfig.textElementConfig, + .dimensions = textElementData->preferredDimensions, + }); + containerElement->children.length++; + Clay__int32_tArray_Add(&Clay__layoutElementChildren, (int32_t)Clay__layoutElements.length - 1); + continue; + } containerElement->dimensions.height = 0; float fontSize = containerElement->elementConfig.textElementConfig->fontSize; int lineStartIndex = 0; int wordStartIndex = 0; int wordEndIndex = 0; - containerElement->children = (Clay__LayoutElementChildren) { // Note: this overwrites the text property - .length = 0, - .elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length] - }; Clay_Dimensions lineDimensions = (Clay_Dimensions){}; float spaceWidth = Clay__MeasureText(&CLAY__SPACECHAR, textConfig).width; // todo may as well cache it somewhere - while (wordStartIndex < text.length) { - if (text.chars[wordEndIndex] == ' ' || text.chars[wordEndIndex] == '\n' || wordEndIndex == text.length) { + while (wordStartIndex <= text.length) { + if (text.chars[wordEndIndex] == ' ' || (text.chars[wordEndIndex] == '\n' && textConfig->wrapMode <= CLAY_TEXT_WRAP_NEWLINES) || wordEndIndex == text.length) { Clay_String stringToRender = (Clay_String) { .length = wordEndIndex - lineStartIndex, .chars = text.chars + lineStartIndex }; Clay_String wordToMeasure = (Clay_String) { .length = wordEndIndex - wordStartIndex, .chars = text.chars + wordStartIndex }; // Clip off trailing spaces and newline characters @@ -1643,7 +1893,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { lineDimensions.height = wordDimensions.height; bool isOverlappingBoundaries = (lineDimensions.width - spaceWidth) > containerElement->dimensions.width + 0.01f; // Epsilon for floating point inaccuracy of adding components // Need to wrap - if (isOverlappingBoundaries) { + if (isOverlappingBoundaries && textConfig->wrapMode == CLAY_TEXT_WRAP_WORDS) { lineDimensions.width -= spaceWidth; // We can wrap at the most recent word start if (wordStartIndex != lineStartIndex) { @@ -1682,7 +1932,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { containerElement->dimensions.height += lineDimensions.height + (float)(containerElement->children.length > 0 ? textConfig->lineSpacing : 0); containerElement->children.length++; lineDimensions = (Clay_Dimensions) {}; - Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildren, newTextLayoutElement); + Clay__int32_tArray_Add(&Clay__layoutElementChildren, (int32_t)Clay__layoutElements.length - 1); } else { // In the middle of a word wordEndIndex++; @@ -1702,23 +1952,23 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { dfsBuffer.length = 0; for (int i = 0; i < Clay__layoutElementTreeRoots.length; ++i) { Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, i); - Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = root->layoutElement }); + Clay__treeNodeVisited.internalArray[dfsBuffer.length] = false; + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, (int)root->layoutElementIndex) }); } - Clay__treeNodeVisited.internalArray[0] = false; while (dfsBuffer.length > 0) { Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1); Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement; if (!Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) { Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; // If the element has no children or is the container for a text element, don't bother inspecting it - if (currentElement->children.length == 0 || (currentElement->children.length > 0 && currentElement->children.elements[0]->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT)) { + if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT || currentElement->children.length == 0 || Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[0])->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { dfsBuffer.length--; continue; } // Add the children to the DFS buffer (needs to be pushed in reverse so that stack traversal is in correct layout order) for (int i = 0; i < currentElement->children.length; i++) { Clay__treeNodeVisited.internalArray[dfsBuffer.length] = false; - Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = currentElement->children.elements[i] }); + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]) }); // TODO fix before release } continue; } @@ -1732,7 +1982,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { // Resize any parent containers that have grown in height along their non layout axis for (int j = 0; j < currentElement->children.length; ++j) { - Clay_LayoutElement *childElement = currentElement->children.elements[j]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[j]); float childHeightWithPadding = CLAY__MAX(childElement->dimensions.height + layoutConfig->padding.y * 2, currentElement->dimensions.height); currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(childHeightWithPadding, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); } @@ -1740,7 +1990,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { // Resizing along the layout axis float contentHeight = (float)layoutConfig->padding.y * 2; for (int j = 0; j < currentElement->children.length; ++j) { - Clay_LayoutElement *childElement = currentElement->children.elements[j]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[j]); contentHeight += childElement->dimensions.height; } contentHeight += (float)(CLAY__MAX(currentElement->children.length - 1, 0) * layoutConfig->childGap); @@ -1751,25 +2001,19 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { // Calculate sizing along the Y axis Clay__SizeContainersAlongAxis(false); - // layoutElementsHashMap has non-linear access pattern so just resetting .length won't zero out the data. - // Need to zero it all out here - for (int i = 0; i < Clay__layoutElementsHashMap.capacity; ++i) { - Clay__layoutElementsHashMap.internalArray[i] = -1; - } - Clay__layoutElementsHashMapInternal.length = 0; - // Calculate final positions and generate render commands Clay__renderCommands.length = 0; dfsBuffer.length = 0; for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) { dfsBuffer.length = 0; Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); + Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, (int)root->layoutElementIndex); Clay_Vector2 rootPosition = (Clay_Vector2) {}; Clay_LayoutElementHashMapItem *parentHashMapItem = Clay__GetHashMapItem(root->parentId); // Position root floating containers - if (parentHashMapItem) { - Clay_FloatingElementConfig *config = root->layoutElement->elementConfig.floatingElementConfig; - Clay_Dimensions rootDimensions = root->layoutElement->dimensions; + if (rootElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER && parentHashMapItem) { + Clay_FloatingElementConfig *config = rootElement->elementConfig.floatingElementConfig; + Clay_Dimensions rootDimensions = rootElement->dimensions; Clay_BoundingBox parentBoundingBox = parentHashMapItem->boundingBox; // Set X position Clay_Vector2 targetAttachPosition = (Clay_Vector2){}; @@ -1825,13 +2069,13 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { Clay_LayoutElementHashMapItem *clipHashMapItem = Clay__GetHashMapItem(root->clipElementId); if (clipHashMapItem) { Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { - .id = Clay__RehashWithNumber(root->layoutElement->id, 10), // TODO need a better strategy for managing derived ids + .id = Clay__RehashWithNumber(rootElement->id, 10), // TODO need a better strategy for managing derived ids .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START, .boundingBox = clipHashMapItem->boundingBox, }); } } - Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = root->layoutElement, .position = rootPosition, .nextChildOffset = (Clay_Vector2) { .x = (float)root->layoutElement->layoutConfig->padding.x, .y = (float)root->layoutElement->layoutConfig->padding.y } }); + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = rootElement, .position = rootPosition, .nextChildOffset = (Clay_Vector2) { .x = (float)rootElement->layoutConfig->padding.x, .y = (float)rootElement->layoutConfig->padding.y } }); Clay__treeNodeVisited.internalArray[0] = false; while (dfsBuffer.length > 0) { @@ -1889,14 +2133,18 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { .config = currentElement->elementConfig }; - Clay_LayoutElementHashMapItem *hashMapItem = Clay__AddHashMapItem(currentElement->id, currentElement); + Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(currentElement->id); if (hashMapItem) { hashMapItem->boundingBox = renderCommand.boundingBox; } + #ifndef CLAY_DISABLE_CULLING // Culling - Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow - bool offscreen = currentElementBoundingBox.x > (float)screenWidth || currentElementBoundingBox.y > (float)screenHeight || currentElementBoundingBox.x + currentElementBoundingBox.width < 0 || currentElementBoundingBox.y + currentElementBoundingBox.height < 0; + bool offscreen = currentElementBoundingBox.x > (float)Clay__layoutDimensions.width || currentElementBoundingBox.y > (float)Clay__layoutDimensions.height || currentElementBoundingBox.x + currentElementBoundingBox.width < 0 || currentElementBoundingBox.y + currentElementBoundingBox.height < 0; bool shouldRender = !offscreen; + #elif + bool shouldRender = true; + #endif switch (renderCommand.commandType) { case CLAY_RENDER_COMMAND_TYPE_NONE: { shouldRender = false; @@ -1928,7 +2176,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { Clay_Dimensions contentSize = (Clay_Dimensions) {0,0}; if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { for (int i = 0; i < currentElement->children.length; ++i) { - Clay_LayoutElement *childElement = currentElement->children.elements[i]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); contentSize.width += childElement->dimensions.width; contentSize.height = CLAY__MAX(contentSize.height, childElement->dimensions.height); } @@ -1942,7 +2190,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { currentElementTreeNode->nextChildOffset.x += extraSpace; } else { for (int i = 0; i < currentElement->children.length; ++i) { - Clay_LayoutElement *childElement = currentElement->children.elements[i]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); contentSize.width = CLAY__MAX(contentSize.width, childElement->dimensions.width); contentSize.height += childElement->dimensions.height; } @@ -1970,11 +2218,15 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { // Borders between elements are expressed as additional rectangle render commands } else if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER) { Clay_BoundingBox currentElementBoundingBox = (Clay_BoundingBox) { currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height }; - bool offscreen = currentElementBoundingBox.x > (float)screenWidth || currentElementBoundingBox.y > (float)screenHeight || currentElementBoundingBox.x + currentElementBoundingBox.width < 0 || currentElementBoundingBox.y + currentElementBoundingBox.height < 0; + #ifndef CLAY_DISABLE_CULLING + bool offscreen = currentElementBoundingBox.x > (float)Clay__layoutDimensions.width || currentElementBoundingBox.y > (float)Clay__layoutDimensions.height || currentElementBoundingBox.x + currentElementBoundingBox.width < 0 || currentElementBoundingBox.y + currentElementBoundingBox.height < 0; if (offscreen) { + #endif dfsBuffer.length--; continue; + #ifndef CLAY_DISABLE_CULLING } + #endif Clay_BorderElementConfig *borderConfig = currentElement->elementConfig.borderElementConfig; Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { @@ -1989,7 +2241,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { Clay_Vector2 borderOffset = { (float)layoutConfig->padding.x, (float)layoutConfig->padding.y }; if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { for (int i = 0; i < currentElement->children.length; ++i) { - Clay_LayoutElement *childElement = currentElement->children.elements[i]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); if (i > 0) { Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { .id = Clay__RehashWithNumber(currentElement->id, 5 + i), @@ -2002,7 +2254,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { } } else { for (int i = 0; i < currentElement->children.length; ++i) { - Clay_LayoutElement *childElement = currentElement->children.elements[i]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); if (i > 0) { Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { .id = Clay__RehashWithNumber(currentElement->id, 5 + i), @@ -2023,7 +2275,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { // Add children to the DFS buffer if (currentElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { for (int i = 0; i < currentElement->children.length; ++i) { - Clay_LayoutElement *childElement = currentElement->children.elements[i]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); // Alignment along non layout axis if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { currentElementTreeNode->nextChildOffset.y = currentElement->layoutConfig->padding.y; @@ -2068,11 +2320,631 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { } if (root->clipElementId) { - Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { .id = Clay__RehashWithNumber(root->layoutElement->id, 11), .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END }); + Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { .id = Clay__RehashWithNumber(rootElement->id, 11), .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END }); } } } +typedef struct +{ + // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. + // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. + Clay_Vector2 *scrollPosition; + Clay_Dimensions scrollContainerDimensions; + Clay_Dimensions contentDimensions; + Clay_ScrollElementConfig config; + // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. + bool found; +} Clay_ScrollContainerData; + +Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id); + +#pragma region DebugTools +const Clay_Color CLAY__DEBUGVIEW_COLOR_1 = (Clay_Color) {58, 56, 52, 255}; +const Clay_Color CLAY__DEBUGVIEW_COLOR_2 = (Clay_Color) {62, 60, 58, 255}; +const Clay_Color CLAY__DEBUGVIEW_COLOR_3 = (Clay_Color) {141, 133, 135, 255}; +const Clay_Color CLAY__DEBUGVIEW_COLOR_4 = (Clay_Color) {238, 226, 231, 255}; +const Clay_Color CLAY__DEBUGVIEW_COLOR_SELECTED_ROW = (Clay_Color) {102, 80, 78, 255}; +const int CLAY__DEBUGVIEW_ROW_HEIGHT = 30; +const int CLAY__DEBUGVIEW_OUTER_PADDING = 10; +const int CLAY__DEBUGVIEW_INDENT_WIDTH = 16; +Clay_TextElementConfig Clay__DebugView_TextNameConfig = (Clay_TextElementConfig) {.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_4, .wrapMode = CLAY_TEXT_WRAP_NONE }; +Clay_LayoutConfig Clay__DebugView_ScrollViewItemLayoutConfig = (Clay_LayoutConfig) {}; + +Clay_String Clay__IntToString(int integer) { + if (integer == 0) { + return (Clay_String) { .length = 1, .chars = "0" }; + } + char *chars = (char *)(Clay__dynamicStringData.internalArray + Clay__dynamicStringData.length); + int length = 0; + int sign = integer; + + if (integer < 0) { + integer = -integer; + } + while (integer > 0) { + chars[length++] = (char)(integer % 10 + '0'); + integer /= 10; + } + + if (sign < 0) { + chars[length++] = '-'; + } + + // Reverse the string to get the correct order + for (int j = 0, k = length - 1; j < k; j++, k--) { + char temp = chars[j]; + chars[j] = chars[k]; + chars[k] = temp; + } + Clay__dynamicStringData.length += length; + return (Clay_String) { .length = length, .chars = chars }; +} + +typedef struct +{ + uint32_t rowCount; + uint32_t selectedElementRowIndex; +} Clay__RenderDebugLayoutData; + +// Returns row count +Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialRootsLength, int32_t highlightedRowIndex) { + Clay_ElementId outerId = CLAY_ID("Clay__DebugView_ElementOuter"); + Clay_ElementId border = CLAY_ID("Clay__DebugView_ElementOuterBorder"); + Clay_ElementId inner = CLAY_ID("Clay__DebugView_ElementInner"); + Clay_ElementId paddingOffset = CLAY_ID("Clay__DebugView_ElementPaddingOffset"); + Clay_ElementId spacerId = CLAY_ID("Clay__DebugView_ElementSpacer"); + Clay_ElementId outerHighlightId = CLAY_ID("Clay__DebugView_OuterHighlight"); + Clay_ElementId collapseIconButton = CLAY_ID("Clay__DebugView_CollapseIconButton"); + Clay_ElementId collapseIcon = CLAY_ID("Clay__DebugView_CollapseIcon"); + Clay_ElementId elementNameId = CLAY_ID("Clay__DebugView_ElementName"); + Clay_ElementId elementDuplicateWarning = CLAY_ID("Clay__DebugView_ElementDuplicateWarning"); + Clay_ElementId elementOffscreenBorderId = CLAY_ID("Clay__DebugView_ElementOffscreenBorder"); + Clay_ElementId elementTextSpacer = CLAY_ID("Clay__DebugView_ElementTextSpacer"); + Clay_ElementId elementTypeBorderId = CLAY_ID("Clay__DebugView_ElementTextBorder"); + Clay_ElementId elementTypeInnerRectId = CLAY_ID("Clay__DebugView_ElementTextInnerRect"); + Clay_ElementId elementTypeTextId = CLAY_ID("Clay__DebugView_ElementTypeText"); + Clay_ElementId textContentsOuterId = CLAY_ID("Clay__DebugView_ElementTextContentsOuter"); + Clay_ElementId textContentsId = CLAY_ID("Clay__DebugView_ElementTextContents"); + Clay__int32_tArray dfsBuffer = Clay__reusableElementIndexBuffer; + Clay__DebugView_ScrollViewItemLayoutConfig = (Clay_LayoutConfig) { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT) }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, .childGap = 6}; + Clay__RenderDebugLayoutData layoutData = {}; + + uint32_t highlightedElementId = 0; + + for (int rootIndex = 0; rootIndex < initialRootsLength; ++rootIndex) { + dfsBuffer.length = 0; + Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); + Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex); + Clay__treeNodeVisited.internalArray[0] = false; + if (rootIndex > 0) { + CLAY_CONTAINER(CLAY_IDI("Clay__DebugView_EmptyRowOuter", rootIndex), CLAY_LAYOUT(.sizing = {.width = CLAY_SIZING_GROW()}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2}), { + CLAY_BORDER_CONTAINER(CLAY_IDI("Clay__DebugView_EmptyRow", rootIndex), CLAY_LAYOUT(.sizing = {.height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT), .width = CLAY_SIZING_GROW()}), CLAY_BORDER_CONFIG(.top = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 } ), {}); + }); + layoutData.rowCount++; + } + while (dfsBuffer.length > 0) { + uint32_t currentElementIndex = Clay__int32_tArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1); + Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, (int)currentElementIndex); + if (Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) { + if (currentElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT && currentElement->children.length > 0) { + Clay__CloseElementWithChildren(); + Clay__CloseElementWithChildren(); + Clay__CloseElementWithChildren(); + } + dfsBuffer.length--; + continue; + } + + if (highlightedRowIndex == layoutData.rowCount) { + if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + Clay__debugSelectedElementId = currentElement->id; + } + highlightedElementId = currentElement->id; + } + Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; + Clay_String toPrint = Clay__layoutElementIdStrings.internalArray[currentElementIndex]; + Clay_ElementId outerHash = Clay__Rehash(outerId, currentElement->id); + Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id); + Clay_BoundingBox currentElementBoundingBox = currentElementData->boundingBox; + #ifndef CLAY_DISABLE_CULLING + bool offscreen = currentElementBoundingBox.x > (float)Clay__layoutDimensions.width || currentElementBoundingBox.y > (float)Clay__layoutDimensions.height || currentElementBoundingBox.x + currentElementBoundingBox.width < 0 || currentElementBoundingBox.y + currentElementBoundingBox.height < 0; + #elif + bool offscreen = false; + #endif + Clay_Color outerColor = {0,0,0,0}; + if (Clay__debugSelectedElementId == currentElement->id) { + layoutData.selectedElementRowIndex = layoutData.rowCount; + } + CLAY_CONTAINER(outerHash, &Clay__DebugView_ScrollViewItemLayoutConfig, { + if (!(currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT || currentElement->children.length == 0)) { + CLAY_BORDER_CONTAINER(Clay__Rehash(collapseIconButton, currentElement->id), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(1, CLAY__DEBUGVIEW_COLOR_3, 4), { + CLAY_TEXT(Clay__Rehash(collapseIcon, currentElement->id), (currentElementData && currentElementData->debugData->collapsed) ? CLAY_STRING("+") : CLAY_STRING("-"), CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_4)); + }); + } else { + CLAY_CONTAINER(Clay__Rehash(collapseIconButton, currentElement->id), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}), { + CLAY_RECTANGLE(Clay__Rehash(collapseIcon, currentElement->id), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(8), CLAY_SIZING_FIXED(8)}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3, .cornerRadius = CLAY_CORNER_RADIUS(2)), {}); + }); + } + CLAY_CONTAINER(Clay__Rehash(outerHighlightId, currentElement->id), CLAY_LAYOUT(.padding = {0,4}, .childGap = 12, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }), { + if (currentElementData) { + if (currentElementData->debugData->collision) { + CLAY_BORDER_CONTAINER(Clay__Rehash(elementOffscreenBorderId, currentElement->id), CLAY_LAYOUT(.padding = { 8, 2 }), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(1, ((Clay_Color){177, 147, 8, 255}), 4), { + CLAY_TEXT(Clay__Rehash(elementDuplicateWarning, currentElement->id), CLAY_STRING("Duplicate ID"), CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_3)); + }); + } + + if (offscreen) { + CLAY_BORDER_CONTAINER(Clay__Rehash(elementOffscreenBorderId, currentElement->id), CLAY_LAYOUT(.padding = { 8, 2 }), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(1, CLAY__DEBUGVIEW_COLOR_3, 4), { + CLAY_TEXT(Clay__Rehash(elementDuplicateWarning, currentElement->id), CLAY_STRING("Offscreen"), CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_3)); + }); + } + } + CLAY_TEXT(Clay__Rehash(elementNameId, currentElement->id), Clay__layoutElementIdStrings.internalArray[currentElementIndex], offscreen ? CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_3) : &Clay__DebugView_TextNameConfig); + Clay_String elementTypeName = (Clay_String){}; + Clay_Color elementTypeColor = (Clay_Color){}; + switch (currentElement->elementType) { + case CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE: elementTypeName = CLAY_STRING("Rectangle"); elementTypeColor = ((Clay_Color) {243,134,48,255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER: elementTypeName = CLAY_STRING("Container"); elementTypeColor = ((Clay_Color) {53,92,125, 255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_TEXT: elementTypeName = CLAY_STRING("Text"); elementTypeColor = ((Clay_Color) {105,210,231,255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_IMAGE: elementTypeName = CLAY_STRING("Image"); elementTypeColor = ((Clay_Color) {121,189,154,255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER: elementTypeName = CLAY_STRING("Floating"); elementTypeColor = ((Clay_Color) {250,105,0,255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER: elementTypeName = CLAY_STRING("Scroll"); elementTypeColor = ((Clay_Color) {242,196,90,255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER: elementTypeName = CLAY_STRING("Border"); elementTypeColor = ((Clay_Color) {108,91,123, 255}); break; + case CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM: elementTypeName = CLAY_STRING("Custom"); elementTypeColor = ((Clay_Color) {11,72,107,255}); break; + } + Clay_Color backgroundColor = elementTypeColor; + backgroundColor.a = 90; + CLAY_BORDER_CONTAINER(Clay__Rehash(elementTypeBorderId, currentElement->id), &CLAY_LAYOUT_DEFAULT, CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(1, elementTypeColor, 4), { + CLAY_RECTANGLE(Clay__Rehash(elementTypeInnerRectId, currentElement->id), CLAY_LAYOUT(.padding = { 8, 2 }), CLAY_RECTANGLE_CONFIG(.color = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4)), { + CLAY_TEXT(Clay__Rehash(elementTypeTextId, currentElement->id), elementTypeName, CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4)); + }); + }); + }); + }); + + // Render the text contents below the element as a non-interactive row + if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { + layoutData.rowCount++; + Clay_TextElementConfig *rawTextConfig = offscreen ? CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_3) : &Clay__DebugView_TextNameConfig; + CLAY_RECTANGLE(Clay__Rehash(textContentsOuterId, currentElement->id), CLAY_LAYOUT(.sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }), CLAY_RECTANGLE_CONFIG(), { + CLAY_CONTAINER(Clay__Rehash(elementTextSpacer, currentElement->id), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16)}), {}); + CLAY_TEXT(CLAY_IDI("Clay__DebugView_TextOpenQuote", currentElement->id), CLAY_STRING("\""), rawTextConfig); + CLAY_TEXT(Clay__Rehash(textContentsId, currentElement->id), currentElement->text.length > 40 ? ((Clay_String) { .chars = currentElement->text.chars, .length = 40 }) : currentElement->text, rawTextConfig); + if (currentElement->text.length > 40) { + CLAY_TEXT(CLAY_IDI("Clay__DebugView_TextEllipsis", currentElement->id), CLAY_STRING("..."), rawTextConfig); + } + CLAY_TEXT(CLAY_IDI("Clay__DebugView_TextCloseQuote", currentElement->id), CLAY_STRING("\""), rawTextConfig); + }); + } else if (currentElement->children.length > 0) { + Clay__OpenContainerElement(Clay__Rehash(paddingOffset, currentElement->id), CLAY_LAYOUT(.padding = { 8 })); + Clay__OpenBorderElement(Clay__Rehash(border, currentElement->id), CLAY_LAYOUT(.layoutDirection = CLAY_LEFT_TO_RIGHT), CLAY_BORDER_CONFIG(.left = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = 1})); + CLAY_CONTAINER(Clay__Rehash(spacerId, currentElement->id), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED( CLAY__DEBUGVIEW_INDENT_WIDTH)}, .childAlignment = { .x = CLAY_ALIGN_X_RIGHT }), {}); + Clay__OpenContainerElement(Clay__Rehash(inner, currentElement->id), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM)); + } + + layoutData.rowCount++; + if (!(currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT || (currentElementData && currentElementData->debugData->collapsed))) { + for (int i = currentElement->children.length - 1; i >= 0; --i) { + Clay__int32_tArray_Add(&dfsBuffer, currentElement->children.elements[i]); + Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked + } + } + } + } + + if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + if (Clay__pointerInfo.position.x > Clay__layoutDimensions.width - (float)Clay__debugViewWidth && Clay__pointerInfo.position.x < Clay__layoutDimensions.width && Clay__pointerInfo.position.y > 0 && Clay__pointerInfo.position.y < Clay__layoutDimensions.height) { + for (int i = (int)Clay__pointerOverIds.length - 1; i >= 0; i--) { + Clay_ElementId *elementId = Clay__ElementIdArray_Get(&Clay__pointerOverIds, i); + if (elementId->baseId == collapseIconButton.baseId) { + Clay_LayoutElementHashMapItem *highlightedItem = Clay__GetHashMapItem(elementId->offset); + highlightedItem->debugData->collapsed = !highlightedItem->debugData->collapsed; + break; + } + } + } + } + + if (highlightedElementId) { + CLAY_FLOATING_CONTAINER(CLAY_ID("Clay__DebugView_ElementHighlight"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_FLOATING_CONFIG(.zIndex = 65535, .parentId = highlightedElementId), { + CLAY_RECTANGLE(CLAY_ID("Clay__DebugView_ElementHighlightRectangle"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_RECTANGLE_CONFIG(.color = Clay__debugViewHighlightColor), {}); + }); + } + return layoutData; +} + +void Clay__RenderDebugLayoutSizing(Clay_ElementId baseId, Clay_SizingAxis sizing, Clay_TextElementConfig *infoTextConfig) { + Clay_String sizingLabel = CLAY_STRING("GROW"); + if (sizing.type == CLAY__SIZING_TYPE_FIT) { + sizingLabel = CLAY_STRING("FIT"); + } else if (sizing.type == CLAY__SIZING_TYPE_PERCENT) { + sizingLabel = CLAY_STRING("PERCENT"); + } + CLAY_TEXT(Clay__Rehash(baseId, 1), sizingLabel, infoTextConfig); + if (sizing.type == CLAY__SIZING_TYPE_GROW || sizing.type == CLAY__SIZING_TYPE_FIT) { + CLAY_TEXT(Clay__Rehash(baseId, 2), CLAY_STRING("("), infoTextConfig); + if (sizing.sizeMinMax.min != 0) { + CLAY_TEXT(Clay__Rehash(baseId, 3), CLAY_STRING("min: "), infoTextConfig); + CLAY_TEXT(Clay__Rehash(baseId, 4), Clay__IntToString(sizing.sizeMinMax.min), infoTextConfig); + if (sizing.sizeMinMax.max != CLAY__MAXFLOAT) { + CLAY_TEXT(Clay__Rehash(baseId, 5), CLAY_STRING(", "), infoTextConfig); + } + } + if (sizing.sizeMinMax.max != CLAY__MAXFLOAT) { + CLAY_TEXT(Clay__Rehash(baseId, 6), CLAY_STRING("max: "), infoTextConfig); + CLAY_TEXT(Clay__Rehash(baseId, 7), Clay__IntToString(sizing.sizeMinMax.max), infoTextConfig); + } + CLAY_TEXT(Clay__Rehash(baseId, 8), CLAY_STRING(")"), infoTextConfig); + } +} + +void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay_String title) { + CLAY_RECTANGLE(CLAY_IDI("Clay__DebugViewElementConfigItemBorder", 1), CLAY_LAYOUT(.sizing = {.width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1)}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3), {}); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementConfigItemHeader"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING} ), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementConfigTitle", 1), title, CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_4, .wrapMode = CLAY_TEXT_WRAP_NONE)); + CLAY_CONTAINER(CLAY_IDI("Clay__DebugViewElementConfigTitleSpacer", 1), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }), {}); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementConfigTitle", 2), elementId, CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_3, .wrapMode = CLAY_TEXT_WRAP_NONE)); + }); + CLAY_RECTANGLE(CLAY_IDI("Clay__DebugViewElementConfigItemBorder", 2), CLAY_LAYOUT(.sizing = {.width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1)}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3), {}); +} + +void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) { + CLAY_CONTAINER(CLAY_ID_AUTO, CLAY_LAYOUT(.childAlignment = {.y = CLAY_ALIGN_Y_CENTER}), { + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING("{ r: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(color.r), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", g: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(color.g), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", b: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(color.b), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", a: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(color.a), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(" }"), textConfig); + CLAY_CONTAINER(CLAY_ID_AUTO, CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(10) }), {}); + CLAY_BORDER_CONTAINER(CLAY_ID_AUTO, CLAY_LAYOUT(.sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)}), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(1, CLAY__DEBUGVIEW_COLOR_4, 4), { + CLAY_RECTANGLE(CLAY_ID_AUTO, CLAY_LAYOUT(.sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)}), CLAY_RECTANGLE_CONFIG(.cornerRadius = CLAY_CORNER_RADIUS(4), .color = color), {}); + }); + }); +} + +void Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) { + CLAY_CONTAINER(CLAY_ID_AUTO, CLAY_LAYOUT(.childAlignment = {.y = CLAY_ALIGN_Y_CENTER}), { + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING("{ topLeft: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(cornerRadius.topLeft), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", topRight: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(cornerRadius.topRight), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", bottomLeft: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(cornerRadius.bottomLeft), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", bottomRight: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(cornerRadius.bottomRight), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(" }"), textConfig); + }); +} + +void Clay__RenderDebugViewBorder(int index, Clay_Border border, Clay_TextElementConfig *textConfig) { + CLAY_CONTAINER(CLAY_ID_AUTO, CLAY_LAYOUT(.childAlignment = {.y = CLAY_ALIGN_Y_CENTER}), { + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING("{ width: "), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, Clay__IntToString(border.width), textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(", color: "), textConfig); + Clay__RenderDebugViewColor(border.color, textConfig); + CLAY_TEXT(CLAY_ID_AUTO, CLAY_STRING(" }"), textConfig); + }); +} + +void Clay__RenderDebugView() { + Clay_ElementId closeButtonId = CLAY_ID("Clay__DebugViewTopHeaderCloseButtonOuter"); + if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + for (int i = 0; i < Clay__pointerOverIds.length; ++i) { + Clay_ElementId *elementId = Clay__ElementIdArray_Get(&Clay__pointerOverIds, i); + if (elementId->id == closeButtonId.id) { + Clay__debugModeEnabled = false; + return; + } + } + } + + uint32_t initialRootsLength = Clay__layoutElementTreeRoots.length; + uint32_t initialElementsLength = Clay__layoutElements.length; + Clay_TextElementConfig *infoTextConfig = CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_4, .wrapMode = CLAY_TEXT_WRAP_NONE); + Clay_TextElementConfig *infoTitleConfig = CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_3, .wrapMode = CLAY_TEXT_WRAP_NONE); + uint32_t scrollId = CLAY_ID("Clay__DebugViewOuterScrollPane").id; + float scrollYOffset = 0; + for (int i = 0; i < Clay__scrollContainerDatas.length; ++i) { + Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); + if (scrollContainerData->elementId == scrollId) { + scrollYOffset = scrollContainerData->scrollPosition.y; + break; + } + } + int32_t highlightedRow = (int32_t)((Clay__pointerInfo.position.y - scrollYOffset) / (float)CLAY__DEBUGVIEW_ROW_HEIGHT) - 1; + if (Clay__pointerInfo.position.x < Clay__layoutDimensions.width - (float)Clay__debugViewWidth) { + highlightedRow = -1; + } + Clay__RenderDebugLayoutData layoutData = {}; + CLAY_FLOATING_CONTAINER(CLAY_ID("Clay__DebugView"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_FIXED(Clay__debugViewWidth) , CLAY_SIZING_FIXED(Clay__layoutDimensions.height) }), CLAY_FLOATING_CONFIG(.attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_CENTER }), { + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewLeftBorder"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(1), .height = CLAY_SIZING_GROW() }), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3), {}); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewInner"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), { + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewTopHeaderOuter"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_2), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewTopHeaderId", 1), CLAY_STRING("Clay Debug Tools"), infoTextConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewTopHeaderSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }), {}); + CLAY_BORDER_CONTAINER(CLAY_ID("Clay__DebugViewTopHeaderCloseButtonOuter"), &CLAY_LAYOUT_DEFAULT, CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(1, ((Clay_Color){217,91,67,255}), 4), { + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewTopHeaderCloseButtonInner"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10)}, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}), CLAY_RECTANGLE_CONFIG(.color = ((Clay_Color){217,91,67,80})), { + CLAY_TEXT(CLAY_ID("Clay__DebugViewTopHeaderCloseButtonText"), CLAY_STRING("x"), CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_4)); + }); + }); + }); + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewTopHeaderBorder"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(1)}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3), {}); + CLAY_SCROLL_CONTAINER(CLAY_ID("Clay__DebugViewOuterScrollPane"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_SCROLL_CONFIG(.vertical = true, .horizontal = true), { + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewPaneBackground"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_RECTANGLE_CONFIG(.color = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1), { + Clay_ElementId panelContentsId = CLAY_ID("Clay__DebugViewPaneOuter"); + CLAY_FLOATING_CONTAINER(panelContentsId, CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_FLOATING_CONFIG(), { + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewPane"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .padding = {.x = CLAY__DEBUGVIEW_OUTER_PADDING }), { + layoutData = Clay__RenderDebugLayoutElementsList(initialRootsLength, highlightedRow); + }); + }); + float contentWidth = Clay__GetHashMapItem(panelContentsId.id)->layoutElement->dimensions.width; + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewScrollPanelWidth"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_FIXED(contentWidth)}), {}); + for (uint32_t i = 0; i < layoutData.rowCount; i++) { + Clay_Color rowColor = (i & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1; + if (i == layoutData.selectedElementRowIndex) { + rowColor = CLAY__DEBUGVIEW_COLOR_SELECTED_ROW; + } + if (i == highlightedRow) { + rowColor.r *= 1.25; + rowColor.g *= 1.25; + rowColor.b *= 1.25; + } + CLAY_RECTANGLE(CLAY_IDI("Clay__DebugViewStripe", i), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}), CLAY_RECTANGLE_CONFIG(.color = rowColor), {}); + } + }); + }); + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewPanelBorder"), CLAY_LAYOUT(.sizing = {.width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1)}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3), {}); + if (Clay__debugSelectedElementId != 0) { + Clay_LayoutElementHashMapItem *selectedItem = Clay__GetHashMapItem(Clay__debugSelectedElementId); + CLAY_SCROLL_CONTAINER(CLAY_ID("Clay__DebugViewInfoScrollPane"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(300)}, .childGap = 6), CLAY_SCROLL_CONFIG(.vertical = true), { + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewElementInfoPane"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_2), { + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoItemHeader"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoTitleId", 1), CLAY_STRING("Layout Config"), infoTextConfig); + CLAY_CONTAINER(CLAY_IDI("Clay__DebugViewElementInfoTitleSpacer", 1), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }), {}); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoTitle", 2), selectedItem->elementId.stringId, infoTitleConfig); + if (selectedItem->elementId.offset != 0) { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoTitleIdOpenParen", 1), CLAY_STRING(" ("), infoTitleConfig); + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoTitleIdIndex"), Clay__IntToString(selectedItem->elementId.offset), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoTitleIdOpenParen", 3), CLAY_STRING(")"), infoTitleConfig); + } + }); + // Clay_LayoutConfig debug info + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewElementInfoTopBorder"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1)}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_3), {}); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoLayoutBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .boundingBox + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutTitle", 1), CLAY_STRING("Bounding Box"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoLayoutBoundingBox"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 1), CLAY_STRING("{ x: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 2), Clay__IntToString(selectedItem->boundingBox.x), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 3), CLAY_STRING(", y: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 4), Clay__IntToString(selectedItem->boundingBox.y), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 5), CLAY_STRING(", width: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 6), Clay__IntToString(selectedItem->boundingBox.width), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 7), CLAY_STRING(", height: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 8), Clay__IntToString(selectedItem->boundingBox.height), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutBoundingData", 9), CLAY_STRING(" }"), infoTextConfig); + }); + // .layoutDirection + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutTitle", 2), CLAY_STRING("Layout Direction"), infoTitleConfig); + Clay_LayoutConfig *layoutConfig = selectedItem->layoutElement->layoutConfig; + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoLayoutDirection"), layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM ? CLAY_STRING("TOP_TO_BOTTOM") : CLAY_STRING("LEFT_TO_RIGHT"), infoTextConfig); + // .sizing + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutTitle", 3), CLAY_STRING("Sizing"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoSizingWidth"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoSizingData", 1), CLAY_STRING("width: "), infoTextConfig); + Clay__RenderDebugLayoutSizing(CLAY_ID("Clay__DebugViewElementInfoSizingDataWidth"), layoutConfig->sizing.width, infoTextConfig); + }); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoSizingHeight"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoSizingData", 2), CLAY_STRING("height: "), infoTextConfig); + Clay__RenderDebugLayoutSizing(CLAY_ID("Clay__DebugViewElementInfoSizingDataHeight"), layoutConfig->sizing.height, infoTextConfig); + }); + // .padding + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutTitle", 4), CLAY_STRING("Padding"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoPadding"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutPaddingData", 1), CLAY_STRING("{ x: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutPaddingData", 2), Clay__IntToString(layoutConfig->padding.x), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutPaddingData", 3), CLAY_STRING(", y: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutPaddingData", 4), Clay__IntToString(layoutConfig->padding.y), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutPaddingData", 5), CLAY_STRING(" }"), infoTextConfig); + }); + // .childGap + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutTitle", 5), CLAY_STRING("Child Gap"), infoTitleConfig); + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoLayoutChildGap"), Clay__IntToString(layoutConfig->childGap), infoTextConfig); + // .childAlignment + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutTitle", 6), CLAY_STRING("Child Alignment"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoLayoutAlignment"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutAlignment", 1), CLAY_STRING("{ x: "), infoTextConfig); + Clay_String alignX = CLAY_STRING("LEFT"); + if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_CENTER) { + alignX = CLAY_STRING("CENTER"); + } else if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_RIGHT) { + alignX = CLAY_STRING("RIGHT"); + } + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutAlignment", 2), alignX, infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutAlignment", 3), CLAY_STRING(", y: "), infoTextConfig); + Clay_String alignY = CLAY_STRING("TOP"); + if (layoutConfig->childAlignment.y == CLAY_ALIGN_Y_CENTER) { + alignY = CLAY_STRING("CENTER"); + } else if (layoutConfig->childAlignment.y == CLAY_ALIGN_Y_BOTTOM) { + alignY = CLAY_STRING("BOTTOM"); + } + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutAlignment", 4), alignY, infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoLayoutAlignment", 5), CLAY_STRING(" }"), infoTextConfig); + }); + }); + switch (selectedItem->layoutElement->elementType) { + case CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER: break; + case CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE: { + Clay_RectangleElementConfig *rectangleConfig = selectedItem->layoutElement->elementConfig.rectangleElementConfig; + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Rectangle Element Config")); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoRectangleBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .color + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleColorTitle", 1), CLAY_STRING("Color"), infoTitleConfig); + Clay__RenderDebugViewColor(rectangleConfig->color, infoTextConfig); + // .cornerRadius + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleColorTitle", 2), CLAY_STRING("Corner Radius"), infoTitleConfig); + Clay__RenderDebugViewCornerRadius(rectangleConfig->cornerRadius, infoTextConfig); + }); + break; + } + case CLAY__LAYOUT_ELEMENT_TYPE_TEXT: { + Clay_TextElementConfig *textConfig = selectedItem->layoutElement->elementConfig.textElementConfig; + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Text Element Config")); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoRectangleBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .fontSize + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 1), CLAY_STRING("Font Size"), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontBody", 1), Clay__IntToString(textConfig->fontSize), infoTextConfig); + // .fontId + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 2), CLAY_STRING("Font ID"), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontBody", 2), Clay__IntToString(textConfig->fontId), infoTextConfig); + // .lineSpacing + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 3), CLAY_STRING("Line Spacing"), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontBody", 3), Clay__IntToString(textConfig->lineSpacing), infoTextConfig); + // .letterSpacing + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 4), CLAY_STRING("Letter Spacing"), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontBody", 4), Clay__IntToString(textConfig->letterSpacing), infoTextConfig); + // .lineSpacing + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 5), CLAY_STRING("Wrap Mode"), infoTitleConfig); + Clay_String wrapMode = CLAY_STRING("WORDS"); + if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE) { + wrapMode = CLAY_STRING("NONE"); + } else if (textConfig->wrapMode == CLAY_TEXT_WRAP_NEWLINES) { + wrapMode = CLAY_STRING("NEWLINES"); + } + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontBody", 5), wrapMode, infoTextConfig); + // .textColor + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 6), CLAY_STRING("Text Color"), infoTitleConfig); + Clay__RenderDebugViewColor(textConfig->textColor, infoTextConfig); + }); + break; + } + case CLAY__LAYOUT_ELEMENT_TYPE_IMAGE: { + Clay_ImageElementConfig *imageConfig = selectedItem->layoutElement->elementConfig.imageElementConfig; + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Image Element Config")); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoImageBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .sourceDimensions + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageTitle", 1), CLAY_STRING("Source Dimensions"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoImageDimensions"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageDimensionsData", 1), CLAY_STRING("{ width: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageDimensionsData", 2), Clay__IntToString(imageConfig->sourceDimensions.width), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageDimensionsData", 3), CLAY_STRING(", height: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageDimensionsData", 4), Clay__IntToString(imageConfig->sourceDimensions.height), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageDimensionsData", 5), CLAY_STRING(" }"), infoTextConfig); + }); + // Image Preview + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageTitle", 2), CLAY_STRING("Preview"), infoTitleConfig); + CLAY_IMAGE(CLAY_ID("Clay__DebugViewElementInfoImagePreview"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = imageConfig->sourceDimensions.width) }), imageConfig, {}); + }); + break; + } + case CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER: { + Clay_ScrollElementConfig *scrollConfig = selectedItem->layoutElement->elementConfig.scrollElementConfig; + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Scroll Element Config")); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoScrollBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .vertical + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoScrollTitle", 1), CLAY_STRING("Vertical"), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageVerticalData", 1), scrollConfig->vertical ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig); + // .horizontal + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoScrollTitle", 2), CLAY_STRING("Horizontal"), infoTitleConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoImageVerticalData", 2), scrollConfig->horizontal ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig); + }); + break; + } + case CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER: { + Clay_FloatingElementConfig *floatingConfig = selectedItem->layoutElement->elementConfig.floatingElementConfig; + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Floating Element Config")); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoScrollBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .offset + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingTitle", 1), CLAY_STRING("Offset"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoFloatingOffset"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingOffsetData", 1), CLAY_STRING("{ x: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingOffsetData", 2), Clay__IntToString(floatingConfig->offset.x), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingOffsetData", 3), CLAY_STRING(", y: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingOffsetData", 4), Clay__IntToString(floatingConfig->offset.y), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingOffsetData", 5), CLAY_STRING(" }"), infoTextConfig); + }); + // .expand + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingTitle", 2), CLAY_STRING("Expand"), infoTitleConfig); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoFloatingExpand"), CLAY_LAYOUT(), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingExpandData", 1), CLAY_STRING("{ width: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingExpandData", 2), Clay__IntToString(floatingConfig->expand.width), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingExpandData", 3), CLAY_STRING(", height: "), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingExpandData", 4), Clay__IntToString(floatingConfig->expand.height), infoTextConfig); + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingExpandData", 5), CLAY_STRING(" }"), infoTextConfig); + }); + // .zIndex + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingTitle", 3), CLAY_STRING("z-index"), infoTitleConfig); + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoFloatingZIndex"), Clay__IntToString(floatingConfig->zIndex), infoTextConfig); + // .parentId + CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoFloatingTitle", 4), CLAY_STRING("Parent"), infoTitleConfig); + Clay_LayoutElementHashMapItem *hashItem = Clay__GetHashMapItem(floatingConfig->parentId); + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoFloatingParent"), hashItem->elementId.stringId, infoTextConfig); + }); + break; + } + case CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER: { + Clay_BorderElementConfig *borderConfig = selectedItem->layoutElement->elementConfig.borderElementConfig; + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Border Element Config")); + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewElementInfoBorderBody"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}, .childGap = 8), { + // .left + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoBorderLeftTitle"), CLAY_STRING("Left Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(1, borderConfig->left, infoTextConfig); + // .right + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoBorderRightTitle"), CLAY_STRING("Right Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(2, borderConfig->right, infoTextConfig); + // .top + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoBorderTopTitle"), CLAY_STRING("Top Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(3, borderConfig->top, infoTextConfig); + // .bottom + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoBorderBottomTitle"), CLAY_STRING("Bottom Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(4, borderConfig->bottom, infoTextConfig); + // .betweenChildren + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoBorderChildrenTitle"), CLAY_STRING("Border Between Children"), infoTitleConfig); + Clay__RenderDebugViewBorder(5, borderConfig->betweenChildren, infoTextConfig); + // .cornerRadius + CLAY_TEXT(CLAY_ID("Clay__DebugViewElementInfoBorderCornerRadiusTitle"), CLAY_STRING("Corner Radius"), infoTitleConfig); + Clay__RenderDebugViewCornerRadius(borderConfig->cornerRadius, infoTextConfig); + }); + break; + } + case CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM: { + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, CLAY_STRING("Layout Element Config")); + break; + } + } + }); + }); + } else { + CLAY_SCROLL_CONTAINER(CLAY_ID("Clay__DebugViewWarningsScrollPane"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(300)}, .childGap = 6), CLAY_SCROLL_CONFIG(.vertical = true, .horizontal = true), { + Clay_TextElementConfig *warningConfig = CLAY_TEXT_CONFIG(.fontSize = 16, .textColor = CLAY__DEBUGVIEW_COLOR_4, .wrapMode = CLAY_TEXT_WRAP_NONE); + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewWarningsPane"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}), CLAY_RECTANGLE_CONFIG(.color = CLAY__DEBUGVIEW_COLOR_2), { + CLAY_CONTAINER(CLAY_ID("Clay__DebugViewWarningItemHeader"), CLAY_LAYOUT(.sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childGap = 8), { + CLAY_TEXT(CLAY_ID("Clay__DebugViewWarningsTitle"), CLAY_STRING("Warnings"), warningConfig); + }); + CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewWarningsTopBorder"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1)}), CLAY_RECTANGLE_CONFIG(.color = {200, 200, 200, 255}), {}); + int previousWarningsLength = Clay_warnings.length; + for (int i = 0; i < previousWarningsLength; i++) { + Clay__Warning warning = Clay_warnings.internalArray[i]; + CLAY_CONTAINER(CLAY_IDI("Clay__DebugViewWarningItem", i), CLAY_LAYOUT(.sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childGap = 8), { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewWarningText", i), warning.baseMessage, warningConfig); + if (warning.dynamicMessage.length > 0) { + CLAY_TEXT(CLAY_IDI("Clay__DebugViewWarningTextDynamic", i), warning.dynamicMessage, warningConfig); + } + }); + } + }); + }); + } + }); + }); +} +#pragma endregion + // PUBLIC API FROM HERE --------------------------------------- CLAY_WASM_EXPORT("Clay_MinMemorySize") @@ -2098,15 +2970,20 @@ void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_Str } #endif -CLAY_WASM_EXPORT("Clay_SetPointerPosition") -void Clay_SetPointerPosition(Clay_Vector2 position) { - Clay__pointerPosition = position; +CLAY_WASM_EXPORT("Clay_SetLayoutDimensions") +void Clay_SetLayoutDimensions(Clay_Dimensions dimensions) { + Clay__layoutDimensions = dimensions; +} + +CLAY_WASM_EXPORT("Clay_SetPointerState") +void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) { + Clay__pointerInfo.position = position; Clay__pointerOverIds.length = 0; - Clay__LayoutElementPointerArray dfsBuffer = Clay__layoutElementChildrenBuffer; + Clay__int32_tArray dfsBuffer = Clay__layoutElementChildrenBuffer; for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) { dfsBuffer.length = 0; Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); - Clay__LayoutElementPointerArray_Add(&dfsBuffer, root->layoutElement); + Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex); Clay__treeNodeVisited.internalArray[0] = false; while (dfsBuffer.length > 0) { if (Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) { @@ -2114,16 +2991,16 @@ void Clay_SetPointerPosition(Clay_Vector2 position) { continue; } Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; - Clay_LayoutElement *currentElement = *Clay__LayoutElementPointerArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1); + Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1)); Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO I wish there was a way around this, maybe the fact that it's essentially a binary tree limits the cost, have to measure if ((mapItem && Clay__PointIsInsideRect(position, mapItem->boundingBox)) || (!mapItem && Clay__PointIsInsideRect(position, (Clay_BoundingBox) {0,0, currentElement->dimensions.width, currentElement->dimensions.height}))) { - Clay__int32_tArray_Add(&Clay__pointerOverIds, (int)currentElement->id); + Clay__ElementIdArray_Add(&Clay__pointerOverIds, mapItem->elementId); if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { dfsBuffer.length--; continue; } for (int i = currentElement->children.length - 1; i >= 0; --i) { - Clay__LayoutElementPointerArray_Add(&dfsBuffer, currentElement->children.elements[i]); + Clay__int32_tArray_Add(&dfsBuffer, currentElement->children.elements[i]); Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked } } else { @@ -2131,10 +3008,24 @@ void Clay_SetPointerPosition(Clay_Vector2 position) { } } } + + if (isPointerDown) { + if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + Clay__pointerInfo.state = CLAY__POINTER_INFO_PRESSED; + } else if (Clay__pointerInfo.state != CLAY__POINTER_INFO_PRESSED) { + Clay__pointerInfo.state = CLAY__POINTER_INFO_PRESSED_THIS_FRAME; + } + } else { + if (Clay__pointerInfo.state == CLAY__POINTER_INFO_RELEASED_THIS_FRAME) { + Clay__pointerInfo.state = CLAY__POINTER_INFO_RELEASED; + } else if (Clay__pointerInfo.state != CLAY__POINTER_INFO_RELEASED) { + Clay__pointerInfo.state = CLAY__POINTER_INFO_RELEASED_THIS_FRAME; + } + } } CLAY_WASM_EXPORT("Clay_Initialize") -void Clay_Initialize(Clay_Arena arena) { +void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions) { Clay__internalArena = arena; Clay__InitializePersistentMemory(&Clay__internalArena); Clay__InitializeEphemeralMemory(&Clay__internalArena); @@ -2142,6 +3033,7 @@ void Clay_Initialize(Clay_Arena arena) { Clay__layoutElementsHashMap.internalArray[i] = -1; } Clay__measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element" + Clay__layoutDimensions = layoutDimensions; } CLAY_WASM_EXPORT("Clay_UpdateScrollContainers") @@ -2197,7 +3089,7 @@ void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, scrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(scrollData->scrollPosition.y, 0), -(scrollData->contentSize.height - scrollData->layoutElement->dimensions.height)); for (int j = 0; j < Clay__pointerOverIds.length; ++j) { // TODO n & m are small here but this being n*m gives me the creeps - if (scrollData->layoutElement->id == Clay__int32_tArray_Get(&Clay__pointerOverIds, j)) { + if (scrollData->layoutElement->id == Clay__ElementIdArray_Get(&Clay__pointerOverIds, j)->id) { highestPriorityElementIndex = j; highestPriorityScrollData = scrollData; } @@ -2219,26 +3111,26 @@ void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, if (isPointerActive) { highestPriorityScrollData->scrollMomentum = (Clay_Vector2){0}; if (!highestPriorityScrollData->pointerScrollActive) { - highestPriorityScrollData->pointerOrigin = Clay__pointerPosition; + highestPriorityScrollData->pointerOrigin = Clay__pointerInfo.position; highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition; highestPriorityScrollData->pointerScrollActive = true; } else { float scrollDeltaX = 0, scrollDeltaY = 0; if (canScrollHorizontally) { float oldXScrollPosition = highestPriorityScrollData->scrollPosition.x; - highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollOrigin.x + (Clay__pointerPosition.x - highestPriorityScrollData->pointerOrigin.x); + highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollOrigin.x + (Clay__pointerInfo.position.x - highestPriorityScrollData->pointerOrigin.x); highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - highestPriorityScrollData->boundingBox.width)); scrollDeltaX = highestPriorityScrollData->scrollPosition.x - oldXScrollPosition; } if (canScrollVertically) { float oldYScrollPosition = highestPriorityScrollData->scrollPosition.y; - highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollOrigin.y + (Clay__pointerPosition.y - highestPriorityScrollData->pointerOrigin.y); + highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollOrigin.y + (Clay__pointerInfo.position.y - highestPriorityScrollData->pointerOrigin.y); highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - highestPriorityScrollData->boundingBox.height)); scrollDeltaY = highestPriorityScrollData->scrollPosition.y - oldYScrollPosition; } if (scrollDeltaX > -0.1f && scrollDeltaX < 0.1f && scrollDeltaY > -0.1f && scrollDeltaY < 0.1f && highestPriorityScrollData->momentumTime > 0.15f) { highestPriorityScrollData->momentumTime = 0; - highestPriorityScrollData->pointerOrigin = Clay__pointerPosition; + highestPriorityScrollData->pointerOrigin = Clay__pointerInfo.position; highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition; } else { highestPriorityScrollData->momentumTime += deltaTime; @@ -2256,50 +3148,56 @@ void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, } CLAY_WASM_EXPORT("Clay_BeginLayout") -void Clay_BeginLayout(int screenWidth, int screenHeight) { +void Clay_BeginLayout() { Clay__InitializeEphemeralMemory(&Clay__internalArena); + Clay__generation++; + Clay__dynamicElementIndex = 0; // Set up the root container that covers the entire window - Clay_LayoutElement rootLayoutElement = (Clay_LayoutElement){.layoutConfig = CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED((float)screenWidth), CLAY_SIZING_FIXED((float)screenHeight)})}; + Clay_Dimensions rootDimensions = (Clay_Dimensions) {Clay__layoutDimensions.width, Clay__layoutDimensions.height}; + if (Clay__debugModeEnabled) { + rootDimensions.width -= (float)Clay__debugViewWidth; + } + Clay_ElementId rootElementId = CLAY_ID("Clay__RootContainer"); + Clay_LayoutElement rootLayoutElement = (Clay_LayoutElement){.id = rootElementId.id, .layoutConfig = CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)})}; Clay__openLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, rootLayoutElement); Clay__LayoutElementPointerArray_Add(&Clay__openLayoutElementStack, Clay__openLayoutElement); - Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, (Clay__LayoutElementTreeRoot) { .layoutElement = Clay__openLayoutElement }); + Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, (Clay__LayoutElementTreeRoot) { .layoutElementIndex = Clay__layoutElements.length - 1 }); + Clay__StringArray_Add(&Clay__layoutElementIdStrings, CLAY_STRING("Clay__RootContainer")); + Clay__AddHashMapItem(rootElementId, Clay__openLayoutElement); } CLAY_WASM_EXPORT("Clay_EndLayout") -Clay_RenderCommandArray Clay_EndLayout(int screenWidth, int screenHeight) +Clay_RenderCommandArray Clay_EndLayout() { Clay__AttachContainerChildren(); - Clay__CalculateFinalLayout(screenWidth, screenHeight); + if (Clay__debugModeEnabled) { + #ifndef CLAY_DEBUG + Clay__warningsEnabled = false; + #endif + Clay__RenderDebugView(); + #ifndef CLAY_DEBUG + Clay__warningsEnabled = true; + #endif + } + Clay__CalculateFinalLayout(); return Clay__renderCommands; } CLAY_WASM_EXPORT("Clay_PointerOver") -bool Clay_PointerOver(uint32_t id) { // TODO return priority for separating multiple results +bool Clay_PointerOver(Clay_ElementId elementId) { // TODO return priority for separating multiple results for (int i = 0; i < Clay__pointerOverIds.length; ++i) { - if (Clay__int32_tArray_Get(&Clay__pointerOverIds, i) == id) { + if (Clay__ElementIdArray_Get(&Clay__pointerOverIds, i)->id == elementId.id) { return true; } } return false; } -typedef struct -{ - // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. - // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. - Clay_Vector2 *scrollPosition; - Clay_Dimensions scrollContainerDimensions; - Clay_Dimensions contentDimensions; - Clay_ScrollElementConfig config; - // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. - bool found; -} Clay_ScrollContainerData; - CLAY_WASM_EXPORT("Clay_GetScrollContainerData") -Clay_ScrollContainerData Clay_GetScrollContainerData(uint32_t id) { +Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id) { for (int i = 0; i < Clay__scrollContainerDatas.length; ++i) { Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); - if (scrollContainerData->elementId == id) { + if (scrollContainerData->elementId == id.id) { return (Clay_ScrollContainerData) { .scrollPosition = &scrollContainerData->scrollPosition, .scrollContainerDimensions = (Clay_Dimensions) { scrollContainerData->boundingBox.width, scrollContainerData->boundingBox.height }, @@ -2312,6 +3210,11 @@ Clay_ScrollContainerData Clay_GetScrollContainerData(uint32_t id) { return (Clay_ScrollContainerData){}; } +CLAY_WASM_EXPORT("Clay_SetDebugModeEnabled") +void Clay_SetDebugModeEnabled(bool enabled) { + Clay__debugModeEnabled = enabled; +} + #endif //CLAY_IMPLEMENTATION /* diff --git a/examples/clay-official-website/build.sh b/examples/clay-official-website/build.sh index e10fc68..3c9fe4b 100755 --- a/examples/clay-official-website/build.sh +++ b/examples/clay-official-website/build.sh @@ -2,6 +2,7 @@ mkdir -p build/clay \ && clang \ -Os \ -DCLAY_WASM \ +-DCLAY_DEBUG \ -mbulk-memory \ --target=wasm32 \ -nostdlib \ diff --git a/examples/clay-official-website/images/debugger.png b/examples/clay-official-website/images/debugger.png new file mode 100644 index 0000000..79c0133 Binary files /dev/null and b/examples/clay-official-website/images/debugger.png differ diff --git a/examples/clay-official-website/index.html b/examples/clay-official-website/index.html index 1b955bf..25607d0 100644 --- a/examples/clay-official-website/index.html +++ b/examples/clay-official-website/index.html @@ -13,6 +13,7 @@ padding: 0; margin: 0; pointer-events: none; + background: rgb(244, 235, 230); } /* Import the font using @font-face */ @font-face { @@ -48,7 +49,7 @@ .text { pointer-events: all; - white-space: nowrap; + white-space: pre; } @@ -63,14 +64,14 @@ const CLAY_RENDER_COMMAND_TYPE_CUSTOM = 7; const GLOBAL_FONT_SCALING_FACTOR = 0.8; let renderCommandSize = 0; - let scratchSpaceAddress = 0; + let scratchSpaceAddress = 8; let heapSpaceAddress = 0; let memoryDataView; let textDecoder = new TextDecoder("utf-8"); let previousFrameTime; let fontsById = [ - 'Calistoga', 'Quicksand', + 'Calistoga', 'Quicksand', 'Quicksand', 'Quicksand', @@ -117,6 +118,7 @@ { name: 'fontSize', type: 'uint16_t' }, { name: 'letterSpacing', type: 'uint16_t' }, { name: 'lineSpacing', type: 'uint16_t' }, + { name: 'wrapMode', type: 'uint32_t' }, { name: 'disablePointerEvents', type: 'uint8_t' } ]}; let imageConfigDefinition = { name: 'image', type: 'struct', members: [ @@ -212,6 +214,18 @@ instance.exports.Clay_CreateArenaWithCapacityAndMemory(arenaStructAddress, memorySize, arenaMemoryAddress); } async function init() { + await new Promise((resolve, reject) => { + // repeatedly poll check + const poller = setInterval(async () => { + await Promise.all(fontsById.map(f => document.fonts.load(`12px "${f}"`))); + console.log(`12px "${fontsById[0]}"`); + if (document.fonts.check(`12px "${fontsById[0]}"`)) { + clearInterval(poller); + resolve(true); + } + }, 10); + setTimeout(() => {clearInterval(poller); resolve()}, 1000); + }); window.htmlRoot = document.body.appendChild(document.createElement('div')); window.canvasRoot = document.body.appendChild(document.createElement('canvas')); window.canvasContext = window.canvasRoot.getContext("2d"); @@ -224,7 +238,7 @@ window.arrowKeyUpPressedThisFrame = false; let zeroTimeout = null; addEventListener("wheel", (event) => { - window.mouseWheelXThisFrame = event.deltaX * 0.1; + window.mouseWheelXThisFrame = event.deltaX * -0.1; window.mouseWheelYThisFrame = event.deltaY * -0.1; clearTimeout(zeroTimeout); zeroTimeout = setTimeout(() => { @@ -270,6 +284,9 @@ if (event.key === "ArrowUp") { window.arrowKeyUpPressedThisFrame = true; } + if (event.key === "d") { + window.dKeyPressedThisFrame = true; + } }); const importObject = { @@ -293,7 +310,9 @@ let arenaAddress = scratchSpaceAddress; window.instance = instance; createMainArena(arenaAddress, heapSpaceAddress); - instance.exports.Clay_Initialize(arenaAddress); + memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); + memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); + instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); renderCommandSize = getStructTotalSize(renderCommandDefinition); renderLoop(); } @@ -326,7 +345,9 @@ } break; } - case CLAY_RENDER_COMMAND_TYPE_IMAGE: { elementType = 'img'; break; } + case CLAY_RENDER_COMMAND_TYPE_IMAGE: { + elementType = 'img'; break; + } default: break; } element = document.createElement(elementType); @@ -349,7 +370,7 @@ if (parentElement.nextElementIndex === 0) { parentElement.element.insertAdjacentElement('afterbegin', element); } else { - parentElement.element.childNodes[parentElement.nextElementIndex - 1].insertAdjacentElement('afterend', element); + parentElement.element.childNodes[Math.min(parentElement.nextElementIndex - 1, parentElement.element.childNodes.length - 1)].insertAdjacentElement('afterend', element); } } @@ -375,7 +396,8 @@ let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(renderCommand.id.value)) { + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { window.location.href = linkContents; } if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { @@ -504,8 +526,8 @@ let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true); window.canvasRoot.width = window.innerWidth * window.devicePixelRatio; window.canvasRoot.height = window.innerHeight * window.devicePixelRatio; - window.canvasRoot.style.width = window.innerWidth; - window.canvasRoot.style.height = window.innerHeight; + window.canvasRoot.style.width = window.innerWidth + 'px'; + window.canvasRoot.style.height = window.innerHeight + 'px'; let ctx = window.canvasContext; let scale = window.devicePixelRatio; for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { @@ -530,7 +552,8 @@ ctx.closePath(); // Handle link clicks let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(renderCommand.id.value)) { + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { window.location.href = linkContents; } break; @@ -643,9 +666,11 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { + window.canvasContext.save(); window.canvasContext.beginPath(); window.canvasContext.rect(boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale); window.canvasContext.clip(); + window.canvasContext.closePath(); break; } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): { @@ -676,7 +701,7 @@ const elapsed = currentTime - previousFrameTime; previousFrameTime = currentTime; let activeRendererIndex = memoryDataView.getUint32(instance.exports.ACTIVE_RENDERER_INDEX.value, true); - instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, window.arrowKeyDownPressedThisFrame, window.arrowKeyUpPressedThisFrame, elapsed / 1000); + instance.exports.UpdateDrawFrame(scratchSpaceAddress, window.innerWidth, window.innerHeight, window.mouseWheelXThisFrame, window.mouseWheelYThisFrame, window.mousePositionXThisFrame, window.mousePositionYThisFrame, window.touchDown, window.mouseDown, window.arrowKeyDownPressedThisFrame, window.arrowKeyUpPressedThisFrame, window.dKeyPressedThisFrame, elapsed / 1000); let rendererChanged = activeRendererIndex !== window.previousActiveRendererIndex; switch (activeRendererIndex) { case 0: { @@ -701,6 +726,7 @@ window.mouseDownThisFrame = false; window.arrowKeyUpPressedThisFrame = false; window.arrowKeyDownPressedThisFrame = false; + window.dKeyPressedThisFrame = false; } init(); diff --git a/examples/clay-official-website/main.c b/examples/clay-official-website/main.c index 009f50f..efd2b66 100644 --- a/examples/clay-official-website/main.c +++ b/examples/clay-official-website/main.c @@ -7,9 +7,9 @@ double windowWidth = 1024, windowHeight = 768; float modelPageOneZRotation = 0; int ACTIVE_RENDERER_INDEX = 0; -const uint32_t FONT_ID_TITLE_56 = 0; -const uint32_t FONT_ID_BODY_24 = 1; -const uint32_t FONT_ID_BODY_16 = 2; +const uint32_t FONT_ID_BODY_16 = 0; +const uint32_t FONT_ID_TITLE_56 = 1; +const uint32_t FONT_ID_BODY_24 = 2; const uint32_t FONT_ID_BODY_36 = 3; const uint32_t FONT_ID_TITLE_36 = 4; const uint32_t FONT_ID_MONOSPACE_24 = 5; @@ -18,7 +18,6 @@ const Clay_Color COLOR_LIGHT = (Clay_Color) {244, 235, 230, 255}; Clay_Color COLOR_LIGHT_HOVER = (Clay_Color) {224, 215, 210, 255}; Clay_Color COLOR_BUTTON_HOVER = (Clay_Color) {238, 227, 225, 255}; Clay_Color COLOR_BROWN = (Clay_Color) {61, 26, 5, 255}; -//Clay_Color COLOR_RED = (Clay_Color) {252, 67, 27, 255}; Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255}; Clay_Color COLOR_RED_HOVER = (Clay_Color) {148, 46, 8, 255}; Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255}; @@ -56,7 +55,7 @@ void LandingPageDesktop() { CLAY_BORDER_CONTAINER(CLAY_ID("LandingPage1"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { 32, 32 }, .childGap = 32), CLAY_BORDER_CONFIG(.left = { 2, COLOR_RED }, .right = { 2, COLOR_RED }), { CLAY_CONTAINER(CLAY_ID("LeftText"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { CLAY_TEXT(CLAY_ID("LeftTextTitle"), CLAY_STRING("Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance."), CLAY_TEXT_CONFIG(.fontSize = 56, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED)); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) }), {}); + CLAY_CONTAINER(CLAY_ID("LandingPageSpacer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) }), {}); CLAY_TEXT(CLAY_ID("LeftTextTagline"), CLAY_STRING("Clay is laying out this webpage right now!"), CLAY_TEXT_CONFIG(.fontSize = 36, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE)); }); CLAY_CONTAINER(CLAY_ID("HeroImageOuter"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_PERCENT(0.45f) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16), { @@ -74,7 +73,7 @@ void LandingPageMobile() { CLAY_CONTAINER(CLAY_ID("LandingPage1Mobile"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 32 }, .childGap = 32), { CLAY_CONTAINER(CLAY_ID("LeftText"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { CLAY_TEXT(CLAY_ID("LeftTextTitle"), CLAY_STRING("Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance."), CLAY_TEXT_CONFIG(.fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED)); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) }), {}); + CLAY_CONTAINER(CLAY_ID("LandingPageSpacer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) }), {}); CLAY_TEXT(CLAY_ID("LeftTextTagline"), CLAY_STRING("Clay is laying out this webpage right now!"), CLAY_TEXT_CONFIG(.fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE)); }); CLAY_CONTAINER(CLAY_ID("HeroImageOuter"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW() }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16), { @@ -136,7 +135,7 @@ void DeclarativeSyntaxPageDesktop() { CLAY_TEXT(CLAY_ID("SyntaxPageTextSubTitle3"), CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); }); CLAY_CONTAINER(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER}), { - CLAY_IMAGE(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 568) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png")), {}); + CLAY_IMAGE(CLAY_ID("SyntaxPageRightImageInner"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 568) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png")), {}); }); }); }); @@ -152,7 +151,7 @@ void DeclarativeSyntaxPageMobile() { CLAY_TEXT(CLAY_ID("SyntaxPageTextSubTitle3"), CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); }); CLAY_CONTAINER(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER}), { - CLAY_IMAGE(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 568) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png")), {}); + CLAY_IMAGE(CLAY_ID("SyntaxPageRightImageInner"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 568) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png")), {}); }); }); } @@ -172,7 +171,7 @@ void HighPerformancePageDesktop(float lerpValue) { CLAY_RECTANGLE(CLAY_ID("PerformanceDesktop"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 82, 32}, .childGap = 64), CLAY_RECTANGLE_CONFIG(.color = COLOR_RED), { CLAY_CONTAINER(CLAY_ID("PerformanceLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { CLAY_TEXT(CLAY_ID("PerformanceTextTitle"), CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG(.fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); - CLAY_CONTAINER(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); + CLAY_CONTAINER(CLAY_ID("PerformanceSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); CLAY_TEXT(CLAY_IDI("PerformanceTextSubTitle", 1), CLAY_STRING("Fast enough to recompute your entire UI every frame."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); CLAY_TEXT(CLAY_IDI("PerformanceTextSubTitle", 2), CLAY_STRING("Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); CLAY_TEXT(CLAY_IDI("PerformanceTextSubTitle", 3), CLAY_STRING("Simplify animations and reactive UI design by avoiding the standard performance hacks."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); @@ -194,7 +193,7 @@ void HighPerformancePageMobile(float lerpValue) { CLAY_RECTANGLE(CLAY_ID("PerformanceMobile"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 16, 32}, .childGap = 32), CLAY_RECTANGLE_CONFIG(.color = COLOR_RED), { CLAY_CONTAINER(CLAY_ID("PerformanceLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { CLAY_TEXT(CLAY_ID("PerformanceTextTitle"), CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG(.fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); - CLAY_CONTAINER(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); + CLAY_CONTAINER(CLAY_ID("PerformanceSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); CLAY_TEXT(CLAY_IDI("PerformanceTextSubTitle", 1), CLAY_STRING("Fast enough to recompute your entire UI every frame."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); CLAY_TEXT(CLAY_IDI("PerformanceTextSubTitle", 2), CLAY_STRING("Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); CLAY_TEXT(CLAY_IDI("PerformanceTextSubTitle", 3), CLAY_STRING("Simplify animations and reactive UI design by avoiding the standard performance hacks."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); @@ -212,13 +211,13 @@ void HighPerformancePageMobile(float lerpValue) { }); } -void RendererButtonActive(uint32_t id, int index, Clay_String text) { +void RendererButtonActive(Clay_ElementId id, int index, Clay_String text) { CLAY_RECTANGLE(id, CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(300) }, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = Clay_PointerOver(id) ? COLOR_RED_HOVER : COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(10)), { CLAY_TEXT(CLAY_ID("RendererButtonActiveText"), text, CLAY_TEXT_CONFIG(.disablePointerEvents = true, .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); }); } -void RendererButtonInactive(uint32_t id, int index, Clay_String text) { +void RendererButtonInactive(Clay_ElementId id, int index, Clay_String text) { CLAY_BORDER_CONTAINER(id, CLAY_LAYOUT(), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(2, COLOR_RED, 10), { CLAY_RECTANGLE(CLAY_IDI("RendererButtonInactiveInner", index), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(300) }, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = Clay_PointerOver(id) ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .cornerRadius = CLAY_CORNER_RADIUS(10), .cursorPointer = true), { CLAY_TEXT(CLAY_IDI("RendererButtonInactiveText", index), text, CLAY_TEXT_CONFIG(.disablePointerEvents = true, .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); @@ -231,14 +230,14 @@ void RendererPageDesktop() { CLAY_BORDER_CONTAINER(CLAY_ID("RendererPage"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = { 32, 32 }, .childGap = 32), CLAY_BORDER_CONFIG(.left = { 2, COLOR_RED }, .right = { 2, COLOR_RED }), { CLAY_CONTAINER(CLAY_ID("RendererLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { CLAY_TEXT(CLAY_ID("RendererTextTitle"), CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG(.fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED)); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); + CLAY_CONTAINER(CLAY_ID("RendererSpacerLeft"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); CLAY_TEXT(CLAY_IDI("RendererTextSubTitle", 1), CLAY_STRING("Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); CLAY_TEXT(CLAY_IDI("RendererTextSubTitle", 2), CLAY_STRING("Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); CLAY_TEXT(CLAY_IDI("RendererTextSubTitle", 3), CLAY_STRING("There's even an HTML renderer - you're looking at it right now!"), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); }); CLAY_CONTAINER(CLAY_ID("RendererRightText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.5) }, .childAlignment = {CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16), { CLAY_TEXT(CLAY_ID("RendererTextRightTitle"), CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG(.fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE)); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 32) }), {}); + CLAY_CONTAINER(CLAY_ID("RendererSpacerRight"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 32) }), {}); if (ACTIVE_RENDERER_INDEX == 0) { RendererButtonActive(CLAY_IDI("RendererSelectButtonActive", 0), 0, CLAY_STRING("HTML Renderer")); RendererButtonInactive(CLAY_ID("RendererSelectButtonCanvas"), 1, CLAY_STRING("Canvas Renderer")); @@ -255,14 +254,14 @@ void RendererPageMobile() { CLAY_RECTANGLE(CLAY_ID("RendererMobile"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = {.x = 16, 32}, .childGap = 32), CLAY_RECTANGLE_CONFIG(.color = COLOR_LIGHT), { CLAY_CONTAINER(CLAY_ID("RendererLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { CLAY_TEXT(CLAY_ID("RendererTextTitle"), CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG(.fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED)); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); + CLAY_CONTAINER(CLAY_ID("RendererSpacerLeft"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); CLAY_TEXT(CLAY_IDI("RendererTextSubTitle", 1), CLAY_STRING("Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); CLAY_TEXT(CLAY_IDI("RendererTextSubTitle", 2), CLAY_STRING("Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); CLAY_TEXT(CLAY_IDI("RendererTextSubTitle", 3), CLAY_STRING("There's even an HTML renderer - you're looking at it right now!"), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); }); CLAY_CONTAINER(CLAY_ID("RendererRightText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16), { CLAY_TEXT(CLAY_ID("RendererTextRightTitle"), CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG(.fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE)); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 32) }), {}); + CLAY_CONTAINER(CLAY_ID("RendererSpacerRight"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 32) }), {}); if (ACTIVE_RENDERER_INDEX == 0) { RendererButtonActive(CLAY_IDI("RendererSelectButtonActive", 0), 0, CLAY_STRING("HTML Renderer")); RendererButtonInactive(CLAY_ID("RendererSelectButtonCanvas"), 1, CLAY_STRING("Canvas Renderer")); @@ -274,6 +273,22 @@ void RendererPageMobile() { }); } +void DebuggerPageDesktop() { + CLAY_RECTANGLE(CLAY_ID("DebuggerDesktop"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 82, 32}, .childGap = 64), CLAY_RECTANGLE_CONFIG(.color = COLOR_RED), { + CLAY_CONTAINER(CLAY_ID("DebuggerLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8), { + CLAY_TEXT(CLAY_ID("DebuggerTextTitle"), CLAY_STRING("Integrated Debug Tools"), CLAY_TEXT_CONFIG(.fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); + CLAY_CONTAINER(CLAY_ID("DebuggerSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) }), {}); + CLAY_TEXT(CLAY_IDI("DebuggerTextSubTitle", 1), CLAY_STRING("Clay includes built in \"Chrome Inspector\"-style debug tooling."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); + CLAY_TEXT(CLAY_IDI("DebuggerTextSubTitle", 2), CLAY_STRING("View your layout hierarchy and config in real time."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT)); + CLAY_CONTAINER(CLAY_ID("DebuggerPageSpacer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) }), {}); + CLAY_TEXT(CLAY_ID("DebuggerTagline"), CLAY_STRING("Press the \"d\" key to try it out now!"), CLAY_TEXT_CONFIG(.fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE)); + }); + CLAY_CONTAINER(CLAY_ID("DebuggerRightImageOuter"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER}), { + CLAY_IMAGE(CLAY_ID("DebuggerPageRightImageInner"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 558) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1620, 1474}, .sourceURL = CLAY_STRING("/clay/images/debugger.png")), {}); + }); + }); +} + typedef struct { Clay_Vector2 clickOrigin; @@ -284,9 +299,8 @@ typedef struct ScrollbarData scrollbarData = (ScrollbarData) {}; float animationLerpValue = -1.0f; -Clay_RenderCommandArray CreateLayout(float lerpValue) { - bool mobileScreen = windowWidth < 750; - Clay_BeginLayout((int)windowWidth, (int)windowHeight); +Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) { + Clay_BeginLayout(); CLAY_RECTANGLE(CLAY_ID("OuterContainer"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }), CLAY_RECTANGLE_CONFIG(.color = COLOR_LIGHT), { CLAY_CONTAINER(CLAY_ID("Header"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 24, .padding = { 32 }), { CLAY_TEXT(CLAY_ID("Logo"), CLAY_STRING("Clay"), &headerTextConfig); @@ -300,7 +314,7 @@ Clay_RenderCommandArray CreateLayout(float lerpValue) { CLAY_TEXT(CLAY_ID("LinkDocsText"), CLAY_STRING("Docs"), CLAY_TEXT_CONFIG(.disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255})); }); } - uint32_t githubButtonId = CLAY_ID("HeaderButtonGithub"); + Clay_ElementId githubButtonId = CLAY_ID("HeaderButtonGithub"); CLAY_BORDER_CONTAINER(CLAY_ID("LinkGithubOuter"), CLAY_LAYOUT(), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(2, COLOR_RED, 10), { CLAY_RECTANGLE(githubButtonId, CLAY_LAYOUT(.padding = {16, 6}), CLAY_RECTANGLE_CONFIG(.cornerRadius = CLAY_CORNER_RADIUS(10), .link = CLAY_STRING("https://github.com/nicbarker/clay"), .color = Clay_PointerOver(githubButtonId) ? COLOR_LIGHT_HOVER : COLOR_LIGHT), { CLAY_TEXT(CLAY_ID("LinkGithubText"), CLAY_STRING("Github"), CLAY_TEXT_CONFIG(.disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255})); @@ -327,6 +341,7 @@ Clay_RenderCommandArray CreateLayout(float lerpValue) { DeclarativeSyntaxPageDesktop(); HighPerformancePageDesktop(lerpValue); RendererPageDesktop(); + DebuggerPageDesktop(); } }); }); @@ -342,16 +357,19 @@ Clay_RenderCommandArray CreateLayout(float lerpValue) { scrollbarColor = (Clay_Color){225, 138, 50, 160}; } float scrollHeight = scrollData.scrollContainerDimensions.height - 12; - CLAY_FLOATING_CONTAINER(CLAY_ID("ScrollBar"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = CLAY_ID("OuterScrollContainer"), .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP}), { + CLAY_FLOATING_CONTAINER(CLAY_ID("ScrollBar"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = CLAY_ID("OuterScrollContainer").id, .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP}), { CLAY_RECTANGLE(CLAY_ID("ScrollBarButton"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(10), CLAY_SIZING_FIXED((scrollHeight / scrollData.contentDimensions.height) * scrollHeight)}), CLAY_RECTANGLE_CONFIG(.cornerRadius = CLAY_CORNER_RADIUS(5), .color = scrollbarColor), {}); }); } - return Clay_EndLayout((int)windowWidth, (int)windowHeight); + return Clay_EndLayout(); } -CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(float width, float height, float mouseWheelX, float mouseWheelY, float mousePositionX, float mousePositionY, bool isTouchDown, bool isMouseDown, bool arrowKeyDownPressedThisFrame, bool arrowKeyUpPressedThisFrame, float deltaTime) { +bool debugModeEnabled = false; + +CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(float width, float height, float mouseWheelX, float mouseWheelY, float mousePositionX, float mousePositionY, bool isTouchDown, bool isMouseDown, bool arrowKeyDownPressedThisFrame, bool arrowKeyUpPressedThisFrame, bool dKeyPressedThisFrame, float deltaTime) { windowWidth = width; windowHeight = height; + Clay_SetLayoutDimensions((Clay_Dimensions) { width, height }); if (deltaTime == deltaTime) { // NaN propagation can cause pain here animationLerpValue += deltaTime; if (animationLerpValue > 1) { @@ -359,6 +377,11 @@ CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(floa } } + if (dKeyPressedThisFrame) { + debugModeEnabled = !debugModeEnabled; + Clay_SetDebugModeEnabled(debugModeEnabled); + } + if (isTouchDown || isMouseDown) { if (Clay_PointerOver(CLAY_ID("RendererSelectButtonHTML"))) { ACTIVE_RENDERER_INDEX = 0; @@ -366,9 +389,10 @@ CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(floa ACTIVE_RENDERER_INDEX = 1; } } - //---------------------------------------------------------------------------------- - // Handle scroll containers - Clay_SetPointerPosition((Clay_Vector2) {mousePositionX, mousePositionY}); + + Clay__debugViewHighlightColor = (Clay_Color) {105,210,231, 120}; + + Clay_SetPointerState((Clay_Vector2) {mousePositionX, mousePositionY}, isMouseDown); if (!isMouseDown) { scrollbarData.mouseDown = false; @@ -408,7 +432,11 @@ CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(floa } Clay_UpdateScrollContainers(isTouchDown, (Clay_Vector2) {mouseWheelX, mouseWheelY}, deltaTime); - return CreateLayout(animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue)); + bool isMobileScreen = windowWidth < 750; + if (debugModeEnabled) { + isMobileScreen = windowWidth < 950; + } + return CreateLayout(isMobileScreen, animationLerpValue < 0 ? (animationLerpValue + 1) : (1 - animationLerpValue)); //---------------------------------------------------------------------------------- } diff --git a/examples/raylib-sidebar-scrolling-container/CMakeLists.txt b/examples/raylib-sidebar-scrolling-container/CMakeLists.txt index 7b7082d..32e833d 100644 --- a/examples/raylib-sidebar-scrolling-container/CMakeLists.txt +++ b/examples/raylib-sidebar-scrolling-container/CMakeLists.txt @@ -20,7 +20,7 @@ set(CMAKE_C_STANDARD 99) add_executable(clay_examples_raylib_sidebar_scrolling_container main.c) -target_compile_options(clay_examples_raylib_sidebar_scrolling_container PUBLIC -Wno-initializer-overrides) +target_compile_options(clay_examples_raylib_sidebar_scrolling_container PUBLIC -DCLAY_DEBUG) target_include_directories(clay_examples_raylib_sidebar_scrolling_container PUBLIC .) target_link_libraries(clay_examples_raylib_sidebar_scrolling_container PUBLIC raylib) diff --git a/examples/raylib-sidebar-scrolling-container/main.c b/examples/raylib-sidebar-scrolling-container/main.c index c338510..5ab85bb 100644 --- a/examples/raylib-sidebar-scrolling-container/main.c +++ b/examples/raylib-sidebar-scrolling-container/main.c @@ -14,7 +14,7 @@ Clay_TextElementConfig headerTextConfig = (Clay_TextElementConfig) { .fontId = F // Examples of re-usable "Components" void RenderHeaderButton(uint16_t index, Clay_String text) { - uint32_t buttonId = CLAY_IDI("HeaderButton", index); + Clay_ElementId buttonId = CLAY_IDI("HeaderButton", index); CLAY_RECTANGLE(buttonId, CLAY_LAYOUT(.padding = {16, 8}), CLAY_RECTANGLE_CONFIG(.color = Clay_PointerOver(buttonId) ? COLOR_BLUE : COLOR_ORANGE), { CLAY_TEXT(CLAY_IDI("Button", index), text, &headerTextConfig); }); @@ -24,14 +24,14 @@ Clay_LayoutConfig dropdownTextItemLayout = (Clay_LayoutConfig) { .padding = {8, Clay_RectangleElementConfig dropdownRectangleConfig = (Clay_RectangleElementConfig) { .color = {180, 180, 180, 255} }; Clay_TextElementConfig dropdownTextElementConfig = (Clay_TextElementConfig) { .fontSize = 24, .textColor = {255,255,255,255} }; -void RenderDropdownTextItem() { - CLAY_RECTANGLE(CLAY_ID("ScrollContainerItem"), &dropdownTextItemLayout, &dropdownRectangleConfig, { // We can save a lot of memory by re-using configs in loops rather than redefining them - CLAY_TEXT(CLAY_ID("ScrollContainerText"), CLAY_STRING("I'm a text field in a scroll container."), &dropdownTextElementConfig); +void RenderDropdownTextItem(int index) { + CLAY_RECTANGLE(CLAY_IDI("ScrollContainerItem", index), &dropdownTextItemLayout, &dropdownRectangleConfig, { // We can save a lot of memory by re-using configs in loops rather than redefining them + CLAY_TEXT(CLAY_IDI("ScrollContainerText", index), CLAY_STRING("I'm a text field in a scroll container."), &dropdownTextElementConfig); }); } Clay_RenderCommandArray CreateLayout() { - Clay_BeginLayout((int)GetScreenWidth(), (int)GetScreenHeight()); + Clay_BeginLayout(); CLAY_RECTANGLE(CLAY_ID("OuterContainer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, .padding = { 16, 16 }, .childGap = 16), CLAY_RECTANGLE_CONFIG(.color = {200, 200, 200, 255}), { CLAY_RECTANGLE(CLAY_ID("SideBar"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW() }, .padding = {16, 16}, .childGap = 16), CLAY_RECTANGLE_CONFIG(.color = {150, 150, 255, 255}), { CLAY_RECTANGLE(CLAY_ID("ProfilePictureOuter"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW() }, .padding = { 8, 8 }, .childGap = 8, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }), CLAY_RECTANGLE_CONFIG(.color = {130, 130, 255, 255}), { @@ -52,9 +52,11 @@ Clay_RenderCommandArray CreateLayout() { }); CLAY_SCROLL_CONTAINER(CLAY_ID("MainContent"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }), CLAY_SCROLL_CONFIG(.vertical = true), { CLAY_RECTANGLE(CLAY_ID("MainContentInner"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {16, 16}, .childGap = 16), CLAY_RECTANGLE_CONFIG(.color = {200, 200, 255, 255}), { - CLAY_FLOATING_CONTAINER(CLAY_ID("FloatingContainer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = {16, 16}), CLAY_FLOATING_CONFIG(.zIndex = 1, .attachment = { CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP }, .offset = {0, -16}), { - CLAY_RECTANGLE(CLAY_ID("FloatingContainerBackground"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }), CLAY_RECTANGLE_CONFIG(.color = {140,80, 200, 200}), { - CLAY_TEXT(CLAY_ID("FloatingContainerText"), CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {255,255,255,255})); + CLAY_FLOATING_CONTAINER(CLAY_ID("FloatingContainer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }), CLAY_FLOATING_CONFIG(.zIndex = 1, .attachment = { CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP }, .offset = {0, -16}), { + CLAY_BORDER_CONTAINER(CLAY_ID("FloatingContainerBorder"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .padding = { 16, 16 }), CLAY_BORDER_CONFIG_OUTSIDE(.color = {80, 80, 80, 255}, .width = 2), { + CLAY_RECTANGLE(CLAY_ID("FloatingContainerBackground"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }), CLAY_RECTANGLE_CONFIG(.color = {140,80, 200, 200}), { + CLAY_TEXT(CLAY_ID("FloatingContainerText"), CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {255,255,255,255})); + }); }); }); @@ -62,10 +64,10 @@ Clay_RenderCommandArray CreateLayout() { CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt."), CLAY_TEXT_CONFIG(.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {0,0,0,255})); - CLAY_RECTANGLE(CLAY_ID("Photos"), CLAY_LAYOUT(.childGap = 16, .padding = { 16, 16 }), CLAY_RECTANGLE_CONFIG(.color = {180, 180, 220, 255}), { - CLAY_IMAGE(CLAY_ID("Picture1"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); - CLAY_IMAGE(CLAY_ID("Picture2"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); - CLAY_IMAGE(CLAY_ID("Picture3"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); + CLAY_RECTANGLE(CLAY_ID("Photos2"), CLAY_LAYOUT(.childGap = 16, .padding = { 16, 16 }), CLAY_RECTANGLE_CONFIG(.color = {180, 180, 220, 255}), { + CLAY_IMAGE(CLAY_ID("Picture4"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); + CLAY_IMAGE(CLAY_ID("Picture5"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); + CLAY_IMAGE(CLAY_ID("Picture6"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); }); CLAY_TEXT(CLAY_ID("BodyText2"), @@ -79,8 +81,8 @@ Clay_RenderCommandArray CreateLayout() { CLAY_RECTANGLE(CLAY_ID("Photos"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW() }, .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = {180, 180, 220, 255}), { CLAY_IMAGE(CLAY_ID("Picture2"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); CLAY_RECTANGLE(CLAY_ID("Picture1"), CLAY_LAYOUT(.childAlignment = { .x = CLAY_ALIGN_X_CENTER }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8}), CLAY_RECTANGLE_CONFIG(.color = {170, 170, 220, 255}), { - CLAY_IMAGE(CLAY_ID("ProfilePicture"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {60, 60}), {}); - CLAY_TEXT(CLAY_ID("ProfileTitle"), CLAY_STRING("Image caption below"), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {0,0,0,255})); + CLAY_IMAGE(CLAY_ID("ProfilePicture2"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {60, 60}), {}); + CLAY_TEXT(CLAY_ID("ProfileTitle2"), CLAY_STRING("Image caption below"), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {0,0,0,255})); }); CLAY_IMAGE(CLAY_ID("Picture3"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = {120, 120}), {}); }); @@ -92,26 +94,26 @@ Clay_RenderCommandArray CreateLayout() { }); }); - CLAY_FLOATING_CONTAINER(CLAY_ID("Blob4Floating"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.zIndex = 1, .parentId = CLAY_ID("SidebarBlob4")), { + CLAY_FLOATING_CONTAINER(CLAY_ID("Blob4Floating2"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.zIndex = 1, .parentId = CLAY_ID("SidebarBlob4").id), { CLAY_SCROLL_CONTAINER(CLAY_ID("ScrollContainer"), CLAY_LAYOUT(.sizing = { .height = CLAY_SIZING_FIXED(200) }, .childGap = 2), CLAY_SCROLL_CONFIG(.vertical = true), { - CLAY_FLOATING_CONTAINER(CLAY_ID("FloatingContainer"), CLAY_LAYOUT(), CLAY_FLOATING_CONFIG(.zIndex = 1), { - CLAY_RECTANGLE(CLAY_ID("FLoatingContainerInner"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = {140,80, 200, 200}), { - CLAY_TEXT(CLAY_ID("FloatingContainerText"), CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {255,255,255,255})); + CLAY_FLOATING_CONTAINER(CLAY_ID("FloatingContainer2"), CLAY_LAYOUT(), CLAY_FLOATING_CONFIG(.zIndex = 1), { + CLAY_RECTANGLE(CLAY_ID("FloatingContainerInner"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = {140,80, 200, 200}), { + CLAY_TEXT(CLAY_ID("FloatingContainerText2"), CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {255,255,255,255})); }); }); CLAY_RECTANGLE(CLAY_ID("ScrollContainerInner"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM), CLAY_RECTANGLE_CONFIG(.color = {160, 160, 160, 255}), { - for (int i = 0; i < 100; i++) { - RenderDropdownTextItem(); - } +// for (int i = 0; i < 100; i++) { +// RenderDropdownTextItem(i); +// } }); }); }); Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(CLAY_ID("MainContent")); - CLAY_FLOATING_CONTAINER(CLAY_ID("ScrollBar"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.offset = { .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height }, .zIndex = 1, .parentId = CLAY_ID("MainContent"), .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP}), { + CLAY_FLOATING_CONTAINER(CLAY_ID("ScrollBar"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.offset = { .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height }, .zIndex = 1, .parentId = CLAY_ID("MainContent").id, .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP}), { CLAY_RECTANGLE(CLAY_ID("ScrollBarButton"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height)}), CLAY_RECTANGLE_CONFIG(.cornerRadius = 6, .color = Clay_PointerOver(CLAY_ID("ScrollBar")) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150}), {}); }); }); - return Clay_EndLayout(GetScreenWidth(), GetScreenHeight()); + return Clay_EndLayout(); } typedef struct @@ -123,16 +125,23 @@ typedef struct ScrollbarData scrollbarData = (ScrollbarData) {}; +bool debugEnabled = false; + void UpdateDrawFrame(void) { - float mouseWheelX = 0, mouseWheelY = 0; Vector2 mouseWheelDelta = GetMouseWheelMoveV(); - mouseWheelX = mouseWheelDelta.x; - mouseWheelY = mouseWheelDelta.y; + float mouseWheelX = mouseWheelDelta.x; + float mouseWheelY = mouseWheelDelta.y; + + if (IsKeyPressed(KEY_D)) { + debugEnabled = !debugEnabled; + Clay_SetDebugModeEnabled(debugEnabled); + } //---------------------------------------------------------------------------------- // Handle scroll containers Clay_Vector2 mousePosition = RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition()); - Clay_SetPointerPosition(mousePosition); + Clay_SetPointerState(mousePosition, IsMouseButtonDown(0)); + Clay_SetLayoutDimensions((Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }); if (!IsMouseButtonDown(0)) { scrollbarData.mouseDown = false; } @@ -166,6 +175,7 @@ void UpdateDrawFrame(void) // RENDERING --------------------------------- // currentTime = GetTime(); BeginDrawing(); + ClearBackground(BLACK); Clay_Raylib_Render(renderCommands); EndDrawing(); // printf("render time: %f ms\n", (GetTime() - currentTime) * 1000); @@ -177,7 +187,7 @@ int main(void) { uint64_t totalMemorySize = Clay_MinMemorySize(); Clay_Arena clayMemory = (Clay_Arena) { .label = CLAY_STRING("Clay Memory Arena"), .memory = malloc(totalMemorySize), .capacity = totalMemorySize }; Clay_SetMeasureTextFunction(Raylib_MeasureText); - Clay_Initialize(clayMemory); + Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }); Clay_Raylib_Initialize(FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT); profilePicture = LoadTextureFromImage(LoadImage("resources/profile-picture.png")); Raylib_fonts[FONT_ID_BODY_24] = (Raylib_Font) { diff --git a/generator/array_set.template.c b/generator/array_set.template.c index 4729b04..36b560f 100644 --- a/generator/array_set.template.c +++ b/generator/array_set.template.c @@ -3,7 +3,9 @@ void $NAME$_Set($NAME$ *array, int index, $TYPE$ value) { array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; } else { - Clay__StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.")); + if (Clay__warningsEnabled) { + Clay__WarningArray_Add(&Clay_warnings, (Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); + } #ifdef CLAY_OVERFLOW_TRAP raise(SIGTRAP); #endif diff --git a/renderers/raylib/clay_renderer_raylib.c b/renderers/raylib/clay_renderer_raylib.c index c9fb80f..7c66ecb 100644 --- a/renderers/raylib/clay_renderer_raylib.c +++ b/renderers/raylib/clay_renderer_raylib.c @@ -102,6 +102,7 @@ static inline Clay_Dimensions Raylib_MeasureText(Clay_String *text, Clay_TextEle float textHeight = config->fontSize; Font fontToUse = Raylib_fonts[config->fontId].font; + float scaleFactor = config->fontSize/(float)fontToUse.baseSize; for (int i = 0; i < text->length; ++i) { @@ -117,7 +118,7 @@ static inline Clay_Dimensions Raylib_MeasureText(Clay_String *text, Clay_TextEle maxTextWidth = fmax(maxTextWidth, lineTextWidth); - textSize.width = maxTextWidth / 2; + textSize.width = maxTextWidth * scaleFactor; textSize.height = textHeight; return textSize; @@ -170,7 +171,7 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands) case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { Clay_RectangleElementConfig *config = renderCommand->config.rectangleElementConfig; if (config->cornerRadius.topLeft > 0) { - float radius = (config->cornerRadius.topLeft * 2) / (boundingBox.width > boundingBox.height) ? boundingBox.height : boundingBox.width; + float radius = (config->cornerRadius.topLeft * 2) / (float)((boundingBox.width > boundingBox.height) ? boundingBox.height : boundingBox.width); DrawRectangleRounded((Rectangle) { boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, CLAY_COLOR_TO_RAYLIB_COLOR(config->color)); } else { DrawRectangle(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, CLAY_COLOR_TO_RAYLIB_COLOR(config->color)); diff --git a/renderers/web/canvas2d/clay-canvas2d-renderer.html b/renderers/web/canvas2d/clay-canvas2d-renderer.html index 5a791a8..73f883c 100644 --- a/renderers/web/canvas2d/clay-canvas2d-renderer.html +++ b/renderers/web/canvas2d/clay-canvas2d-renderer.html @@ -111,6 +111,7 @@ { name: 'fontSize', type: 'uint16_t' }, { name: 'letterSpacing', type: 'uint16_t' }, { name: 'lineSpacing', type: 'uint16_t' }, + { name: 'wrapMode', type: 'uint32_t' }, { name: 'disablePointerEvents', type: 'uint8_t' } ] }; @@ -259,6 +260,18 @@ window.mouseDown = true; }); + document.addEventListener("keydown", (event) => { + if (event.key === "ArrowDown") { + window.arrowKeyDownPressedThisFrame = true; + } + if (event.key === "ArrowUp") { + window.arrowKeyUpPressedThisFrame = true; + } + if (event.key === "d") { + window.dKeyPressedThisFrame = true; + } + }); + const importObject = { clay: { measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => { @@ -288,26 +301,22 @@ } function renderLoopCanvas() { - // Note: Rendering to canvas needs to be scaled up by window.devicePixelRatio in both width and height. - // e.g. if we're working on a device where devicePixelRatio is 2, we need to render - // everything at width^2 x height^2 resolution, then scale back down with css to get the correct pixel density. + // Note: Rendering to canvas needs to be scaled up by window.devicePixelRatio in both width and height. + // e.g. if we're working on a device where devicePixelRatio is 2, we need to render + // everything at width^2 x height^2 resolution, then scale back down with css to get the correct pixel density. let capacity = memoryDataView.getUint32(scratchSpaceAddress, true); let length = memoryDataView.getUint32(scratchSpaceAddress + 4, true); let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true); - if (window.previousWidth !== window.innerWidth) { - window.canvasRoot.width = window.innerWidth * window.devicePixelRatio; - window.previousWidth = window.innerWidth; - } - if (window.previousHeight !== window.innerHeight) { - window.canvasRoot.height = window.innerHeight * window.devicePixelRatio; - window.previousHeight = window.innerHeight; - } + window.canvasRoot.width = window.innerWidth * window.devicePixelRatio; + window.canvasRoot.height = window.innerHeight * window.devicePixelRatio; + window.canvasRoot.style.width = window.innerWidth + 'px'; + window.canvasRoot.style.height = window.innerHeight + 'px'; let ctx = window.canvasContext; let scale = window.devicePixelRatio; for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition); let boundingBox = renderCommand.boundingBox; - switch (renderCommand.commandType.value) { + switch(renderCommand.commandType.value) { case (CLAY_RENDER_COMMAND_TYPE_NONE): { break; } @@ -315,7 +324,7 @@ let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let color = config.color; ctx.beginPath(); - window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; window.canvasContext.roundRect( boundingBox.x.value * scale, // x boundingBox.y.value * scale, // y @@ -326,7 +335,8 @@ ctx.closePath(); // Handle link clicks let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - if (linkContents.length > 0 && (window.mouseDown || window.touchDown) && instance.exports.Clay_PointerOver(renderCommand.id.value)) { + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { window.location.href = linkContents; } break; @@ -342,7 +352,7 @@ ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale); let color = config.top.color; ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, config.cornerRadius.topLeft.value * scale); ctx.stroke(); } @@ -352,7 +362,7 @@ let halfLineWidth = lineWidth / 2; let color = config.top.color; ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); ctx.stroke(); @@ -364,7 +374,7 @@ ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); let color = config.top.color; ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale, config.cornerRadius.topRight.value * scale); ctx.stroke(); } @@ -374,7 +384,7 @@ let lineWidth = config.right.width.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale); ctx.lineTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.topRight.value - halfLineWidth) * scale); ctx.stroke(); @@ -386,7 +396,7 @@ let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale); ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, config.cornerRadius.bottomRight.value * scale); ctx.stroke(); } @@ -396,7 +406,7 @@ let lineWidth = config.bottom.width.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.lineTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.stroke(); @@ -408,7 +418,7 @@ let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale, (boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale, config.cornerRadius.bottomLeft.value * scale); ctx.stroke(); } @@ -418,7 +428,7 @@ let lineWidth = config.left.width.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; - ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomLeft.value - halfLineWidth) * scale); ctx.lineTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.bottomRight.value + halfLineWidth) * scale); ctx.stroke(); @@ -434,14 +444,16 @@ ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`; let color = config.textColor; ctx.textBaseline = 'middle'; - ctx.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value})`; + ctx.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.fillText(textDecoder.decode(stringContents), boundingBox.x.value * scale, (boundingBox.y.value + boundingBox.height.value / 2 + 1) * scale); break; } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { + window.canvasContext.save(); window.canvasContext.beginPath(); window.canvasContext.rect(boundingBox.x.value * scale, boundingBox.y.value * scale, boundingBox.width.value * scale, boundingBox.height.value * scale); window.canvasContext.clip(); + window.canvasContext.closePath(); break; } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): { diff --git a/renderers/web/html/clay-html-renderer.html b/renderers/web/html/clay-html-renderer.html index 4f04ea4..f638291 100644 --- a/renderers/web/html/clay-html-renderer.html +++ b/renderers/web/html/clay-html-renderer.html @@ -51,7 +51,7 @@ .text { pointer-events: all; - white-space: nowrap; + white-space: pre; } @@ -279,12 +279,13 @@ document.addEventListener("keydown", (event) => { if (event.key === "ArrowDown") { - console.log("arrowdown"); + window.arrowKeyDownPressedThisFrame = true; } if (event.key === "ArrowUp") { - console.log("arrowup"); + window.arrowKeyUpPressedThisFrame = true; } }); + const importObject = { clay: { measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => { @@ -361,7 +362,7 @@ let elementData = elementCache[renderCommand.id.value]; element = elementData.element; if (Array.prototype.indexOf.call(parentElement.element.children, element) !== parentElement.nextElementIndex) { - if (parentElement.nextElementIndex === 0) { + if (parentElement.nextElementIndex === 0 || !parentElement.element.childNodes[parentElement.nextElementIndex - 1]) { parentElement.element.insertAdjacentElement('afterbegin', element); } else { parentElement.element.childNodes[parentElement.nextElementIndex - 1].insertAdjacentElement('afterend', element); @@ -390,7 +391,8 @@ let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(renderCommand.id.value)) { + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { window.location.href = linkContents; } if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {