diff --git a/README.md b/README.md index 01df96e..159584d 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,8 @@ Clay_LayoutConfig sidebarItemLayout = (Clay_LayoutConfig) { }; // Re-useable components are just normal functions -void SidebarItemComponent(int index) { - CLAY_RECTANGLE(CLAY_IDI("SidebarBlob", index), sidebarItemLayout, CLAY_RECTANGLE_CONFIG(.color = COLOR_ORANGE)) {} +void SidebarItemComponent() { + CLAY(CLAY_LAYOUT(sidebarItemLayout), CLAY_RECTANGLE({ .color = COLOR_ORANGE })) {} } // An example function to begin the "root" of your layout tree @@ -92,20 +92,23 @@ Clay_RenderCommandArray CreateLayout() { Clay_BeginLayout(); // An example of laying out a UI with a fixed width sidebar and flexible width main content - CLAY_RECTANGLE(CLAY_ID("OuterContainer"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .padding = {16, 16}, .childGap = 16), CLAY_RECTANGLE_CONFIG(.color = {250,250,255,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 = COLOR_LIGHT)) { - CLAY_RECTANGLE(CLAY_ID("ProfilePictureOuter"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW() }, .padding = {16, 16}, .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }), CLAY_RECTANGLE_CONFIG(.color = COLOR_RED)) { - CLAY_IMAGE(CLAY_ID("ProfilePicture"), CLAY_LAYOUT( .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }), CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .height = 60, .width = 60)) {} - CLAY_TEXT(CLAY_ID("ProfileTitle"), CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {255, 255, 255, 255})); + CLAY(CLAY_ID("OuterContainer"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .padding = {16, 16}, .childGap = 16 }), CLAY_RECTANGLE({ .color = {250,250,255,255} })) { + CLAY(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({ .color = COLOR_LIGHT }) + ) { + CLAY(CLAY_ID("ProfilePictureOuter"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW() }, .padding = {16, 16}, .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }), CLAY_RECTANGLE({ .color = COLOR_RED })) { + CLAY(CLAY_ID("ProfilePicture"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}), CLAY_IMAGE({ .imageData = &profilePicture, .height = 60, .width = 60 })) {} + CLAY_TEXT(CLAY_ID("ProfileTitle"), CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} })); } // Standard C code like loops etc work inside components for (int i = 0; i < 5; i++) { - SidebarItemComponent(i); + SidebarItemComponent(); } } - CLAY_RECTANGLE(CLAY_ID("MainContent"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }), CLAY_RECTANGLE_CONFIG(.color = COLOR_LIGHT)) {} + CLAY(CLAY_ID("MainContent"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }}), CLAY_RECTANGLE({ .color = COLOR_LIGHT })) {} } // ... }); @@ -149,16 +152,16 @@ For help starting out or to discuss clay, considering joining [the discord serve ## High Level Documentation ### Building UI Hierarchies -Clay UI hierarchies are built using C macros that allow _nested_ declarations, similar to other declarative UI systems like HTML. +Clay UIs are built using the C macro `CLAY()`. This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The `CLAY()` macro can also be _nested_, similar to other declarative UI systems like HTML. -This means that child elements are declared _inside_ their parent elements. Clay element macros work by opening a block: `{}` after calling the element macro (exactly like you would with an `if` statement or `for` loop), and declaring child components inside the braces. +Child elements are added by opening a block: `{}` after calling the `CLAY()` macro (exactly like you would with an `if` statement or `for` loop), and declaring child components inside the braces. ```C -// Parent element -CLAY_CONTAINER(id, layout) { +// Parent element with 8px of padding +CLAY(CLAY_LAYOUT({ .padding = 8 })) { // Child element 1 - CLAY_TEXT(id, text, config); - // Child element 2 - CLAY_RECTANGLE(id, layout, config) { + CLAY_TEXT(CLAY_STRING("Hello World"), CLAY_TEXT_CONFIG({ .fontSize = 16 })); + // Child element 2 with red background + CLAY(CLAY_RECTANGLE({ .color = COLOR_RED })) { // etc } } @@ -168,20 +171,21 @@ However, unlike HTML and other declarative DSLs, these macros are just C. As a r ```C // Re-usable "components" are just functions that declare more UI void ButtonComponent(Clay_String buttonText) { - CLAY_RECTANGLE(id, layout, config) { - CLAY_TEXT(id, buttonText, config); + // Red box button with 8px of padding + CLAY(CLAY_LAYOUT({ .padding = { 8, 8 }}), CLAY_RECTANGLE({ .color = COLOR_RED })) { + CLAY_TEXT(buttonText, textConfig); } } // Parent element -CLAY_CONTAINER(id, layout, { +CLAY(CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM })) { // Render a bunch of text elements for (int i = 0; i < textArray.length; i++) { - CLAY_TEXT(id, textArray.elements[i], config); + CLAY_TEXT(textArray.elements[i], textConfig); } // Only render this element if we're on a mobile screen if (isMobileScreen) { - CLAY_CONTAINER(id, layout) { + CLAY() { // etc } } @@ -192,9 +196,9 @@ CLAY_CONTAINER(id, layout, { ``` ### Configuring Layout and Styling UI Elements -Many of the element macros in Clay take a `Clay_LayoutConfig` as the second argument. Clay provides a convenience macro, [CLAY_LAYOUT()](#clay_layout) for easy construction of element styles. +The layout of clay elements is configured with the `CLAY_LAYOUT()` macro. ```C -CLAY_CONTAINER(id, CLAY_LAYOUT(.padding = {.x = 8, .y = 8}, .backgroundColor = {120, 120, 120, 255})) { +CLAY(CLAY_LAYOUT({ .padding = {.x = 8, .y = 8}, .backgroundColor = {120, 120, 120, 255} })) { // ... } ``` @@ -202,84 +206,100 @@ This macro isn't magic - all it's doing is wrapping the standard designated init See the [Clay_LayoutConfig](#clay_layout) API for the full list of options. -A `Clay_LayoutConfig` struct can be defined in file scope or elsewhere, as long as the lifetime ends after `EndLayout` is called. +A `Clay_LayoutConfig` struct can be defined in file scope or elsewhere, and reused. ```C // Define a style in the global / file scope -Clay_LayoutConfig reusableStyle = (Clay_LayoutConfig) {.backgroundColor = {120, 120, 120, 255}}; +Clay_LayoutConfig reusableStyle = (Clay_LayoutConfig) { .backgroundColor = {120, 120, 120, 255} }; -CLAY_CONTAINER(id, &reusableStyle) { +CLAY(CLAY_LAYOUT(reusableStyle)) { // ... } ``` -Some of the other element macros, such as [CLAY_TEXT()](#clay_text) and [CLAY_RECTANGLE()](#clay_rectangle) take an element-specific config object as their 3rd argument. These config objects also have convenience macros for constructing them, generally of the form [CLAY_TEXT_CONFIG()](#clay_text_config) or [CLAY_RECTANGLE_CONFIG()](#clay_rectangle_config): - -```C -CLAY_TEXT(id, CLAY_STRING("button text"), CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_LATO, .textColor = {255, 255, 255, 255})); -``` - -See the [Full API](#api) for details on the specific config macros. - ### Element IDs -All element macros take a `Clay_ElementId` ID as their first argument. Clay provides the [CLAY_ID()](#clay_id) macro to generate these IDs as string hashes: +Clay elements can optionally be tagged with a unique identifier using [CLAY_ID()](#clay_id). + ```C // Will always produce the same ID from the same input string -CLAY_CONTAINER(CLAY_ID("OuterContainer"), style) {} -``` +CLAY(CLAY_ID("OuterContainer"), style) {} +``` -To avoid having to construct dynamic strings at runtime to differentiate ids, clay provides the [CLAY_IDI(string, index)](#clay_idi) macro to generate different ids from a single input string. Think of IDI as "**ID** + **I**ndex" +Element IDs have two main use cases. Firstly, tagging an element with an ID allows you to query information about the element later, such as its [mouseover state](#clay-pointerover) or dimensions. + +Secondly, IDs are visually useful when attempting to read and modify UI code, as well as when using the built-in [debug tools](#debug-tools). + +To avoid having to construct dynamic strings at runtime to differentiate ids in loops, clay provides the [CLAY_IDI(string, index)](#clay_idi) macro to generate different ids from a single input string. Think of IDI as "**ID** + **I**ndex" ```C // This is the equivalent of calling CLAY_ID("Item0"), CLAY_ID("Item1") etc for (int index = 0; index < items.length; index++) { - CLAY_CONTAINER(CLAY_IDI("Item", index), style) {} + CLAY(CLAY_IDI("Item", index)) {} } ``` -**_Generally, you should try to keep IDs unique if possible._** - -This ID is used for querying mouse / pointer events, and will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect) +This ID (or, if not provided, an auto generated ID) will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect) ### Mouse, Touch and Pointer Interactions -Clay provides a very simple unified API for handling mouse and pointer interactions, with specific handling left to user code. +Clay provides several functions for handling mouse and pointer interactions. All pointer interactions depend on the function `void Clay_SetPointerState(Clay_Vector2 position)` being called after each mouse position update and before any other clay functions. -The function `bool Clay_PointerOver(Clay_ElementId id)` takes an element id that was used during layout creation and returns a bool representing whether the current pointer position is within its bounding box. +**During UI declaration** + +The function `bool Clay_Hovered()` can be called during element construction or in the body of an element, and returns `true` if the mouse / pointer is over the currently open element. + +```C +// An orange button that turns blue when hovered +CLAY(CLAY_RECTANGLE(.color = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE)) { + bool buttonHovered = Clay_Hovered(); + CLAY_TEXT(buttonHovered ? CLAY_STRING("Hovered") : CLAY_STRING("Hover me!"), headerTextConfig); +} +``` + +The function `void Clay_OnHover()` allows you to attach a function pointer to the currently open element, which will be called if the mouse / pointer is over the element. + +```C +void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData) { + ButtonData *buttonData = (ButtonData *)userData; + // Pointer state allows you to detect mouse down / hold / release + if (pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME) { + // Do some click handling + NavigateTo(buttonData->link); + } +} + +ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" }; + +// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries +CLAY(CLAY_LAYOUT({ .padding = { 8, 8 }}), Clay_OnHover(HandleButtonInteraction, &buttonData)) { + CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig); +} +``` + +**Before / After UI declaration** + +If you want to query mouse / pointer overlaps outside layout declarations, you can use the function `bool Clay_PointerOver(Clay_ElementId id)`, which takes an [element id](#element-ids) and returns a bool representing whether the current pointer position is within its bounding box. ```C // Reminder: Clay_SetPointerState must be called before functions that rely on pointer position otherwise it will have no effect Clay_Vector2 mousePosition = { x, y }; Clay_SetPointerState(mousePosition); // ... // If profile picture was clicked -if (mouseButtonDown(0) && Clay_PointerOver(CLAY_ID("ProfilePicture"))) { +if (mouseButtonDown(0) && Clay_PointerOver(Clay_GetElementId("ProfilePicture"))) { // Handle profile picture clicked } ``` -Querying `Clay_PointerOver` also works _during_ layout construction, and can be used as a convenient way for applying "hover" states, for example: - -```C -// Reminder: Clay_SetPointerState must be called before functions that rely on pointer position otherwise it will have no effect -Clay_Vector2 mousePosition = { x, y }; -Clay_SetPointerState(mousePosition); -// ... -Clay_ElementId buttonId = CLAY_ID("HeaderButton"); -// An orange button that turns blue when hovered -CLAY_CONTAINER(buttonId, CLAY_LAYOUT(.backgroundColor = Clay_PointerOver(buttonId) ? COLOR_BLUE : COLOR_ORANGE)) { - CLAY_TEXT(CLAY_IDI("Button", index), text, &headerTextConfig); -} -``` -Note that the bounding box queried by `Clay_PointerOver` is from the last frame. This shouldn't make a difference except in the case of animations that move at high speed. +Note that the bounding box queried by `Clay_PointerOver` is from the last frame. This generally shouldn't make a difference except in the case of animations that move at high speed. If this is an issue for you, performing layout twice per frame with the same data will give you the correct interaction the second time. -### Scrolling Containers +### Scrolling Elements -Scrolling containers are defined with the `CLAY_SCROLL_CONTAINER` element macro and function just like normal containers, however to make scroll containers respond to mouse wheel and scroll events, two functions need to be called: +Elements are configured as scrollable with the `CLAY_SCROLL` macro. To make scroll containers respond to mouse wheel and scroll events, two functions need to be called before `BeginLayout()`: ```C -// Reminder: Clay_SetPointerState must be called before Clay_UpdateScrollContainers otherwise it will have no effect Clay_Vector2 mousePosition = { x, y }; +// Reminder: Clay_SetPointerState must be called before Clay_UpdateScrollContainers otherwise it will have no effect Clay_SetPointerState(mousePosition); // Clay_UpdateScrollContainers needs to be called before Clay_BeginLayout for the position to avoid a 1 frame delay Clay_UpdateScrollContainers( @@ -290,33 +310,32 @@ Clay_UpdateScrollContainers( // ... ``` -More specific details can be found in the full [Scroll Container API](#clay_scroll_container). +More specific details can be found in the full [Scroll API](#clay_scroll). -### Floating Containers ("Absolute" Positioning) +### Floating Elements ("Absolute" Positioning) All standard elements in clay are laid out on top of, and _within_ their parent, positioned according to their parent's layout rules, and affect the positioning and sizing of siblings. -**"Floating"** elements are defined with the `CLAY_FLOATING_CONTAINER` element macro and don't affect the parent they are defined in, or the position of their siblings. +**"Floating"** is configured with the `CLAY_FLOATING()` macro. Floating elements don't affect the parent they are defined in, or the position of their siblings. They also have a **z-index**, and as a result can intersect and render over the top of other elements. -Aside from positioning, `CLAY_FLOATING_CONTAINER` elements function like standard `CLAY_CONTAINER` elements. A classic example use case for floating elements is tooltips and modals. ```C // The two text elements will be laid out top to bottom, and the floating container // will be attached to "Outer" -CLAY_CONTAINER(CLAY_ID("Outer"), CLAY_LAYOUT(.layoutDirection = TOP_TO_BOTTOM)) { +CLAY(CLAY_ID("Outer"), CLAY_LAYOUT({ .layoutDirection = TOP_TO_BOTTOM })) { CLAY_TEXT(CLAY_ID("Button"), text, &headerTextConfig); - CLAY_FLOATING(CLAY_ID("Tooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG) {} + CLAY(CLAY_ID("Tooltip"), CLAY_FLOATING()) {} CLAY_TEXT(CLAY_ID("Button"), text, &headerTextConfig); } ``` -More specific details can be found in the full [Floating Container API](#clay_floating_container). +More specific details can be found in the full [Floating API](#clay_floating). ### Laying Out Your Own Custom Elements -Clay only supports a simple set of UI element primitives, such as rectangles, text and images. Clay provides a simple, singular API for layout out custom elements: +Clay only supports a simple set of UI element primitives, such as rectangles, text and images. Clay provides a singular API for layout out custom elements: ```C // Extend CLAY_CUSTOM_ELEMENT_CONFIG with your custom data #define CLAY_EXTEND_CONFIG_CUSTOM struct t_CustomElementData customData; @@ -336,9 +355,9 @@ typedef struct t_CustomElementData { Model myModel = Load3DModel(filePath); CustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel } // ... -CLAY_CONTAINER(id, style) { +CLAY() { // This config is type safe and contains the CustomElementData struct - CLAY_CUSTOM_ELEMENT(id, layout, CLAY_CUSTOM_ELEMENT_CONFIG(.customData = { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel })) {} + CLAY(CLAY_CUSTOM_ELEMENT({ .customData = { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel } })) {} } // Later during your rendering @@ -364,7 +383,7 @@ switch (renderCommand->commandType) { } ``` -More specific details can be found in the full [Custom Container API](#clay_custom_element). +More specific details can be found in the full [Custom Element API](#clay_custom_element). ### Retained Mode Rendering Clay was originally designed for [Immediate Mode](https://www.youtube.com/watch?v=Z1qyvQsjK5Y) rendering - where the entire UI is redrawn every frame. This may not be possible with your platform, renderer design or performance constraints. @@ -391,7 +410,7 @@ The supported directives are: - `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 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_RECTANGLE` - Provide additional struct members to `CLAY_RECTANGLE` 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. - `CLAY_EXTEND_CONFIG_CUSTOM` - Provide additional struct members to `CLAY_IMAGE_CONFIG` that will be passed through with output render commands. @@ -435,7 +454,7 @@ _The official Clay website with debug tooling visible_ `Clay_MinMemorySize` -> `Clay_CreateArenaWithCapacityAndMemory` -> `Clay_SetMeasureTextFunction` -> `Clay_Initialize` **Each Frame** -`Clay_SetLayoutDimensions` -> `Clay_SetPointerState` -> `Clay_UpdateScrollContainers` -> `Clay_BeginLayout` -> `CLAY_CONTAINER() etc...` -> `Clay_EndLayout` +`Clay_SetLayoutDimensions` -> `Clay_SetPointerState` -> `Clay_UpdateScrollContainers` -> `Clay_BeginLayout` -> `CLAY() etc...` -> `Clay_EndLayout` ### Clay_MinMemorySize @@ -499,6 +518,36 @@ Prepares clay to calculate a new layout. Called each frame / layout **before** a Ends declaration of element macros and calculates the results of the currrent layout. Renders a [Clay_RenderCommandArray](#clay_rendercommandarrray) containing the results of the layout calculation. +### Clay_Hovered + +`bool Clay_Hovered()` + +Called **during** layout declaration, and returns `true` if the pointer position previously set with `Clay_SetPointerState` is inside the bounding box of the currently open element. Note: this is based on the element's position from the **last** frame. + +### Clay_OnHover + +`void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData), intptr_t userData)` + +Called **during** layout declaration, this function allows you to attach a function pointer to the currently open element that will be called once per layout if the pointer position previously set with `Clay_SetPointerState` is inside the bounding box of the currently open element. See [Clay_PointerInfo](#clay-pointer-info) for more information on the `pointerInfo` argument. + +```C +void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData) { + ButtonData *buttonData = (ButtonData *)userData; + // Pointer state allows you to detect mouse down / hold / release + if (pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME) { + // Do some click handling + NavigateTo(buttonData->link); + } +} + +ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" }; + +// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries +CLAY(CLAY_LAYOUT({ .padding = { 8, 8 }}), Clay_OnHover(HandleButtonInteraction, &buttonData)) { + CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig); +} +``` + ### Clay_PointerOver `bool Clay_PointerOver(Clay_ElementId id)` @@ -511,307 +560,104 @@ Returns `true` if the pointer position previously set with `Clay_SetPointerState Returns [Clay_ScrollContainerData](#clay_scrollcontainerdata) for the scroll container matching the provided ID. This function allows imperative manipulation of scroll position, allowing you to build things such as scroll bars, buttons that "jump" to somewhere in a scroll container, etc. +### Clay_GetElementId + +`Clay_ElementId Clay_GetElementId(Clay_String idString)` + +Returns a [Clay_ElementId](#clay-elementid) for the provided id string, used for querying element info such as mouseover state, scroll container data, etc. + ## Element Macros -### CLAY_CONTAINER +### CLAY() **Usage** -`CLAY_CONTAINER(Clay_ElementId id, Clay_LayoutConfig *layoutConfig);` +`CLAY(...configuration) { ...children }` **Lifecycle** -`Clay_BeginLayout()` -> `CLAY_CONTAINER()` -> `Clay_EndLayout()` +`Clay_BeginLayout()` -> `CLAY()` -> `Clay_EndLayout()` **Notes** -**CONTAINER** is a generic rectangular container that supports child elements. It uses a [Clay_LayoutConfig](#clay_layout) for styling and layout. +**CLAY** opens a generic empty container, that is configurable and supports nested children. **Examples** ```C -// Define a container with 16px of x and y padding -CLAY_CONTAINER(CLAY_ID("SideBar"), CLAY_LAYOUT(.padding = {16, 16})) { - // A nested child container - CLAY_CONTAINER(CLAY_ID("SideBar"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16)) { - // Children laid out top to bottom +// Define an element with 16px of x and y padding +CLAY(CLAY_ID("Outer"), CLAY_LAYOUT({ .padding = {16, 16} })) { + // A nested child element + CLAY(CLAY_ID("SideBar"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 })) { + // Children laid out top to bottom with a 16 px gap between them + } + // A vertical scrolling container with a colored background + CLAY( + CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 }) + CLAY_RECTANGLE({ .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) }) + CLAY_SCROLL({ .vertical = true }) + ) { + // child elements } } ``` -**Rendering** +### CLAY_ID -`CLAY_CONTAINER` elements will not generate any render commands. - -### CLAY_TEXT **Usage** -`CLAY_TEXT(Clay_ElementId id, Clay_String textContents, Clay_TextElementConfig *textConfig);` +`CLAY(CLAY_ID(char* idString)) {}` **Lifecycle** -`Clay_BeginLayout()` -> `CLAY_TEXT()` -> `Clay_EndLayout()` +`Clay_BeginLayout()` -> `CLAY(` -> `CLAY_ID()` -> `)` -> `Clay_EndLayout()` **Notes** -**TEXT** is a measured, auto-wrapped text element. It uses [Clay_TextElementConfig](#clay_text_config) to configure text specific options. +**CLAY_ID()** is used to generate and attach a [Clay_ElementId](#clay_elementid) to a layout element during declaration. -Note that `Clay_TextElementConfig` uses `uint32_t fontId`. Font ID to font asset mapping is managed in user code and passed to render commands. +To regenerate the same ID outside of layout declaration when using utility functions such as [Clay_PointerOver](#clay_pointerover), use the [Clay_GetElementId](#clay_getelementid) function. **Examples** ```C -// Define a font somewhere in your code -const uint32_t FONT_ID_LATO = 3; -// .. -CLAY_TEXT(CLAY_ID("Username"), CLAY_STRING("John Smith"), CLAY_TEXT_CONFIG(.fontId = FONT_ID_LATO, .fontSize = 24, .textColor = {255, 0, 0, 255})); -// Rendering example -Font fontToUse = LoadedFonts[renderCommand->elementConfig.textElementConfig->fontId]; -``` +// Tag a button with the Id "Button" +CLAY( + CLAY_ID("Button"), + CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW() }, .padding = {16, 16}, .childGap = 16) }) +) { + // ...children +} -**Rendering** - -Element is subject to [culling](#visibility-culling). Otherwise, multiple `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_TEXT` may be created, one for each wrapped line of text. - -`Clay_RenderCommand.textContent` will be populated with a `Clay_String` _slice_ of the original string passed in (i.e. wrapping doesn't reallocate, it just returns a `Clay_String` pointing to the start of the new line with a `length`) - -### CLAY_IMAGE -**Usage** - -`CLAY_IMAGE(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_ImageElementConfig *imageConfig);` - -**Lifecycle** - -`Clay_BeginLayout()` -> `CLAY_IMAGE()` -> `Clay_EndLayout()` - -**Notes** - -**IMAGE_CONTAINER** is a used to layout images, and can optionally have children. It uses [Clay_LayoutConfig](#clay_layout) for styling and layout, and [Clay_ImageElementConfig](#clay_image_config) to configure image specific options. - -**Examples** - -```C -// Load an image somewhere in your code -Image profilePicture = LoadImage("profilePicture.png"); -// .. -CLAY_IMAGE(CLAY_ID("ProfilePicture"), &CLAY_LAYOUT_DEFAULT, CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .height = 60, .width = 60)) {} -// Rendering example -Image *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData; -``` - -**Rendering** - -Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE` will be created. The user will need to access `renderCommand->elementConfig.imageElementConfig->imageData` to retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blend `renderCommand->color` with the image. - -### CLAY_SCROLL_CONTAINER -**Usage** - -`CLAY_IMAGE(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_ScrollElementConfig *scrollConfig);` - -**Lifecycle** - -`Clay_SetPointerState()` -> `Clay_UpdateScrollContainers()` -> `Clay_BeginLayout()` -> `CLAY_SCROLL_CONTAINER()` -> `Clay_EndLayout()` - -**Notes** - -**SCROLL_CONTAINER** creates a masked container that allows layout of children to extend beyond its boundaries. It uses [Clay_LayoutConfig](#clay_layout) for styling and layout, and [Clay_ScrollElementConfig](#clay_scroll_config) to configure scroll specific options. - -Note: In order to process scrolling based on pointer position and mouse wheel or touch interactions, you must call `Clay_SetPointerState()` and `Clay_UpdateScrollContainers()` _before_ calling `BeginLayout`. - -**Examples** - -```C -CLAY_SCROLL_CONTAINER(CLAY_ID("MainContent"), &CLAY_LAYOUT_DEFAULT, CLAY_SCROLL_CONFIG(.vertical = true)) { - // Create child content with a fixed height of 5000 - CLAY_CONTAINER(CLAY_ID("ScrollInner"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(5000) })) {} +// Later on outside of layout code +bool buttonIsHovered = Clay_IsPointerOver(Clay_GetElementId("Button")); +if (buttonIsHovered && leftMouseButtonPressed) { + // ... do some click handling } ``` -**Rendering** +### CLAY_IDI() -Scroll containers will result in two render commands: -- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START`, which should create a rectangle mask with its `boundingBox` and is **not** subject to [culling](#visibility-culling) -- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END`, which disables the previous rectangle mask and is **not** subject to [culling](#visibility-culling) +`Clay_ElementId CLAY_IDI(char *label, int index)` -### CLAY_BORDER_CONTAINER -**Usage** - -`CLAY_BORDER_CONTAINER(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_BorderElementConfig *borderConfig);` - -**Lifecycle** - -`Clay_BeginLayout()` -> `CLAY_BORDER_CONTAINER()` -> `Clay_EndLayout()` - -**Notes** - -**BORDER_CONTAINER** is functionally identical to **CONTAINER** but also allows configuration of a border around the element. It uses [Clay_LayoutConfig](#clay_layout) for styling and layout, and [Clay_BorderElementConfig](#clay_border_config) to configure border specific options. - -**Examples** - -```C -// 300x300 container with a 1px red border around all the edges -CLAY_BORDER_CONTAINER(CLAY_ID("OuterBorder"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300)}), CLAY_BORDER_CONFIG_OUTSIDE(.color = COLOR_RED, .width = 1)) { - // ... -} - -// Container with a 3px yellow bottom border -CLAY_BORDER_CONTAINER(CLAY_ID("OuterBorder"), &CLAY_LAYOUT_DEFAULT, CLAY_BORDER_CONFIG(.bottom = { .color = COLOR_YELLOW, .width = 3 })) { - // ... -} - -// Container with a 5px curved border around the edges, and a 5px blue border between all children laid out top to bottom -CLAY_BORDER_CONTAINER(CLAY_ID("OuterBorder"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM), CLAY_BORDER_CONFIG_ALL_RADIUS({ .color = COLOR_BLUE, .width = 5 })) { - // Child - // -- border will be here -- - // Child -} -``` - -**Rendering** - -Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_BORDER` representing the container will be created. -Rendering of borders and rounded corners is left up to the user. See the provided [Raylib Renderer](https://github.com/nicbarker/clay/tree/main/renderers/raylib) for examples of how to draw borders using line and curve primitives. - -### CLAY_FLOATING_CONTAINER -**Usage** - -`CLAY_FLOATING_CONTAINER(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingConfig);` - -**Lifecycle** - -`Clay_BeginLayout()` -> `CLAY_FLOATING_CONTAINER()` -> `Clay_EndLayout()` - -**Notes** - -**FLOATING_CONTAINER** defines an element that "floats" above other content. Typical use-cases include tooltips and modals. - -Floating containers: - -- With the [default configuration](#clay_floating_config), attach to the top left corner of their "parent" -- Don't affect the width and height of their parent -- Don't affect the positioning of sibling elements -- Depending on their z-index can appear above or below other elements, partially or completely occluding them -- Apart from positioning, function just like standard `CLAY_CONTAINER` elements - including expanding to fit their children, etc. - -The easiest mental model to use when thinking about floating containers is that they are a completely separate UI hierarchy, attached to a specific x,y point on their "parent". - -Floating elements use [Clay_LayoutConfig](#clay_layout) for styling and layout, and [Clay_FloatingElementConfig](#clay_floating_config) to configure specific options. - -**Examples** - -```C -// Horizontal container with three option buttons -CLAY_CONTAINER(CLAY_ID("OptionsList"), CLAY_LAYOUT(.childGap = 16)) { - CLAY_RECTANGLE(CLAY_IDI("Option", 1), CLAY_LAYOUT(.padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = COLOR_BLUE)) { - CLAY_TEXT(CLAY_IDI("OptionText", 1), CLAY_STRING("Option 1"), CLAY_TEXT_CONFIG()); - } - CLAY_RECTANGLE(CLAY_IDI("Option", 2), CLAY_LAYOUT(.padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = COLOR_BLUE)) { - CLAY_TEXT(CLAY_IDI("OptionText", 2), CLAY_STRING("Option 2"), CLAY_TEXT_CONFIG()); - // Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements - CLAY_FLOATING_CONTAINER(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP })) { - CLAY_TEXT(CLAY_IDI("OptionTooltipText", 1), CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG()); - } - } - CLAY_RECTANGLE(CLAY_IDI("Option", 3), CLAY_LAYOUT(.padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = COLOR_BLUE)) { - CLAY_TEXT(CLAY_IDI("OptionText", 3), CLAY_STRING("Option 3"), CLAY_TEXT_CONFIG()); - } -} - -// Floating containers can also be declared elsewhere in a layout, to avoid branching or polluting other UI -for (int i = 0; i < 1000; i++) { - CLAY_CONTAINER(CLAY_IDI("Option", i + 1), &CLAY_LAYOUT_DEFAULT) { - // ... - } -} -// Note the use of "parentId". -// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements -CLAY_FLOATING_CONTAINER(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER })) { - CLAY_TEXT(CLAY_IDI("OptionTooltipText", 1), CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG()); -} -``` - -When using `.parentId`, the floating container can be declared anywhere after `BeginLayout` and before `EndLayout`. The target element matching the `.parentId` doesn't need to exist when `CLAY_FLOATING_CONTAINER` is called. - -**Rendering** - -`CLAY_FLOATING_CONTAINER` elements will not generate any render commands. - -### CLAY_CUSTOM_ELEMENT -**Usage** - -`CLAY_CUSTOM_ELEMENT(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customConfig);` - -**Lifecycle** - -`Clay_BeginLayout()` -> `CLAY_CUSTOM_ELEMENT()` -> `Clay_EndLayout()` - -**Notes** - -**CUSTOM_ELEMENT** uses [Clay_LayoutConfig](#clay_layout) for styling and layout, and allows the user to pass custom data to the renderer. - -**Examples** -```C -// Extend CLAY_CUSTOM_ELEMENT_CONFIG with your custom data -#define CLAY_EXTEND_CONFIG_CUSTOM struct t_CustomElementData customData; -// Extensions need to happen _before_ the clay include -#include "clay.h" - -// A rough example of how you could handle laying out 3d models in your UI -typedef struct t_CustomElementData { - CustomElementType type; - union { - Model model; - Video video; - // ... - }; -} CustomElementData; - -Model myModel = Load3DModel(filePath); -CustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel } -// ... -CLAY_CONTAINER(id, style) { - // This config is type safe and contains the CustomElementData struct - CLAY_CUSTOM_ELEMENT(id, style, CLAY_CUSTOM_ELEMENT_CONFIG(.customData = { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel })) {} -} - -// Later during your rendering -switch (renderCommand->commandType) { - // ... - case CLAY_RENDER_COMMAND_TYPE_CUSTOM: { - // Your extended struct is passed through - CustomElementData *data = renderCommand->elementConfig.customElementConfig->customData; - if (!customElement) continue; - switch (customElement->type) { - case CUSTOM_ELEMENT_TYPE_MODEL: { - // Render your 3d model here - break; - } - case CUSTOM_ELEMENT_TYPE_VIDEO: { - // Render your video here - break; - } - // ... - } - break; - } -} -``` - -**Rendering** - -Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM` will be created. -The user will need to access [Clay_CustomElementConfig](#clay_custom_element_config) to retrieve custom data referenced during layout creation. - -## Config Macros - -A number of clay element macros take element-specific **config** structs. +An offset version of [CLAY_ID](#clay_id). Generates a [Clay_ElementId](#clay_elementid) 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. ### CLAY_LAYOUT -**CLAY_LAYOUT()** is used for configuring layout for most clay elements. +**Usage** + +`CLAY(CLAY_LAYOUT(...layout config)) {}` + +**Lifecycle** + +`Clay_BeginLayout()` -> `CLAY(` -> `CLAY_LAYOUT()` -> `)` -> `Clay_EndLayout()` + +**Notes** + +**CLAY_LAYOUT()** is used for configuring _layout_ options (i.e. options that affect the final position and size of an element, its parents, siblings, and children) **Struct API (Pseudocode)** ```C -// CLAY_LAYOUT(.member = value) supports these options +// CLAY_LAYOUT({ .member = value }) supports these options Clay_LayoutConfig { Clay_LayoutDirection layoutDirection = CLAY_LEFT_TO_RIGHT (default) | CLAY_TOP_TO_BOTTOM; Clay_Padding padding { @@ -834,7 +680,7 @@ As with all config macros, `CLAY_LAYOUT()` accepts designated initializer syntax **`.layoutDirection`** - `Clay_LayoutDirection` -`CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM)` +`CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM })` Controls the axis / direction in which child elements are laid out. Available options are `CLAY_LEFT_TO_RIGHT` (default) and `CLAY_TOP_TO_BOTTOM`. @@ -846,7 +692,7 @@ _Did you know that "left to right" and "top to bottom" both have 13 letters?_ **`.padding`** - `Clay_Padding` -`CLAY_LAYOUT(.padding = { .x = 16, .y = 16 })` +`CLAY_LAYOUT({ .padding = { .x = 16, .y = 16 } })` Controls horizontal and vertical white-space "padding" around the **outside** of child elements. @@ -856,7 +702,7 @@ Controls horizontal and vertical white-space "padding" around the **outside** of **`.childGap`** - `uint16_t` -`CLAY_LAYOUT(.childGap = 16)` +`CLAY_LAYOUT({ .childGap = 16 })` Controls the white-space **between** child elements as they are laid out. When `.layoutDirection` is `CLAY_LEFT_TO_RIGHT` (default), this will be horizontal space, whereas for `CLAY_TOP_TO_BOTTOM` it will be vertical space. @@ -866,7 +712,7 @@ Controls the white-space **between** child elements as they are laid out. When ` **`.childAlignment`** - `Clay_ChildAlignment` -`CLAY_LAYOUT(.childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER })` +`CLAY_LAYOUT({ .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } })` Controls the alignment of children relative to the height and width of the parent container. Available options are: ```C @@ -880,7 +726,7 @@ Controls the alignment of children relative to the height and width of the paren **`.sizing`** - `Clay_Sizing` -`CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) })` +`CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } })` Controls how final width and height of element are calculated. The same configurations are available for both the `.width` and `.height` axis. There are several options: @@ -900,20 +746,29 @@ Controls how final width and height of element are calculated. The same configur **Example Usage** ```C -CLAY_CONTAINER(CLAY_ID("Button"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW() }, .padding = {16, 16}, .childGap = 16)) { +CLAY(CLAY_ID("Button"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW() }, .padding = {16, 16}, .childGap = 16) }) { // Children will be laid out vertically with 16px of padding around and between } ``` -### CLAY_RECTANGLE_CONFIG +### CLAY_RECTANGLE +**Usage** -**CLAY_RECTANGLE_CONFIG()** is used for configuring rendering for [CLAY_RECTANGLE()](#clay_rectangle) elements. The config will be passed through to render commands as `Clay_RenderCommand.config.rectangleElementConfig` +`CLAY(CLAY_RECTANGLE(...rectangle config)) {}` + +**Lifecycle** + +`Clay_BeginLayout()` -> `CLAY(` -> `CLAY_RECTANGLE()` -> `)` -> `Clay_EndLayout()` + +**Notes** + +**RECTANGLE** configures a clay element to background-fill its bounding box with a color. It uses [Clay_RectangleElementConfig](#clay_rectangleelementconfig) for rectangle specific options. **Struct API (Pseudocode)** ```C -// CLAY_RECTANGLE_CONFIG(.member = value) supports these options -Clay_RectangleConfig { +// CLAY_RECTANGLE({ .member = value }) supports these options +Clay_RectangleElementConfig { Clay_Color color { float r; float g; float b; float a; }; @@ -925,7 +780,7 @@ Clay_RectangleConfig { } ``` -As with all config macros, `CLAY_RECTANGLE_CONFIG()` accepts designated initializer syntax and provides default values for any unspecified struct members. +As with all config macros, `CLAY_RECTANGLE()` accepts designated initializer syntax and provides default values for any unspecified struct members. **Extension** @@ -939,7 +794,7 @@ The underlying `Clay_RectangleElementConfig` can be extended with new members by **`.color`** - `Clay_Color` -`CLAY_RECTANGLE_CONFIG(.color = {120, 120, 120, 255})` +`CLAY_RECTANGLE({ .color = {120, 120, 120, 255} })` Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout. @@ -947,20 +802,55 @@ Conventionally accepts `rgba` float values between 0 and 255, but interpretation **`.cornerRadius`** - `float` -`CLAY_RECTANGLE_CONFIG(.cornerRadius = 16)` +`CLAY_RECTANGLE({ .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 })` Defines the radius in pixels for the arc of rectangle corners (`0` is square, `rectangle.width / 2` is circular). -Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER_CONFIG(.cornerRadius = CLAY_CORNER_RADIUS(10))` +Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER({ .cornerRadius = CLAY_CORNER_RADIUS(10) })` -### CLAY_TEXT_CONFIG +**Rendering** -**CLAY_TEXT_CONFIG()** is a macro used to create and store `Clay_TextElementConfig` structs, which are for configuring [CLAY_TEXT](#clay_text) elements. The config used in declaration will be passed both as an argument to the user-provided `Clay_MeasureText(Clay_String *text, Clay_TextElementConfig *config)` function as well as the in the final output as `Clay_RenderCommand.config.textElementConfig`. +Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE` will be created, with `renderCommand->elementConfig.rectangleElementConfig` containing a pointer to the element's Clay_RectangleElementConfig. + +**Examples** + +```C +// Declare a reusable rectangle config, with a purple color and 10px rounded corners +Clay_RectangleElementConfig rectangleConfig = (Clay_RectangleElementConfig) { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) }; +// Declare a rectangle element using a reusable config +CLAY(CLAY_RECTANGLE(rectangleConfig)) {} +// Declare a retangle element using an inline config +CLAY(CLAY_RECTANGLE({ .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) { + // child elements +} +// Declare a scrolling container with a colored background +CLAY( + CLAY_RECTANGLE({ .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) }) + CLAY_SCROLL({ .vertical = true }) +) { + // child elements +} +``` + +### CLAY_TEXT +**Usage** + +`CLAY_TEXT(Clay_String textContents, Clay_TextElementConfig *textConfig);` + +**Lifecycle** + +`Clay_BeginLayout()` -> `CLAY_TEXT()` -> `Clay_EndLayout()` + +**Notes** + +**TEXT** is a measured, auto-wrapped text element. It uses `Clay_TextElementConfig` to configure text specific options. + +Note that `Clay_TextElementConfig` uses `uint32_t fontId`. Font ID to font asset mapping is managed in user code and passed to render commands. **Struct API (Pseudocode)** ```C -// CLAY_TEXT_CONFIG(.member = value) supports these options +// CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .member = value })) supports these options Clay_TextElementConfig { Clay_Color textColor { float r; float g; float b; float a; @@ -970,7 +860,7 @@ Clay_TextElementConfig { uint16_t letterSpacing; uint16_t lineHeight; Clay_TextElementConfigWrapMode wrapMode { - CLAY_TEXT_WRAP_WORDS (default), + CLAY_TEXT_WRAP_WORDS (default), CLAY_TEXT_WRAP_NEWLINES, CLAY_TEXT_WRAP_NONE, }; @@ -1044,17 +934,37 @@ Available options are: - `CLAY_TEXT_WRAP_NEWLINES` - will only wrap when encountering newline characters. - `CLAY_TEXT_WRAP_NONE` - Text will never wrap even if its container is compressed beyond the text measured width. ----**** +--- + +**Examples** -**Example Usage** ```C -// A 24px, red text element that says "John Smith" -CLAY_TEXT(CLAY_ID("Username"), CLAY_STRING("John Smith"), CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = 2, .textColor = {255, 0, 0, 255})); +// Define a font somewhere in your code +const uint32_t FONT_ID_LATO = 3; +// .. +CLAY_TEXT(CLAY_STRING("John Smith"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_LATO, .fontSize = 24, .textColor = {255, 0, 0, 255} })); +// Rendering example +Font fontToUse = LoadedFonts[renderCommand->elementConfig.textElementConfig->fontId]; ``` -### CLAY_IMAGE_CONFIG +**Rendering** -**CLAY_IMAGE_CONFIG()** is a macro used to create and store `Clay_ImageElementConfig` structs, which are for configuring [CLAY_IMAGE](#clay_image) elements. The config will be passed through to render commands as `Clay_RenderCommand.config.imageElementConfig` +Element is subject to [culling](#visibility-culling). Otherwise, multiple `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_TEXT` may be created, one for each wrapped line of text. + +`Clay_RenderCommand.textContent` will be populated with a `Clay_String` _slice_ of the original string passed in (i.e. wrapping doesn't reallocate, it just returns a `Clay_String` pointing to the start of the new line with a `length`) + +### CLAY_IMAGE +**Usage** + +`CLAY(CLAY_IMAGE(...image config)) {}` + +**Lifecycle** + +`Clay_BeginLayout()` -> `CLAY(` -> `CLAY_IMAGE()` -> `)` -> `Clay_EndLayout()` + +**Notes** + +**IMAGE** configures a clay element to render an image as its background. It uses Clay_ImageElementConfig for image specific options. **Struct API (Pseudocode)** @@ -1102,7 +1012,7 @@ Used to perform **aspect ratio scaling** on the image element. As of this versio // Load an image somewhere in your code Image profilePicture = LoadImage("profilePicture.png"); // Note that when rendering, .imageData will be void* type. -CLAY_IMAGE(CLAY_ID("ProfilePicture"), &CLAY_LAYOUT_DEFAULT, CLAY_IMAGE_CONFIG(.imageData = &profilePicture, .sourceDimensions = { 60, 60 })) {} +CLAY(CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = { 60, 60 } })) {} // OR ---------------- @@ -1117,19 +1027,234 @@ typedef struct t_Image { // ... etc } Image; -// You can now use CLAY_IMAGE_CONFIG with your custom type and still have type safety & code completion -CLAY_IMAGE(CLAY_ID("ProfilePicture"), &CLAY_LAYOUT_DEFAULT, CLAY_IMAGE_CONFIG(.image = { .format = IMAGE_FORMAT_RGBA, .internalData = &imageData }, .sourceDimensions = { 60, 60 })) {} +// You can now use CLAY_IMAGE with your custom type and still have type safety & code completion +CLAY(CLAY_IMAGE({ .image = { .format = IMAGE_FORMAT_RGBA, .internalData = &imageData }, .sourceDimensions = { 60, 60 } })) {} ``` -### CLAY_FLOATING_CONFIG +**Examples** -**CLAY_FLOATING_CONFIG()** is a macro used to create and store `Clay_FloatingElementConfig` structs, which are for configuring [CLAY_FLOATING_CONTAINER](#clay_floating_container) elements. +```C +// Load an image somewhere in your code +Image profilePicture = LoadImage("profilePicture.png"); +// Declare a reusable image config +Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture, .height = 60, .width = 60 }; +// Declare an image element using a reusable config +CLAY(CLAY_IMAGE(imageConfig)) {} +// Declare an image element using an inline config +CLAY(CLAY_IMAGE({ .imageData = &profilePicture, .height = 60, .width = 60 })) {} +// Rendering example +Image *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData; +``` + +**Rendering** + +Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE` will be created. The user will need to access `renderCommand->elementConfig.imageElementConfig->imageData` to retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blend `rectangleElementConfig->color` with the image. + +### CLAY_SCROLL +**Usage** + +`CLAY(CLAY_SCROLL(...scroll config)) {}` + +**Lifecycle** + +`Clay_SetPointerState()` -> `Clay_UpdateScrollContainers()` -> `Clay_BeginLayout()` -> `CLAY(` -> `CLAY_SCROLL()` -> `)` -> `Clay_EndLayout()` + +**Notes** + +**SCROLL** configures the element as a scrolling container, enabling masking of children that extend beyond its boundaries. It uses [Clay_ScrollElementConfig](#clay_scrollelementconfig) to configure scroll specific options. + +Note: In order to process scrolling based on pointer position and mouse wheel or touch interactions, you must call `Clay_SetPointerState()` and `Clay_UpdateScrollContainers()` _before_ calling `BeginLayout`. + +**Struct Definition (Pseudocode)** + +```C +Clay_ScrollElementConfig { + bool horizontal; + bool vertical; +}; +``` + +As with all config macros, `CLAY_SCROLL()` accepts designated initializer syntax and provides default values for any unspecified struct members. + +**Fields** + +**`.horizontal`** - `bool` + +`CLAY_SCROLL(.horizontal = true)` + +Enables or disables horizontal scrolling for this container element. + +--- + +**`.vertical`** - `bool` + +`CLAY_SCROLL(.vertical = true)` + +Enables or disables vertical scrolling for this container element. + +--- + +**Rendering** + +Enabling scroll for an element will result in two additional render commands: +- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START`, which should create a rectangle mask with its `boundingBox` and is **not** subject to [culling](#visibility-culling) +- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END`, which disables the previous rectangle mask and is **not** subject to [culling](#visibility-culling) + +**Examples** + +```C +CLAY(CLAY_SCROLL(.vertical = true)) { + // Create child content with a fixed height of 5000 + CLAY(CLAY_ID("ScrollInner"), CLAY_LAYOUT({ .sizing = { .height = CLAY_SIZING_FIXED(5000) } })) {} +} +``` + +### CLAY_BORDER +**Usage** + +`CLAY(CLAY_BORDER(...border config)) {}` + +**Lifecycle** + +`Clay_BeginLayout()` -> `CLAY(` -> `CLAY_BORDER()` -> `)` -> `Clay_EndLayout()` + +**Notes** + +**BORDER** adds borders to the edges or between the children of elements. It uses Clay_BorderElementConfig to configure border specific options. + +**Struct Definition (Pseudocode)** + +```C +typedef struct Clay_BorderElementConfig +{ + Clay_Border left { + float width; + Clay_Color color { + float r; float g; float b; float a; + }; + }; + Clay_Border right // Exactly the same as left + Clay_Border top // Exactly the same as left + Clay_Border bottom // Exactly the same as left + Clay_Border betweenChildren // Exactly the same as left + Clay_CornerRadius cornerRadius { + float topLeft; + float topRight; + float bottomLeft; + float bottomRight; + }; +} Clay_BorderElementConfig; +``` + +**Usage** + +As with all config macros, `CLAY_BORDER()` accepts designated initializer syntax and provides default values for any unspecified struct members. + +**Fields** + +**`.left, .right, .top, .bottom`** - `Clay_Border` + +`CLAY_BORDER({ .left = { 2, COLOR_RED }, .right = { 4, COLOR_YELLOW } /* etc */ })` + +Indicates to the renderer that a border of `.color` should be draw at the specified edges of the bounding box, **overlapping the box contents by `.width`**. + +This means that border configuration does not affect layout, as the width of the border doesn't contribute to the total container width or layout position. Border containers with zero padding will be drawn over the top of child elements. + +--- + +**`.betweenChildren`** - `Clay_Border` + +`CLAY_BORDER({ .betweenChildren = { 2, COLOR_RED } })` + +Configures the width and color of borders to be drawn between children. These borders will be vertical lines if the parent uses `.layoutDirection = CLAY_LEFT_TO_RIGHT` and horizontal lines if the parent uses `CLAY_TOP_TO_BOTTOM`. Unlike `.left, .top` etc, this option **will generate additional rectangle render commands representing the borders between children.** As a result, the renderer does not need to specifically implement rendering for these border elements. + +--- + +**`.cornerRadius`** - `float` + +`CLAY_BORDER({ .cornerRadius = 16 })` + +Defines the radius in pixels for the arc of border corners (`0` is square, `rectangle.width / 2` is circular). It is up to the renderer to decide how to interpolate between differing border widths and colors across shared corners. + +Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER(.cornerRadius = CLAY_CORNER_RADIUS(10))` + +**Convenience Macros** + +There are some common cases for border configuration that are repetitive, i.e. specifying the same border around all four edges. Some convenience macros are provided for these cases: + +- `CLAY_BORDER_CONFIG_OUTSIDE({ .width = 2, .color = COLOR_RED })` - Shorthand for configuring all 4 outside borders at once.` +- `CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(width, color, radius)` - Shorthand for configuring all 4 outside borders at once, with the provided `.cornerRadius`. Note this is a function-like macro and does not take `.member = value` syntax. +- `CLAY_BORDER_CONFIG_ALL({ .width = 2, .color = COLOR_RED })` - Shorthand for configuring all 4 outside borders and `.betweenChildren` at once. +- `CLAY_BORDER_CONFIG_ALL_RADIUS(width, color, radius)` - Shorthand for configuring all 4 outside borders and `.betweenChildren` at once, with the provided `cornerRadius`. Note this is a function-like macro and does not take `.member = value` syntax. + +**Examples** + +```C +// 300x300 container with a 1px red border around all the edges +CLAY( + CLAY_ID("OuterBorder"), + CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300)}), + CLAY_BORDER_CONFIG_OUTSIDE({ .color = COLOR_RED, .width = 1 }) +) { + // ... +} + +// Container with a 3px yellow bottom border +CLAY( + CLAY_ID("OuterBorder"), + CLAY_BORDER({ .bottom = { .color = COLOR_YELLOW, .width = 3 } }) +) { + // ... +} + +// Container with a 5px curved border around the edges, and a 5px blue border between all children laid out top to bottom +CLAY( + CLAY_ID("OuterBorder"), + CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM }), + CLAY_BORDER_CONFIG_ALL_RADIUS(5, COLOR_BLUE, 5) +) { + // Child + // -- 5px blue border will be here -- + // Child + // -- 5px blue border will be here -- + // Child +} +``` + +**Rendering** + +Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_BORDER` representing the container will be created. +Rendering of borders and rounded corners is left up to the user. See the provided [Raylib Renderer](https://github.com/nicbarker/clay/tree/main/renderers/raylib) for examples of how to draw borders using line and curve primitives. + +### CLAY_FLOATING +**Usage** + +`CLAY_FLOATING(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingConfig);` + +**Lifecycle** + +`Clay_BeginLayout()` -> `CLAY_FLOATING()` -> `Clay_EndLayout()` + +**Notes** + +**FLOATING** defines an element that "floats" above other content. Typical use-cases include tooltips and modals. + +Floating containers: + +- With the [default configuration](#clay_floating_config), attach to the top left corner of their "parent" +- Don't affect the width and height of their parent +- Don't affect the positioning of sibling elements +- Depending on their z-index can appear above or below other elements, partially or completely occluding them +- Apart from positioning, function just like standard `CLAY` elements - including expanding to fit their children, etc. + +The easiest mental model to use when thinking about floating containers is that they are a completely separate UI hierarchy, attached to a specific x,y point on their "parent". + +Floating elements uses `Clay_FloatingElementConfig` to configure specific options. **Struct Definition (Pseudocode)** ```C -typedef struct -{ +Clay_FloatingElementConfig { Clay_Vector2 offset { float x, float y }; @@ -1142,16 +1267,16 @@ typedef struct .element = CLAY_ATTACH_POINT_LEFT_TOP (default) | CLAY_ATTACH_POINT_LEFT_CENTER | CLAY_ATTACH_POINT_LEFT_BOTTOM | CLAY_ATTACH_POINT_CENTER_TOP | CLAY_ATTACH_POINT_CENTER_CENTER | CLAY_ATTACH_POINT_CENTER_BOTTOM | CLAY_ATTACH_POINT_RIGHT_TOP | CLAY_ATTACH_POINT_RIGHT_CENTER | CLAY_ATTACH_POINT_RIGHT_BOTTOM .parent = CLAY_ATTACH_POINT_LEFT_TOP (default) | CLAY_ATTACH_POINT_LEFT_CENTER | CLAY_ATTACH_POINT_LEFT_BOTTOM | CLAY_ATTACH_POINT_CENTER_TOP | CLAY_ATTACH_POINT_CENTER_CENTER | CLAY_ATTACH_POINT_CENTER_BOTTOM | CLAY_ATTACH_POINT_RIGHT_TOP | CLAY_ATTACH_POINT_RIGHT_CENTER | CLAY_ATTACH_POINT_RIGHT_BOTTOM }; -} Clay_FloatingElementConfig; +}; ``` -As with all config macros, `CLAY_FLOATING_CONFIG()` accepts designated initializer syntax and provides default values for any unspecified struct members. +As with all config macros, `CLAY_FLOATING()` accepts designated initializer syntax and provides default values for any unspecified struct members. **Fields** **`.offset`** - `Clay_Vector2` -`CLAY_FLOATING_CONFIG(.offset = { -24, -24 })` +`CLAY_FLOATING({ .offset = { -24, -24 } })` Used to apply a position offset to the floating container _after_ all other layout has been calculated. @@ -1159,7 +1284,7 @@ Used to apply a position offset to the floating container _after_ all other layo **`.expand`** - `Clay_Dimensions` -`CLAY_FLOATING_CONFIG(.expand = { 16, 16 })` +`CLAY_FLOATING({ .expand = { 16, 16 } })` Used to expand the width and height of the floating container _before_ laying out child elements. @@ -1167,7 +1292,7 @@ Used to expand the width and height of the floating container _before_ laying ou **`.zIndex`** - `float` -`CLAY_FLOATING_CONFIG(.zIndex = 1)` +`CLAY_FLOATING({ .zIndex = 1 })` All floating elements (as well as their entire child hierarchies) will be sorted by `.zIndex` order before being converted to render commands. If render commands are drawn in order, elements with higher `.zIndex` values will be drawn on top. @@ -1175,41 +1300,41 @@ All floating elements (as well as their entire child hierarchies) will be sorted **`.parentId`** - `uint32_t` -`CLAY_FLOATING_CONFIG(.parentId = CLAY_ID("HeaderButton").id)` +`CLAY_FLOATING({ .parentId = Clay_GetElementId("HeaderButton").id })` By default, floating containers will "attach" to the parent element that they are declared inside. However, there are cases where this limitation could cause significant performance or ergonomics problems. `.parentId` allows you to specify a `CLAY_ID().id` to attach the floating container to. The parent element with the matching id can be declared anywhere in the hierarchy, it doesn't need to be declared before or after the floating container in particular. Consider the following case: ```C // Load an image somewhere in your code -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 1), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 1), &CLAY_LAYOUT_DEFAULT) { // .. some button contents if (tooltip.attachedButtonIndex == 1) { - CLAY_FLOATING_CONTAINER(/* floating config... */) + CLAY_FLOATING(/* floating config... */) } } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 2), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 2), &CLAY_LAYOUT_DEFAULT) { // .. some button contents if (tooltip.attachedButtonIndex == 2) { - CLAY_FLOATING_CONTAINER(/* floating config... */) + CLAY_FLOATING(/* floating config... */) } } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 3), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 3), &CLAY_LAYOUT_DEFAULT) { // .. some button contents if (tooltip.attachedButtonIndex == 3) { - CLAY_FLOATING_CONTAINER(/* floating config... */) + CLAY_FLOATING(/* floating config... */) } } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 4), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 4), &CLAY_LAYOUT_DEFAULT) { // .. some button contents if (tooltip.attachedButtonIndex == 4) { - CLAY_FLOATING_CONTAINER(/* floating config... */) + CLAY_FLOATING(/* floating config... */) } } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 5), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 5), &CLAY_LAYOUT_DEFAULT) { // .. some button contents if (tooltip.attachedButtonIndex == 5) { - CLAY_FLOATING_CONTAINER(/* floating config... */) + CLAY_FLOATING(/* floating config... */) } } ``` @@ -1218,24 +1343,24 @@ The definition of the above UI is significantly polluted by the need to conditio ```C // Load an image somewhere in your code -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 1), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 1), &CLAY_LAYOUT_DEFAULT) { // .. some button contents } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 2), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 2), &CLAY_LAYOUT_DEFAULT) { // .. some button contents } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 3), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 3), &CLAY_LAYOUT_DEFAULT) { // .. some button contents } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 4), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 4), &CLAY_LAYOUT_DEFAULT) { // .. some button contents } -CLAY_CONTAINER(CLAY_IDI("SidebarButton", 5), &CLAY_LAYOUT_DEFAULT) { +CLAY(CLAY_IDI("SidebarButton", 5), &CLAY_LAYOUT_DEFAULT) { // .. some button contents } // Any other point in the hierarchy -CLAY_FLOATING_CONTAINER(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id)) { +CLAY_FLOATING(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING(.parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id)) { // Tooltip contents... } ``` @@ -1244,7 +1369,7 @@ CLAY_FLOATING_CONTAINER(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLO **`.attachment`** - `Clay_FloatingAttachPoints` -`CLAY_FLOATING_CONFIG(.attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP });` +`CLAY_FLOATING(.attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP });` In terms of positioning the floating container, `.attachment` specifies @@ -1259,145 +1384,61 @@ For example: "Attach the LEFT_CENTER of the floating container to the RIGHT_TOP of the parent" -`CLAY_FLOATING_CONFIG(.attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP });` +`CLAY_FLOATING(.attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP });` ![Screenshot 2024-08-23 at 11 53 24 AM](https://github.com/user-attachments/assets/ebe75e0d-1904-46b0-982d-418f929d1516) - -### CLAY_SCROLL_CONFIG - -**CLAY_SCROLL_CONFIG()** is a macro used to create and store `Clay_ScrollElementConfig` structs, which are for configuring [CLAY_SCROLL_CONTAINER](#clay_scroll_container) elements. - -**Struct Definition (Pseudocode)** - -```C -typedef struct -{ - bool horizontal; - bool vertical; -} Clay_ScrollElementConfig; -``` - -As with all config macros, `CLAY_SCROLL_CONFIG()` accepts designated initializer syntax and provides default values for any unspecified struct members. - -**Fields** - -**`.horizontal`** - `bool` - -`CLAY_SCROLL_CONFIG(.horizontal = true)` - -Enables or disables horizontal scrolling for this container element. - ---- - -**`.vertical`** - `bool` - -`CLAY_SCROLL_CONFIG(.vertical = true)` - -Enables or disables vertical scrolling for this container element. - **Examples** ```C -CLAY_SCROLL_CONTAINER(CLAY_ID("MainContent"), &CLAY_LAYOUT_DEFAULT, CLAY_SCROLL_CONFIG(.vertical = true)) { - // Create child content with a fixed height of 5000 - CLAY_CONTAINER(CLAY_ID("ScrollInner"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(5000) })) {} +// Horizontal container with three option buttons +CLAY(CLAY_ID("OptionsList"), CLAY_LAYOUT(.childGap = 16)) { + CLAY_RECTANGLE(CLAY_IDI("Option", 1), CLAY_LAYOUT(.padding = {16, 16}), CLAY_RECTANGLE(.color = COLOR_BLUE)) { + CLAY_TEXT(CLAY_IDI("OptionText", 1), CLAY_STRING("Option 1"), CLAY_TEXT_CONFIG()); + } + CLAY_RECTANGLE(CLAY_IDI("Option", 2), CLAY_LAYOUT(.padding = {16, 16}), CLAY_RECTANGLE(.color = COLOR_BLUE)) { + CLAY_TEXT(CLAY_IDI("OptionText", 2), CLAY_STRING("Option 2"), CLAY_TEXT_CONFIG()); + // Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements + CLAY_FLOATING(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING(.zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP })) { + CLAY_TEXT(CLAY_IDI("OptionTooltipText", 1), CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG()); + } + } + CLAY_RECTANGLE(CLAY_IDI("Option", 3), CLAY_LAYOUT(.padding = {16, 16}), CLAY_RECTANGLE(.color = COLOR_BLUE)) { + CLAY_TEXT(CLAY_IDI("OptionText", 3), CLAY_STRING("Option 3"), CLAY_TEXT_CONFIG()); + } +} + +// Floating containers can also be declared elsewhere in a layout, to avoid branching or polluting other UI +for (int i = 0; i < 1000; i++) { + CLAY(CLAY_IDI("Option", i + 1), &CLAY_LAYOUT_DEFAULT) { + // ... + } +} +// Note the use of "parentId". +// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements +CLAY_FLOATING(CLAY_ID("OptionTooltip"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING(.parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER })) { + CLAY_TEXT(CLAY_IDI("OptionTooltipText", 1), CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG()); } ``` -### CLAY_BORDER_CONFIG +When using `.parentId`, the floating container can be declared anywhere after `BeginLayout` and before `EndLayout`. The target element matching the `.parentId` doesn't need to exist when `CLAY_FLOATING` is called. -**CLAY_BORDER_CONFIG()** is a macro used to create and store `Clay_BorderElementConfig` structs, which are for configuring [CLAY_BORDER_CONTAINER](#clay_border_container) elements. +**Rendering** -**Struct Definition (Pseudocode)** - -```C -typedef struct -{ - Clay_Border left { - float width; - Clay_Color color { - float r; float g; float b; float a; - }; - }; - Clay_Border right { - float width; - Clay_Color color { - float r; float g; float b; float a; - }; - } - Clay_Border top { - float width; - Clay_Color color { - float r; float g; float b; float a; - }; - }; - Clay_Border bottom { - float width; - Clay_Color color { - float r; float g; float b; float a; - }; - }; - Clay_Border betweenChildren { - float width; - Clay_Color color { - float r; float g; float b; float a; - }; - }; - Clay_CornerRadius cornerRadius { - float topLeft; - float topRight; - float bottomLeft; - float bottomRight; - }; -} Clay_BorderElementConfig; -``` +`CLAY_FLOATING` elements will not generate any render commands. +### CLAY_CUSTOM_ELEMENT **Usage** -As with all config macros, `CLAY_BORDER_CONFIG()` accepts designated initializer syntax and provides default values for any unspecified struct members. +`CLAY_CUSTOM_ELEMENT(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customConfig);` +**Lifecycle** -**Fields** +`Clay_BeginLayout()` -> `CLAY_CUSTOM_ELEMENT()` -> `Clay_EndLayout()` -**`.left, .right, .top, .bottom`** - `Clay_Border` +**Notes** -`CLAY_BORDER_CONFIG(.left = { 2, COLOR_RED }, .right = { 4, COLOR_YELLOW } /* etc */)` - -Indicates to the renderer that a border of `.color` should be draw at the specified edges of the bounding box, **overlapping the box contents by `.width`**. - -This means that border configuration does not affect layout, as the width of the border doesn't contribute to the total container width or layout position. Border containers with zero padding will be drawn over the top of child elements. - ---- - -**`.betweenChildren`** - `Clay_Border` - -`CLAY_BORDER_CONFIG(.betweenChildren = { 2, COLOR_RED })` - -Configures the width and color of borders to be drawn between children. These borders will be vertical lines if the parent uses `.layoutDirection = CLAY_LEFT_TO_RIGHT` and horizontal lines if the parent uses `CLAY_TOP_TO_BOTTOM`. Unlike `.left, .top` etc, this option **will generate additional rectangle render commands representing the borders between children.** As a result, the renderer does not need to specifically implement rendering for these border elements. - ---- - -**`.cornerRadius`** - `float` - -`CLAY_BORDER_CONFIG(.cornerRadius = 16)` - -Defines the radius in pixels for the arc of border corners (`0` is square, `rectangle.width / 2` is circular). It is up to the renderer to decide how to interpolate between differing border widths and colors across shared corners. - -Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER_CONFIG(.cornerRadius = CLAY_CORNER_RADIUS(10))` - -**Convenience Macros** - -There are some common cases for border configuration that are repetitive, i.e. specifying the same border around all four edges. Some convenience macros are provided for these cases: - -- `CLAY_BORDER_CONFIG_OUTSIDE(.width = 2, .color = COLOR_RED)` - Shorthand for configuring all 4 outside borders at once.` -- `CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(width, color, radius)` - Shorthand for configuring all 4 outside borders at once, with the provided `.cornerRadius`. Note this is a function-like macro and does not take `.member = value` syntax. -- `CLAY_BORDER_CONFIG_ALL(.width = 2, .color = COLOR_RED)` - Shorthand for configuring all 4 outside borders and `.betweenChildren` at once. -- `CLAY_BORDER_CONFIG_ALL_RADIUS(width, color, radius)` - Shorthand for configuring all 4 outside borders and `.betweenChildren` at once, with the provided `cornerRadius`. Note this is a function-like macro and does not take `.member = value` syntax. - -### CLAY_CUSTOM_ELEMENT_CONFIG - -**CLAY_CUSTOM_ELEMENT_CONFIG()** is a macro used to create and store `Clay_CustomElementConfig` structs, which are for configuring [CLAY_CUSTOM_ELEMENT]() elements. +**CUSTOM_ELEMENT** uses [Clay_LayoutConfig](#clay_layout) for styling and layout, and allows the user to pass custom data to the renderer. **Struct Definition (Pseudocode)** @@ -1430,6 +1471,7 @@ The underlying `Clay_ImageCustomConfig` can be extended with new members by usin `.customData` is a generic void pointer that can be used to pass through custom data to the renderer. **Note:** this field is generally not recommended for usage due to the lack of type safety, see `#define CLAY_EXTEND_CONFIG_CUSTOM` in [Preprocessor Directives]() for an alternative. +**Examples** ```C // Extend CLAY_CUSTOM_ELEMENT_CONFIG with your custom data #define CLAY_EXTEND_CONFIG_CUSTOM struct t_CustomElementData customData; @@ -1449,7 +1491,7 @@ typedef struct t_CustomElementData { Model myModel = Load3DModel(filePath); CustomElement modelElement = (CustomElement) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel } // ... -CLAY_CONTAINER(id, style) { +CLAY(id, style) { // This config is type safe and contains the CustomElementData struct CLAY_CUSTOM_ELEMENT(id, layout, CLAY_CUSTOM_ELEMENT_CONFIG(.customData = { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel })) {} } @@ -1477,19 +1519,10 @@ switch (renderCommand->commandType) { } ``` -## Misc Macros +**Rendering** -### CLAY_ID - -`Clay_ElementId CLAY_ID(char *label)` - -Generates a [Clay_ElementId](#clay_elementid) 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() - -`Clay_ElementId CLAY_IDI(char *label, int index)` - -Generates a [Clay_ElementId](#clay_elementid) 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. +Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM` will be created. +The user will need to access [Clay_CustomElementConfig](#clay_custom_element_config) to retrieve custom data referenced during layout creation. ## Data Structures & Definitions @@ -1609,11 +1642,11 @@ A rectangle representing the bounding box of this render command, with `.x` and A C union containing various pointers to config data, with the type dependent on `.commandType`. Possible values include: -- `config.rectangleElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE`. See [CLAY_RECTANGLE_CONFIG](#clay_rectangle_config) for details. -- `config.textElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_TEXT`. See [CLAY_TEXT_CONFIG](#clay_text_config) for details. -- `config.imageElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE`. See [CLAY_IMAGE_CONFIG](#clay_image_config) for details. -- `config.borderElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_BORDER`. See [CLAY_BORDER_CONFIG](#clay_border_config) for details. -- `config.customElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM`. See [CLAY_CUSTOM_CONFIG](#clay_custom_config) for details. +- `config.rectangleElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE`. See [CLAY_RECTANGLE](#clay_rectangle) for details. +- `config.textElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_TEXT`. See [CLAY_TEXT](#clay_text) for details. +- `config.imageElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE`. See [CLAY_IMAGE](#clay_image) for details. +- `config.borderElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_BORDER`. See [CLAY_BORDER](#clay_border) for details. +- `config.customElementConfig` - Used when `.commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM`. See [CLAY_CUSTOM](#clay_custom_element) for details. - `config.floatingElementConfig` - Not used and will always be NULL. - `config.scrollElementConfig` - Not used and will always be NULL. diff --git a/bindings/odin/clay-odin/clay.odin b/bindings/odin/clay-odin/clay.odin index 925dcc0..7d114c3 100644 --- a/bindings/odin/clay-odin/clay.odin +++ b/bindings/odin/clay-odin/clay.odin @@ -70,6 +70,19 @@ when ODIN_OS == .Windows { EnumBackingType :: u8 } +ElementConfigType :: enum EnumBackingType { + Rectangle = 1, + Border = 2, + Floating = 4, + Scroll = 8, + Image = 16, + Text = 32, + Custom = 64, + // Odin specific enum types + Id = 65, + Layout = 66, +} + RenderCommandType :: enum EnumBackingType { None, Rectangle, @@ -245,17 +258,24 @@ ClayArray :: struct($type: typeid) { internalArray: [^]type, } +TypedConfig :: struct { + type: ElementConfigType, + config: rawptr, + id: ElementId, +} + @(link_prefix = "Clay_", default_calling_convention = "c") foreign Clay { MinMemorySize :: proc() -> u32 --- CreateArenaWithCapacityAndMemory :: proc(capacity: u32, offset: [^]u8) -> Arena --- SetPointerState :: proc(position: Vector2, pointerDown: bool) --- Initialize :: proc(arena: Arena, layoutDimensions: Dimensions) --- - UpdateScrollContainers :: proc(isPointerActive: bool, scrollDelta: Vector2, deltaTime: c.float) --- + UpdateScrollContainers :: proc(enableDragScrolling: bool, scrollDelta: Vector2, deltaTime: c.float) --- SetLayoutDimensions :: proc(dimensions: Dimensions) --- BeginLayout :: proc() --- EndLayout :: proc() -> ClayArray(RenderCommand) --- PointerOver :: proc(id: ElementId) -> bool --- + GetElementId :: proc(id: String) -> ElementId --- GetScrollContainerData :: proc(id: ElementId) -> ScrollContainerData --- SetMeasureTextFunction :: proc(measureTextFunction: proc "c" (text: ^String, config: ^TextElementConfig) -> Dimensions) --- RenderCommandArray_Get :: proc(array: ^ClayArray(RenderCommand), index: i32) -> ^RenderCommand --- @@ -264,17 +284,13 @@ foreign Clay { @(link_prefix = "Clay_", default_calling_convention = "c", private) foreign Clay { - _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() --- + _OpenElement :: proc() --- + _CloseElement :: proc() --- + _ElementPostConfiguration :: proc() --- + _OpenTextElement :: proc(text: String, textConfig: ^TextElementConfig) --- + _AttachId :: proc(id: ElementId) --- + _AttachLayoutConfig :: proc(layoutConfig: ^LayoutConfig) --- + _AttachElementConfig :: proc(config: rawptr, type: ElementConfigType) --- _StoreLayoutConfig :: proc(config: LayoutConfig) -> ^LayoutConfig --- _StoreRectangleElementConfig :: proc(config: RectangleElementConfig) -> ^RectangleElementConfig --- _StoreTextElementConfig :: proc(config: TextElementConfig) -> ^TextElementConfig --- @@ -287,103 +303,77 @@ foreign Clay { _GetOpenLayoutElementId :: proc() -> u32 --- } -@(require_results, deferred_none = _CloseElementWithChildren) -Container :: proc(id: ElementId, layoutConfig: ^LayoutConfig) -> bool { - _OpenContainerElement(id, layoutConfig) +@(require_results, deferred_none = _CloseElement) +UI :: proc(configs: ..TypedConfig) -> bool { + _OpenElement() + for config in configs { + #partial switch (config.type) { + case ElementConfigType.Id: + _AttachId(config.id) + case ElementConfigType.Layout: + _AttachLayoutConfig(cast(^LayoutConfig)config.config) + case: + _AttachElementConfig(config.config, config.type) + } + } + _ElementPostConfiguration() return true } -@(require_results, deferred_none = _CloseElementWithChildren) -Rectangle :: proc(id: ElementId, layoutConfig: ^LayoutConfig, rectangleConfig: ^RectangleElementConfig) -> bool { - _OpenRectangleElement(id, layoutConfig, rectangleConfig) - return true +Layout :: proc(config: LayoutConfig) -> TypedConfig { + return {type = ElementConfigType.Layout, config = _StoreLayoutConfig(config) } } -Text :: proc(id: ElementId, text: string, textConfig: ^TextElementConfig) -> bool { - _OpenTextElement(id, MakeString(text), textConfig) - return true +Rectangle :: proc(config: RectangleElementConfig) -> TypedConfig { + return {type = ElementConfigType.Rectangle, config = _StoreRectangleElementConfig(config)} } -@(require_results, deferred_none = _CloseElementWithChildren) -Image :: proc(id: ElementId, layoutConfig: ^LayoutConfig, imageConfig: ^ImageElementConfig) -> bool { - _OpenImageElement(id, layoutConfig, imageConfig) - return true -} - -@(require_results, deferred_none = _CloseScrollElement) -Scroll :: proc(id: ElementId, layoutConfig: ^LayoutConfig, scrollConfig: ^ScrollElementConfig) -> bool { - _OpenScrollElement(id, layoutConfig, scrollConfig) - return true -} - -@(require_results, deferred_none = _CloseFloatingElement) -Floating :: proc(id: ElementId, layoutConfig: ^LayoutConfig, floatingConfig: ^FloatingElementConfig) -> bool { - _OpenFloatingElement(id, layoutConfig, floatingConfig) - return true -} - -@(require_results, deferred_none = _CloseElementWithChildren) -Border :: proc(id: ElementId, layoutConfig: ^LayoutConfig, borderConfig: ^BorderElementConfig) -> bool { - _OpenBorderElement(id, layoutConfig, borderConfig) - return true -} - -@(require_results, deferred_none = _CloseElementWithChildren) -Custom :: proc(id: ElementId, layoutConfig: ^LayoutConfig, customConfig: ^CustomElementConfig) -> bool { - _OpenCustomElement(id, layoutConfig, customConfig) - return true -} - -Layout :: proc(config: LayoutConfig) -> ^LayoutConfig { - return _StoreLayoutConfig(config) -} - -RectangleConfig :: proc(config: RectangleElementConfig) -> ^RectangleElementConfig { - return _StoreRectangleElementConfig(config) +Text :: proc(text: string, config: ^TextElementConfig) { + _OpenTextElement(MakeString(text), config) } TextConfig :: proc(config: TextElementConfig) -> ^TextElementConfig { return _StoreTextElementConfig(config) } -ImageConfig :: proc(config: ImageElementConfig) -> ^ImageElementConfig { - return _StoreImageElementConfig(config) +Image :: proc(config: ImageElementConfig) -> TypedConfig { + return {type = ElementConfigType.Image, config = _StoreImageElementConfig(config)} } -FloatingConfig :: proc(config: FloatingElementConfig) -> ^FloatingElementConfig { - return _StoreFloatingElementConfig(config) +Floating :: proc(config: FloatingElementConfig) -> TypedConfig { + return {type = ElementConfigType.Floating, config = _StoreFloatingElementConfig(config)} } -CustomConfig :: proc(config: CustomElementConfig) -> ^CustomElementConfig { - return _StoreCustomElementConfig(config) +Custom :: proc(config: CustomElementConfig) -> TypedConfig { + return {type = ElementConfigType.Custom, config = _StoreCustomElementConfig(config)} } -ScrollConfig :: proc(config: ScrollElementConfig) -> ^ScrollElementConfig { - return _StoreScrollElementConfig(config) +Scroll :: proc(config: ScrollElementConfig) -> TypedConfig { + return {type = ElementConfigType.Scroll, config = _StoreScrollElementConfig(config)} } -BorderConfig :: proc(config: BorderElementConfig) -> ^BorderElementConfig { - return _StoreBorderElementConfig(config) +Border :: proc(config: BorderElementConfig) -> TypedConfig { + return {type = ElementConfigType.Border, config = _StoreBorderElementConfig(config)} } -BorderConfigOutside :: proc(outsideBorders: BorderData) -> ^BorderElementConfig { - return _StoreBorderElementConfig((BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders}) +BorderOutside :: proc(outsideBorders: BorderData) -> TypedConfig { + return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders}) } } -BorderConfigOutsideRadius :: proc(outsideBorders: BorderData, radius: f32) -> ^BorderElementConfig { - return _StoreBorderElementConfig( +BorderOutsideRadius :: proc(outsideBorders: BorderData, radius: f32) -> TypedConfig { + return { type = ElementConfigType.Border, config = _StoreBorderElementConfig( (BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders, cornerRadius = {radius, radius, radius, radius}}, - ) + ) } } -BorderConfigAll :: proc(allBorders: BorderData) -> ^BorderElementConfig { - return _StoreBorderElementConfig((BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, betweenChildren = allBorders}) +BorderAll :: proc(allBorders: BorderData) -> TypedConfig { + return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, betweenChildren = allBorders}) } } -BorderConfigAllRadius :: proc(allBorders: BorderData, radius: f32) -> ^BorderElementConfig { - return _StoreBorderElementConfig( +BorderAllRadius :: proc(allBorders: BorderData, radius: f32) -> TypedConfig { + return { type = ElementConfigType.Border, config = _StoreBorderElementConfig( (BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, cornerRadius = {radius, radius, radius, radius}}, - ) + ) } } CornerRadiusAll :: proc(radius: f32) -> CornerRadius { @@ -410,10 +400,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) -> ElementId { - return _HashString(MakeString(label), index, 0) -} - -IDLocal :: proc(label: string, index: u32 = 0) -> ElementId { - return _HashString(MakeString(label), index, _GetOpenLayoutElementId()) +ID :: proc(label: string, index: u32 = 0) -> TypedConfig { + return { type = ElementConfigType.Id, id = _HashString(MakeString(label), index, 0) } } diff --git a/bindings/odin/clay-odin/linux/clay.a b/bindings/odin/clay-odin/linux/clay.a index ef77c5d..0789fdf 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 c71881e..0c6d92d 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 d52bc5a..b9a2332 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 8b8e2b7..8cb648e 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 ee327c9..9f8a31d 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/examples/clay-official-website/clay-official-website.odin b/bindings/odin/examples/clay-official-website/clay-official-website.odin index 8b64d70..657ca65 100644 --- a/bindings/odin/examples/clay-official-website/clay-official-website.odin +++ b/bindings/odin/examples/clay-official-website/clay-official-website.odin @@ -58,44 +58,42 @@ headerTextConfig := clay.TextElementConfig { } LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, text: string, image: ^raylib.Texture2D) { - if clay.Border( + if clay.UI( clay.ID("HeroBlob", index), clay.Layout({sizing = {width = clay.SizingGrow({max = 480})}, padding = clay.Padding{16, 16}, childGap = 16, childAlignment = clay.ChildAlignment{y = .CENTER}}), - clay.BorderConfigOutsideRadius({2, color}, 10), + clay.BorderOutsideRadius({2, color}, 10), ) { - if clay.Image( + if clay.UI( clay.ID("CheckImage", index), clay.Layout({sizing = {width = clay.SizingFixed(32)}}), - clay.ImageConfig({imageData = image, sourceDimensions = {128, 128}}), + clay.Image({imageData = image, sourceDimensions = {128, 128}}), ) {} - clay.Text(clay.ID("HeroBlobText", index), text, clay.TextConfig({fontSize = fontSize, fontId = fontId, textColor = color})) + clay.Text(text, clay.TextConfig({fontSize = fontSize, fontId = fontId, textColor = color})) } } LandingPageDesktop :: proc() { - if clay.Container( + if clay.UI( clay.ID("LandingPage1Desktop"), clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})}, childAlignment = {y = .CENTER}, padding = {x = 50}}), ) { - if clay.Border( + if clay.UI( clay.ID("LandingPage1"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = {32, 32}, childGap = 32}), - clay.BorderConfig({left = {2, COLOR_RED}, right = {2, COLOR_RED}}), + clay.Border({left = {2, COLOR_RED}, right = {2, COLOR_RED}}), ) { - if clay.Container(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingPercent(0.55)}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { + if clay.UI(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingPercent(0.55)}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { clay.Text( - clay.ID("LeftTextTitle"), "Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.", clay.TextConfig({fontSize = 56, fontId = FONT_ID_TITLE_56, textColor = COLOR_RED}), ) - if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}})) {} + if clay.UI(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}})) {} clay.Text( - clay.ID("LeftTextTagline"), "Clay is laying out this webpage right now!", clay.TextConfig({fontSize = 36, fontId = FONT_ID_TITLE_36, textColor = COLOR_ORANGE}), ) } - if clay.Container( + if clay.UI( clay.ID("HeroImageOuter"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingPercent(0.45)}, childAlignment = {x = .CENTER}, childGap = 16}), ) { @@ -110,7 +108,7 @@ LandingPageDesktop :: proc() { } LandingPageMobile :: proc() { - if clay.Container( + if clay.UI( clay.ID("LandingPage1Mobile"), clay.Layout( { @@ -122,20 +120,18 @@ LandingPageMobile :: proc() { }, ), ) { - if clay.Container(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingGrow({})}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { + if clay.UI(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingGrow({})}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { clay.Text( - clay.ID("LeftTextTitle"), "Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.", clay.TextConfig({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}), ) - if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}})) {} + if clay.UI(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}})) {} clay.Text( - clay.ID("LeftTextTagline"), "Clay is laying out this webpage right now!", clay.TextConfig({fontSize = 32, fontId = FONT_ID_TITLE_32, textColor = COLOR_ORANGE}), ) } - if clay.Container( + if clay.UI( clay.ID("HeroImageOuter"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingGrow({})}, childAlignment = {x = .CENTER}, childGap = 16}), ) { @@ -150,32 +146,32 @@ LandingPageMobile :: proc() { FeatureBlocks :: proc(widthSizing: clay.SizingAxis, outerPadding: u16) { textConfig := clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_RED}) - if clay.Container( + if clay.UI( clay.ID("HFileBoxOuter"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = widthSizing}, childAlignment = {y = .CENTER}, padding = {outerPadding, 32}, childGap = 8}), ) { - if clay.Rectangle(clay.ID("HFileIncludeOuter"), clay.Layout({padding = {8, 4}}), clay.RectangleConfig({color = COLOR_RED, cornerRadius = clay.CornerRadiusAll(8)})) { - clay.Text(clay.ID("HFileBoxText", 2), "#include clay.h", clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_LIGHT})) + if clay.UI(clay.ID("HFileIncludeOuter"), clay.Layout({padding = {8, 4}}), clay.Rectangle({color = COLOR_RED, cornerRadius = clay.CornerRadiusAll(8)})) { + clay.Text("#include clay.h", clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_LIGHT})) } - clay.Text(clay.ID("HFileSecondLine"), "~2000 lines of C99.", textConfig) - clay.Text(clay.ID("HFileBoxText", 5), "Zero dependencies, including no C standard library.", textConfig) + clay.Text("~2000 lines of C99.", textConfig) + clay.Text("Zero dependencies, including no C standard library.", textConfig) } - if clay.Container( + if clay.UI( clay.ID("BringYourOwnRendererOuter"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = widthSizing}, childAlignment = {y = .CENTER}, padding = {x = outerPadding, y = 32}, childGap = 8}), ) { - clay.Text(clay.ID("ZeroDependenciesText", 1), "Renderer agnostic.", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = COLOR_ORANGE})) - clay.Text(clay.ID("ZeroDependenciesText", 2), "Layout with clay, then render with Raylib, WebGL Canvas or even as HTML.", textConfig) - clay.Text(clay.ID("ZeroDependenciesText", 3), "Flexible output for easy compositing in your custom engine or environment.", textConfig) + clay.Text("Renderer agnostic.", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = COLOR_ORANGE})) + clay.Text("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML.", textConfig) + clay.Text("Flexible output for easy compositing in your custom engine or environment.", textConfig) } } FeatureBlocksDesktop :: proc() { - if clay.Container(clay.ID("FeatureBlocksOuter"), clay.Layout({sizing = {width = clay.SizingGrow({})}})) { - if clay.Border( + if clay.UI(clay.ID("FeatureBlocksOuter"), clay.Layout({sizing = {width = clay.SizingGrow({})}})) { + if clay.UI( clay.ID("FeatureBlocksInner"), clay.Layout({sizing = {width = clay.SizingGrow({})}, childAlignment = {y = .CENTER}}), - clay.BorderConfig({betweenChildren = {width = 2, color = COLOR_RED}}), + clay.Border({betweenChildren = {width = 2, color = COLOR_RED}}), ) { FeatureBlocks(clay.SizingPercent(0.5), 50) } @@ -183,53 +179,50 @@ FeatureBlocksDesktop :: proc() { } FeatureBlocksMobile :: proc() { - if clay.Border( + if clay.UI( clay.ID("FeatureBlocksInner"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingGrow({})}}), - clay.BorderConfig({betweenChildren = {width = 2, color = COLOR_RED}}), + clay.Border({betweenChildren = {width = 2, color = COLOR_RED}}), ) { FeatureBlocks(clay.SizingGrow({}), 16) } } DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) { - if clay.Container(clay.ID("SyntaxPageLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { - clay.Text(clay.ID("SyntaxPageTextTitle"), "Declarative Syntax", clay.TextConfig(titleTextConfig)) - if clay.Container(clay.ID("SyntaxSpacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {} + if clay.UI(clay.ID("SyntaxPageLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { + clay.Text("Declarative Syntax", clay.TextConfig(titleTextConfig)) + if clay.UI(clay.ID("SyntaxSpacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {} clay.Text( - clay.ID("SyntaxPageTextSubTitle1"), "Flexible and readable declarative syntax with nested UI element hierarchies.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}), ) clay.Text( - clay.ID("SyntaxPageTextSubTitle2"), "Mix elements with standard C code like loops, conditionals and functions.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}), ) clay.Text( - clay.ID("SyntaxPageTextSubTitle3"), "Create your own library of re-usable components from UI primitives like text, images and rectangles.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}), ) } - if clay.Container(clay.ID("SyntaxPageRightImage"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}})) { - if clay.Image( + if clay.UI(clay.ID("SyntaxPageRightImage"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}})) { + if clay.UI( clay.ID("SyntaxPageRightImage"), clay.Layout({sizing = {width = clay.SizingGrow({max = 568})}}), - clay.ImageConfig({imageData = &syntaxImage, sourceDimensions = {1136, 1194}}), + clay.Image({imageData = &syntaxImage, sourceDimensions = {1136, 1194}}), ) {} } } DeclarativeSyntaxPageDesktop :: proc() { - if clay.Container( + if clay.UI( clay.ID("SyntaxPageDesktop"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, childAlignment = {y = .CENTER}, padding = {x = 50}}), ) { - if clay.Border( + if clay.UI( clay.ID("SyntaxPage"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = {32, 32}, childGap = 32}), - clay.BorderConfig({left = {2, COLOR_RED}, right = {2, COLOR_RED}}), + clay.Border({left = {2, COLOR_RED}, right = {2, COLOR_RED}}), ) { DeclarativeSyntaxPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5)) } @@ -237,7 +230,7 @@ DeclarativeSyntaxPageDesktop :: proc() { } DeclarativeSyntaxPageMobile :: proc() { - if clay.Container( + if clay.UI( clay.ID("SyntaxPageMobile"), clay.Layout( { @@ -260,63 +253,60 @@ ColorLerp :: proc(a: clay.Color, b: clay.Color, amount: f32) -> clay.Color { LOREM_IPSUM_TEXT := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) { - if clay.Container(clay.ID("PerformanceLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { - clay.Text(clay.ID("PerformanceTextTitle"), "High Performance", clay.TextConfig(titleTextConfig)) - if clay.Container(clay.ID("SyntaxSpacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {} + if clay.UI(clay.ID("PerformanceLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { + clay.Text("High Performance", clay.TextConfig(titleTextConfig)) + if clay.UI(clay.ID("SyntaxSpacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {} clay.Text( - clay.ID("PerformanceTextSubTitle", 1), "Fast enough to recompute your entire UI every frame.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_LIGHT}), ) clay.Text( - clay.ID("PerformanceTextSubTitle", 2), "Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_LIGHT}), ) clay.Text( - clay.ID("PerformanceTextSubTitle", 3), "Simplify animations and reactive UI design by avoiding the standard performance hacks.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_LIGHT}), ) } - if clay.Container(clay.ID("PerformanceRightImageOuter"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}})) { - if clay.Border( + if clay.UI(clay.ID("PerformanceRightImageOuter"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}})) { + if clay.UI( clay.ID("PerformanceRightBorder"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(400)}}), - clay.BorderConfigAll({width = 2, color = COLOR_LIGHT}), + clay.BorderAll({width = 2, color = COLOR_LIGHT}), ) { - if clay.Rectangle( + if clay.UI( clay.ID("AnimationDemoContainerLeft"), clay.Layout({sizing = {clay.SizingPercent(0.35 + 0.3 * lerpValue), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = {16, 16}}), - clay.RectangleConfig({color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue)}), + clay.Rectangle({color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue)}), ) { - clay.Text(clay.ID("AnimationDemoTextLeft"), LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT})) + clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT})) } - if clay.Rectangle( + if clay.UI( clay.ID("AnimationDemoContainerRight"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = {16, 16}}), - clay.RectangleConfig({color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue)}), + clay.Rectangle({color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue)}), ) { - clay.Text(clay.ID("AnimationDemoTextRight"), LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT})) + clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT})) } } } } HighPerformancePageDesktop :: proc(lerpValue: f32) { - if clay.Rectangle( + if clay.UI( clay.ID("PerformanceDesktop"), clay.Layout( {sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, childAlignment = {y = .CENTER}, padding = {x = 82, y = 32}, childGap = 64}, ), - clay.RectangleConfig({color = COLOR_RED}), + clay.Rectangle({color = COLOR_RED}), ) { HighPerformancePage(lerpValue, {fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_LIGHT}, clay.SizingPercent(0.5)) } } HighPerformancePageMobile :: proc(lerpValue: f32) { - if clay.Rectangle( + if clay.UI( clay.ID("PerformanceMobile"), clay.Layout( { @@ -327,73 +317,69 @@ HighPerformancePageMobile :: proc(lerpValue: f32) { childGap = 32, }, ), - clay.RectangleConfig({color = COLOR_RED}), + clay.Rectangle({color = COLOR_RED}), ) { HighPerformancePage(lerpValue, {fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_LIGHT}, clay.SizingGrow({})) } } -RendererButtonActive :: proc(id: clay.ElementId, index: i32, text: string) { - if clay.Rectangle( - id, +RendererButtonActive :: proc(index: i32, text: string) { + if clay.UI( clay.Layout({sizing = {width = clay.SizingFixed(300)}, padding = {16, 16}}), - clay.RectangleConfig({color = clay.PointerOver(id) ? COLOR_RED_HOVER : COLOR_RED, cornerRadius = clay.CornerRadiusAll(10)}), + clay.Rectangle({color = COLOR_RED, cornerRadius = clay.CornerRadiusAll(10)}), ) { - clay.Text(clay.ID("RendererButtonActiveText"), text, clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_LIGHT})) + clay.Text(text, clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_LIGHT})) } } -RendererButtonInactive :: proc(id: clay.ElementId, index: u32, text: string) { - if clay.Border(id, clay.Layout({}), clay.BorderConfigOutsideRadius({2, COLOR_RED}, 10)) { - if clay.Rectangle( +RendererButtonInactive :: proc(index: u32, text: string) { + if clay.UI(clay.Layout({}), clay.BorderOutsideRadius({2, COLOR_RED}, 10)) { + if clay.UI( clay.ID("RendererButtonInactiveInner", index), clay.Layout({sizing = {width = clay.SizingFixed(300)}, padding = {16, 16}}), - clay.RectangleConfig({color = clay.PointerOver(id) ? COLOR_LIGHT_HOVER : COLOR_LIGHT, cornerRadius = clay.CornerRadiusAll(10)}), + clay.Rectangle({color = COLOR_LIGHT, cornerRadius = clay.CornerRadiusAll(10)}), ) { - clay.Text(clay.ID("RendererButtonInactiveText", index), text, clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED})) + clay.Text(text, clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED})) } } } RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) { - if clay.Container(clay.ID("RendererLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { - clay.Text(clay.ID("RendererTextTitle"), "Renderer & Platform Agnostic", clay.TextConfig(titleTextConfig)) - if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {} + if clay.UI(clay.ID("RendererLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) { + clay.Text("Renderer & Platform Agnostic", clay.TextConfig(titleTextConfig)) + if clay.UI(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {} clay.Text( - clay.ID("RendererTextSubTitle", 1), "Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_RED}), ) clay.Text( - clay.ID("RendererTextSubTitle", 2), "Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more.", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_RED}), ) clay.Text( - clay.ID("RendererTextSubTitle", 3), "There's even an HTML renderer - you're looking at it right now!", clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_RED}), ) } - if clay.Container( + if clay.UI( 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})) - if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 32})}})) {} - RendererButtonActive(clay.ID("RendererSelectButtonActive", 0), 0, "Raylib Renderer") + clay.Text("Try changing renderer!", clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE})) + if clay.UI(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 32})}})) {} + RendererButtonActive(0, "Raylib Renderer") } } RendererPageDesktop :: proc() { - if clay.Container( + if clay.UI( clay.ID("RendererPageDesktop"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, childAlignment = {y = .CENTER}, padding = {x = 50}}), ) { - if clay.Border( + if clay.UI( clay.ID("RendererPage"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = {32, 32}, childGap = 32}), - clay.BorderConfig({left = {2, COLOR_RED}, right = {2, COLOR_RED}}), + clay.Border({left = {2, COLOR_RED}, right = {2, COLOR_RED}}), ) { RendererPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5)) } @@ -401,7 +387,7 @@ RendererPageDesktop :: proc() { } RendererPageMobile :: proc() { - if clay.Rectangle( + if clay.UI( clay.ID("RendererMobile"), clay.Layout( { @@ -412,7 +398,7 @@ RendererPageMobile :: proc() { childGap = 32, }, ), - clay.RectangleConfig({color = COLOR_LIGHT}), + clay.Rectangle({color = COLOR_LIGHT}), ) { RendererPage({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}, clay.SizingGrow({})) } @@ -430,67 +416,59 @@ animationLerpValue: f32 = -1.0 createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) { mobileScreen := windowWidth < 750 clay.BeginLayout() - if clay.Rectangle( + if clay.UI( clay.ID("OuterContainer"), clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {clay.SizingGrow({}), clay.SizingGrow({})}}), - clay.RectangleConfig({color = COLOR_LIGHT}), + clay.Rectangle({color = COLOR_LIGHT}), ) { - if clay.Container( + if clay.UI( clay.ID("Header"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(50)}, childAlignment = {y = .CENTER}, childGap = 24, padding = {x = 32}}), ) { - clay.Text(clay.ID("Logo"), "Clay", &headerTextConfig) - if clay.Container(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({})}})) {} + clay.Text("Clay", &headerTextConfig) + if clay.UI(clay.ID("Spacer"), clay.Layout({sizing = {width = clay.SizingGrow({})}})) {} if (!mobileScreen) { - if clay.Rectangle(clay.ID("LinkExamplesOuter"), clay.Layout({}), clay.RectangleConfig({color = {0, 0, 0, 0}})) { - clay.Text(clay.ID("LinkExamplesText"), "Examples", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) + if clay.UI(clay.ID("LinkExamplesOuter"), clay.Layout({}), clay.Rectangle({color = {0, 0, 0, 0}})) { + clay.Text("Examples", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) } - if clay.Rectangle(clay.ID("LinkDocsOuter"), clay.Layout({}), clay.RectangleConfig({color = {0, 0, 0, 0}})) { - clay.Text(clay.ID("LinkDocsText"), "Docs", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) + if clay.UI(clay.ID("LinkDocsOuter"), clay.Layout({}), clay.Rectangle({color = {0, 0, 0, 0}})) { + clay.Text("Docs", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) } } - githubButtonId: clay.ElementId = clay.ID("HeaderButtonGithub") - if clay.Border(clay.ID("LinkGithubOuter"), clay.Layout({}), clay.BorderConfigOutsideRadius({2, COLOR_RED}, 10)) { - if clay.Rectangle( - githubButtonId, - clay.Layout({padding = {16, 6}}), - clay.RectangleConfig({cornerRadius = clay.CornerRadiusAll(10), color = clay.PointerOver(githubButtonId) ? COLOR_LIGHT_HOVER : COLOR_LIGHT}), - ) { - clay.Text(clay.ID("LinkGithubText"), "Github", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) - } + if clay.UI( + clay.ID("LinkGithubOuter"), + clay.Layout({padding = {16, 6}}), + clay.BorderOutsideRadius({2, COLOR_RED}, 10), + clay.Rectangle({cornerRadius = clay.CornerRadiusAll(10), color = clay.PointerOver(clay.GetElementId(clay.MakeString("LinkGithubOuter"))) ? COLOR_LIGHT_HOVER : COLOR_LIGHT}) + ) { + clay.Text("Github", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}})) } } - if clay.Rectangle(clay.ID("TopBorder1"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.RectangleConfig({color = COLOR_TOP_BORDER_5})) {} - if clay.Rectangle(clay.ID("TopBorder2"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.RectangleConfig({color = COLOR_TOP_BORDER_4})) {} - if clay.Rectangle(clay.ID("TopBorder3"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.RectangleConfig({color = COLOR_TOP_BORDER_3})) {} - if clay.Rectangle(clay.ID("TopBorder4"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.RectangleConfig({color = COLOR_TOP_BORDER_2})) {} - if clay.Rectangle(clay.ID("TopBorder5"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.RectangleConfig({color = COLOR_TOP_BORDER_1})) {} - if clay.Rectangle( + if clay.UI(clay.ID("TopBorder1"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_5})) {} + if clay.UI(clay.ID("TopBorder2"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_4})) {} + if clay.UI(clay.ID("TopBorder3"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_3})) {} + if clay.UI(clay.ID("TopBorder4"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_2})) {} + if clay.UI(clay.ID("TopBorder5"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_1})) {} + if clay.UI( clay.ID("ScrollContainerBackgroundRectangle"), - clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}}), - clay.RectangleConfig({color = COLOR_LIGHT}), + clay.Scroll({vertical = true}), + clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, layoutDirection = clay.LayoutDirection.TOP_TO_BOTTOM}), + clay.Rectangle({color = COLOR_LIGHT}), + clay.Border({betweenChildren = {2, COLOR_RED}}) ) { - if clay.Scroll(clay.ID("OuterScrollContainer"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}}), clay.ScrollConfig({vertical = true})) { - if clay.Border( - clay.ID("ScrollContainerInner"), - clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingGrow({})}}), - clay.BorderConfig({betweenChildren = {2, COLOR_RED}}), - ) { - if (!mobileScreen) { - LandingPageDesktop() - FeatureBlocksDesktop() - DeclarativeSyntaxPageDesktop() - HighPerformancePageDesktop(lerpValue) - RendererPageDesktop() - } else { - LandingPageMobile() - FeatureBlocksMobile() - DeclarativeSyntaxPageMobile() - HighPerformancePageMobile(lerpValue) - RendererPageMobile() - } - } + if (!mobileScreen) { + LandingPageDesktop() + FeatureBlocksDesktop() + DeclarativeSyntaxPageDesktop() + HighPerformancePageDesktop(lerpValue) + RendererPageDesktop() + } else { + LandingPageMobile() + FeatureBlocksMobile() + DeclarativeSyntaxPageMobile() + HighPerformancePageMobile(lerpValue) + RendererPageMobile() } } } diff --git a/clay.h b/clay.h index 9ee2947..67c1e85 100644 --- a/clay.h +++ b/clay.h @@ -30,47 +30,59 @@ // Public Macro API ------------------------ -#define CLAY_LAYOUT(...) Clay__StoreLayoutConfig(CLAY__INIT(Clay_LayoutConfig) {__VA_ARGS__ }) +#ifdef __cplusplus +#define CLAY__CONFIG_WRAPPER(type, ...) __VA_ARGS__ +#else +#define CLAY__CONFIG_WRAPPER(type, ...) (type) __VA_ARGS__ +#endif -#define CLAY_RECTANGLE_CONFIG(...) Clay__StoreRectangleElementConfig(CLAY__INIT(Clay_RectangleElementConfig) {__VA_ARGS__ }) +#define CLAY_LAYOUT(...) Clay__AttachLayoutConfig(Clay__StoreLayoutConfig(CLAY__CONFIG_WRAPPER(Clay_LayoutConfig, __VA_ARGS__))) -#define CLAY_TEXT_CONFIG(...) Clay__StoreTextElementConfig(CLAY__INIT(Clay_TextElementConfig) {__VA_ARGS__ }) +#define CLAY_RECTANGLE(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .rectangleElementConfig = Clay__StoreRectangleElementConfig(CLAY__INIT(Clay_RectangleElementConfig) __VA_ARGS__) }, CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE)) -#define CLAY_IMAGE_CONFIG(...) Clay__StoreImageElementConfig(CLAY__INIT(Clay_ImageElementConfig) {__VA_ARGS__ }) +#define CLAY_TEXT_CONFIG(...) Clay__StoreTextElementConfig(CLAY__CONFIG_WRAPPER(Clay_TextElementConfig, __VA_ARGS__)) -#define CLAY_FLOATING_CONFIG(...) Clay__StoreFloatingElementConfig(CLAY__INIT(Clay_FloatingElementConfig) {__VA_ARGS__ }) +#define CLAY_IMAGE(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .imageElementConfig = Clay__StoreImageElementConfig(CLAY__INIT(Clay_ImageElementConfig) __VA_ARGS__) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) -#define CLAY_CUSTOM_ELEMENT_CONFIG(...) Clay__StoreCustomElementConfig(CLAY__INIT(Clay_CustomElementConfig) {__VA_ARGS__ }) +#define CLAY_FLOATING(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .floatingElementConfig = Clay__StoreFloatingElementConfig(CLAY__INIT(Clay_FloatingElementConfig) __VA_ARGS__) }, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER)) -#define CLAY_SCROLL_CONFIG(...) Clay__StoreScrollElementConfig(CLAY__INIT(Clay_ScrollElementConfig) {__VA_ARGS__ }) +#define CLAY_CUSTOM_ELEMENT(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .customElementConfig = Clay__StoreCustomElementConfig(CLAY__INIT(Clay_CustomElementConfig) __VA_ARGS__) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM)) -#define CLAY_BORDER_CONFIG(...) Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { __VA_ARGS__ }) +#define CLAY_SCROLL(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .scrollElementConfig = Clay__StoreScrollElementConfig(CLAY__INIT(Clay_ScrollElementConfig) __VA_ARGS__) }, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) -#define CLAY_BORDER_CONFIG_OUTSIDE(...) Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { __VA_ARGS__ }, .right = { __VA_ARGS__ }, .top = { __VA_ARGS__ }, .bottom = { __VA_ARGS__ } }) +#define CLAY_BORDER(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) __VA_ARGS__) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER)) -#define CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(width, color, radius) Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .cornerRadius = { radius, radius, radius, radius } }) +#define CLAY_BORDER_OUTSIDE(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = __VA_ARGS__, .right = __VA_ARGS__, .top = __VA_ARGS__, .bottom = __VA_ARGS__ }) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER)) -#define CLAY_BORDER_CONFIG_ALL(...) Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { __VA_ARGS__ }, .right = { __VA_ARGS__ }, .top = { __VA_ARGS__ }, .bottom = { __VA_ARGS__ }, .betweenChildren = { __VA_ARGS__ } }) +#define CLAY_BORDER_OUTSIDE_RADIUS(width, color, radius) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .cornerRadius = { radius, radius, radius, radius } })}, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER)) -#define CLAY_BORDER_CONFIG_ALL_RADIUS(width, color, radius) Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .betweenChildren = { width, color }, .cornerRadius = { radius, radius, radius, radius }}) +#define CLAY_BORDER_ALL(...) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = __VA_ARGS__, .right = __VA_ARGS__, .top = __VA_ARGS__, .bottom = __VA_ARGS__, .betweenChildren = __VA_ARGS__ }) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER)) + +#define CLAY_BORDER_ALL_RADIUS(width, color, radius) Clay__AttachElementConfig(CLAY__CONFIG_WRAPPER(Clay_ElementConfigUnion, { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .betweenChildren = { width, color }, .cornerRadius = { radius, radius, radius, radius }}) })) #define CLAY_CORNER_RADIUS(radius) CLAY__INIT(Clay_CornerRadius) { radius, radius, radius, radius } -#define CLAY_SIZING_FIT(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) {__VA_ARGS__}, .type = CLAY__SIZING_TYPE_FIT } +#define CLAY__STRUCT_1_ARGS(a) a +#define CLAY__STRUCT_0_ARGS() {} +#define CLAY__STRUCT_OVERRIDE(_0, _1, NAME, ...) NAME -#define CLAY_SIZING_GROW(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) {__VA_ARGS__}, .type = CLAY__SIZING_TYPE_GROW } +#define CLAY__SIZING_FIT_INTERNAL(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) __VA_ARGS__, .type = CLAY__SIZING_TYPE_FIT } +#define CLAY_SIZING_FIT(...) CLAY__SIZING_FIT_INTERNAL(CLAY__STRUCT_OVERRIDE("empty", ##__VA_ARGS__, CLAY__STRUCT_1_ARGS, CLAY__STRUCT_0_ARGS)(__VA_ARGS__)) -#define CLAY_SIZING_FIXED(fixedSize) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) { fixedSize, fixedSize }, .type = CLAY__SIZING_TYPE_FIXED } +#define CLAY__SIZING_GROW_INTERNAL(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) __VA_ARGS__, .type = CLAY__SIZING_TYPE_GROW } +#define CLAY_SIZING_GROW(...) CLAY__SIZING_GROW_INTERNAL(CLAY__STRUCT_OVERRIDE("empty", ##__VA_ARGS__, CLAY__STRUCT_1_ARGS, CLAY__STRUCT_0_ARGS)(__VA_ARGS__)) + +#define CLAY_SIZING_FIXED(fixedSize) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = { fixedSize, fixedSize }, .type = CLAY__SIZING_TYPE_FIXED } #define CLAY_SIZING_PERCENT(percentOfParent) CLAY__INIT(Clay_SizingAxis) { .sizePercent = (percentOfParent), .type = CLAY__SIZING_TYPE_PERCENT } -#define CLAY_ID(label) Clay__HashString(CLAY_STRING(label), 0, 0) +#define CLAY_ID(label) Clay__AttachId(Clay__HashString(CLAY_STRING(label), 0, 0)) -#define CLAY_IDI(label, index) Clay__HashString(CLAY_STRING(label), index, 0) +#define CLAY_IDI(label, index) Clay__AttachId(Clay__HashString(CLAY_STRING(label), index, 0)) #define CLAY_ID_LOCAL(label) CLAY_IDI_LOCAL(label, 0) -#define CLAY_IDI_LOCAL(label, index) Clay__HashString(CLAY_STRING(label), index, Clay__GetOpenLayoutElementId()) +#define CLAY_IDI_LOCAL(label, index) Clay__AttachId(Clay__HashString(CLAY_STRING(label), Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&Clay__openLayoutElementStack, Clay__openLayoutElementStack.length - 2))->children.length + 1, Clay__GetOpenLayoutElement()->id)) #define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof((s)[0])) - sizeof((s)[0])) @@ -78,33 +90,26 @@ static int CLAY__ELEMENT_DEFINITION_LATCH = 0; -#define CLAY__ELEMENT_INTERNAL(open, close, ...) \ +#define CLAY__ELEMENT_INTERNAL(...) \ for (\ - CLAY__ELEMENT_DEFINITION_LATCH = (open(__VA_ARGS__), 0); \ + CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElement(), __VA_ARGS__, Clay__ElementPostConfiguration(), 0); \ CLAY__ELEMENT_DEFINITION_LATCH < 1; \ - ++CLAY__ELEMENT_DEFINITION_LATCH, close() \ + ++CLAY__ELEMENT_DEFINITION_LATCH, Clay__CloseElement() \ ) +#define CLAY__6_ARGS(a, b, c, d, e, f) a, b, c, d, e, f +#define CLAY__5_ARGS(a, b, c, d, e) a, b, c, d, e +#define CLAY__4_ARGS(a, b, c, d) a, b, c, d +#define CLAY__3_ARGS(a, b, c) a, b, c +#define CLAY__2_ARGS(a, b) a, b +#define CLAY__1_ARGS(a) a +#define CLAY__0_ARGS() Clay__Noop() +#define CLAY__ARGS_OVERRIDE(_0, _1, _2, _3, _4, _5, _6, NAME, ...) NAME // Publicly visible layout element macros ----------------------------------------------------- -#define CLAY_CONTAINER(...) CLAY__ELEMENT_INTERNAL(Clay__OpenContainerElement, Clay__CloseElementWithChildren, __VA_ARGS__) +#define CLAY(...) CLAY__ELEMENT_INTERNAL(CLAY__ARGS_OVERRIDE("empty", ##__VA_ARGS__, CLAY__6_ARGS, CLAY__5_ARGS, CLAY__4_ARGS, CLAY__3_ARGS, CLAY__2_ARGS, CLAY__1_ARGS, CLAY__0_ARGS)(__VA_ARGS__)) -#define CLAY_RECTANGLE(...) CLAY__ELEMENT_INTERNAL(Clay__OpenRectangleElement, Clay__CloseElementWithChildren, __VA_ARGS__) - -#define CLAY_TEXT(id, text, textConfig) Clay__OpenTextElement(id, text, textConfig) - -#define CLAY_IMAGE(...) CLAY__ELEMENT_INTERNAL(Clay__OpenImageElement, Clay__CloseElementWithChildren, __VA_ARGS__) - -#define CLAY_SCROLL_CONTAINER(...) CLAY__ELEMENT_INTERNAL(Clay__OpenScrollElement, Clay__CloseScrollElement, __VA_ARGS__) - -#define CLAY_FLOATING_CONTAINER(...) CLAY__ELEMENT_INTERNAL(Clay__OpenFloatingElement, Clay__CloseFloatingElement, __VA_ARGS__) - -#define CLAY_BORDER_CONTAINER(...) CLAY__ELEMENT_INTERNAL(Clay__OpenBorderElement, Clay__CloseElementWithChildren, __VA_ARGS__) - -#define CLAY_CUSTOM_ELEMENT(id, layoutConfig, customElementConfig, children) \ - Clay__OpenCustomElement(id, layoutConfig, customElementConfig); \ - children \ - Clay__CloseElementWithChildren() +#define CLAY_TEXT(text, textConfig) Clay__OpenTextElement(text, textConfig) #ifdef __cplusplus #define CLAY__INIT(type) type @@ -128,6 +133,13 @@ typedef struct { const char *chars; } Clay_String; +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay_String *internalArray; +} Clay__StringArray; + typedef struct { Clay_String label; uint64_t nextAllocation; @@ -166,6 +178,16 @@ typedef struct { float bottomRight; } Clay_CornerRadius; +typedef enum CLAY_PACKED_ENUM { + CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE = 1, + CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER = 2, + CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER = 4, + CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER = 8, + CLAY__ELEMENT_CONFIG_TYPE_IMAGE = 16, + CLAY__ELEMENT_CONFIG_TYPE_TEXT = 32, + CLAY__ELEMENT_CONFIG_TYPE_CUSTOM = 64, +} Clay__ElementConfigType; + // Element Configs --------------------------- // Layout typedef enum CLAY_PACKED_ENUM { @@ -343,6 +365,12 @@ typedef union Clay_BorderElementConfig *borderElementConfig; } Clay_ElementConfigUnion; +typedef struct +{ + Clay__ElementConfigType type; + Clay_ElementConfigUnion config; +} Clay_ElementConfig; + // Miscellaneous Structs & Enums --------------------------------- typedef struct { @@ -374,6 +402,9 @@ typedef struct Clay_String text; // TODO I wish there was a way to avoid having to have this on every render command uint32_t id; Clay_RenderCommandType commandType; + #ifdef CLAY_DEBUG + Clay_String name; + #endif } Clay_RenderCommand; typedef struct @@ -383,8 +414,22 @@ typedef struct Clay_RenderCommand *internalArray; } Clay_RenderCommandArray; +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; + // Function Forward Declarations --------------------------------- -// Public API functions +// Public API functions --- uint32_t Clay_MinMemorySize(); Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset); void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown); @@ -393,26 +438,22 @@ void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDe void Clay_SetLayoutDimensions(Clay_Dimensions dimensions); void Clay_BeginLayout(); Clay_RenderCommandArray Clay_EndLayout(); -bool Clay_PointerOver(Clay_ElementId id); +Clay_ElementId Clay_GetElementId(Clay_String idString); +bool Clay_Hovered(); +void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData), intptr_t userData); Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id); void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_String *text, Clay_TextElementConfig *config)); Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index); void Clay_SetDebugModeEnabled(bool enabled); -void Clay__OpenContainerElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig); -void Clay__OpenRectangleElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_RectangleElementConfig *rectangleConfig); -void Clay__OpenTextElement(Clay_ElementId id, Clay_String text, Clay_TextElementConfig *textConfig); -void Clay__OpenImageElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_ImageElementConfig *imageConfig); -void Clay__OpenScrollElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_ScrollElementConfig *scrollConfig); -void Clay__OpenFloatingElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingConfig); -void Clay__OpenBorderElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_BorderElementConfig *borderConfig); -void Clay__OpenCustomElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customConfig); -void Clay__CloseElementWithChildren(); -void Clay__CloseScrollElement(); -void Clay__CloseFloatingElement(); - // Internal API functions required by macros +void Clay__OpenElement(); +void Clay__CloseElement(); Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config); +void Clay__ElementPostConfiguration(); +void Clay__AttachId(Clay_ElementId id); +void Clay__AttachLayoutConfig(Clay_LayoutConfig *config); +void Clay__AttachElementConfig(Clay_ElementConfigUnion config, Clay__ElementConfigType type); Clay_RectangleElementConfig * Clay__StoreRectangleElementConfig(Clay_RectangleElementConfig config); Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config); Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config); @@ -421,7 +462,7 @@ Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConf Clay_ScrollElementConfig * Clay__StoreScrollElementConfig(Clay_ScrollElementConfig config); Clay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config); Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed); -uint32_t Clay__GetOpenLayoutElementId(void); +void Clay__Noop(); extern Clay_Color Clay__debugViewHighlightColor; extern uint32_t Clay__debugViewWidth; @@ -459,8 +500,10 @@ extern uint32_t Clay__debugViewWidth; bool Clay__warningsEnabled = true; +void Clay__Noop() {}; + Clay_String CLAY__SPACECHAR = CLAY__INIT(Clay_String) { .length = 1, .chars = " " }; -Clay_String CLAY__STRING_DEFAULT = CLAY__INIT(Clay_String) { .length = 0, .chars = "" }; +Clay_String CLAY__STRING_DEFAULT = CLAY__INIT(Clay_String) { .length = 0, .chars = NULL }; typedef struct { @@ -563,7 +606,7 @@ bool Clay__Array_IncrementCapacityCheck(uint32_t length, uint32_t capacity) bool CLAY__BOOL_DEFAULT = false; -// __GENERATED__ template array_define TYPE=bool NAME=Clay__BoolArray +// __GENERATED__ template array_define,array_allocate TYPE=bool NAME=Clay__BoolArray #pragma region generated typedef struct { @@ -571,7 +614,6 @@ typedef struct uint32_t length; bool *internalArray; } Clay__BoolArray; - Clay__BoolArray Clay__BoolArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__BoolArray){.capacity = capacity, .length = 0, .internalArray = (bool *)Clay__Array_Allocate_Arena(capacity, sizeof(bool), CLAY__ALIGNMENT(bool), arena)}; } @@ -580,7 +622,7 @@ Clay__BoolArray Clay__BoolArray_Allocate_Arena(uint32_t capacity, Clay_Arena *ar Clay_ElementId CLAY__ELEMENT_ID_DEFAULT = CLAY__INIT(Clay_ElementId) {}; -// __GENERATED__ template array_define,array_get,array_add TYPE=Clay_ElementId NAME=Clay__ElementIdArray DEFAULT_VALUE=&CLAY__ELEMENT_ID_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_get,array_add TYPE=Clay_ElementId NAME=Clay__ElementIdArray DEFAULT_VALUE=&CLAY__ELEMENT_ID_DEFAULT #pragma region generated typedef struct { @@ -588,7 +630,6 @@ typedef struct uint32_t length; Clay_ElementId *internalArray; } Clay__ElementIdArray; - Clay__ElementIdArray Clay__ElementIdArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__ElementIdArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ElementId *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ElementId), CLAY__ALIGNMENT(Clay_ElementId), arena)}; } @@ -605,31 +646,43 @@ Clay_ElementId *Clay__ElementIdArray_Add(Clay__ElementIdArray *array, Clay_Eleme #pragma endregion // __GENERATED__ template -typedef enum CLAY_PACKED_ENUM { - CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER, - CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE, - CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER, - CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER, - CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER, - CLAY__LAYOUT_ELEMENT_TYPE_IMAGE, - CLAY__LAYOUT_ELEMENT_TYPE_TEXT, - CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM, -} Clay__LayoutElementType; +Clay_ElementConfig CLAY__ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_ElementConfig) {}; -Clay_RenderCommandType Clay__LayoutElementTypeToRenderCommandType[] = { - CLAY_RENDER_COMMAND_TYPE_NONE, - CLAY_RENDER_COMMAND_TYPE_RECTANGLE, - CLAY_RENDER_COMMAND_TYPE_NONE, - CLAY_RENDER_COMMAND_TYPE_NONE, - CLAY_RENDER_COMMAND_TYPE_BORDER, - CLAY_RENDER_COMMAND_TYPE_IMAGE, - CLAY_RENDER_COMMAND_TYPE_TEXT, - CLAY_RENDER_COMMAND_TYPE_CUSTOM, -}; +// __GENERATED__ template array_define,array_define_slice,array_allocate,array_get,array_add,array_get_slice TYPE=Clay_ElementConfig NAME=Clay__ElementConfigArray DEFAULT_VALUE=&CLAY__ELEMENT_CONFIG_DEFAULT +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay_ElementConfig *internalArray; +} Clay__ElementConfigArray; +typedef struct +{ + uint32_t length; + Clay_ElementConfig *internalArray; +} Clay__ElementConfigArraySlice; +Clay__ElementConfigArray Clay__ElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return CLAY__INIT(Clay__ElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ElementConfig), CLAY__ALIGNMENT(Clay_ElementConfig), arena)}; +} +Clay_ElementConfig *Clay__ElementConfigArray_Get(Clay__ElementConfigArray *array, int index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__ELEMENT_CONFIG_DEFAULT; +} +Clay_ElementConfig *Clay__ElementConfigArray_Add(Clay__ElementConfigArray *array, Clay_ElementConfig item) { + if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__ELEMENT_CONFIG_DEFAULT; +} +Clay_ElementConfig *Clay__ElementConfigArraySlice_Get(Clay__ElementConfigArraySlice *slice, int index) { + return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : &CLAY__ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template -Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = CLAY__INIT(Clay_LayoutConfig){}; +Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = CLAY__INIT(Clay_LayoutConfig){ .sizing = { .width = { .sizeMinMax = {0, CLAY__MAXFLOAT }, .type = CLAY__SIZING_TYPE_FIT }, .height = { .sizeMinMax = {0, CLAY__MAXFLOAT }, .type = CLAY__SIZING_TYPE_FIT } } }; -// __GENERATED__ template array_define,array_add TYPE=Clay_LayoutConfig NAME=Clay__LayoutConfigArray DEFAULT_VALUE=&CLAY_LAYOUT_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_LayoutConfig NAME=Clay__LayoutConfigArray DEFAULT_VALUE=&CLAY_LAYOUT_DEFAULT #pragma region generated typedef struct { @@ -637,7 +690,6 @@ typedef struct uint32_t length; Clay_LayoutConfig *internalArray; } Clay__LayoutConfigArray; - Clay__LayoutConfigArray Clay__LayoutConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__LayoutConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutConfig), CLAY__ALIGNMENT(Clay_LayoutConfig), arena)}; } @@ -653,7 +705,7 @@ Clay_LayoutConfig *Clay__LayoutConfigArray_Add(Clay__LayoutConfigArray *array, C Clay_RectangleElementConfig CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_RectangleElementConfig){{0}}; -// __GENERATED__ template array_define,array_add TYPE=Clay_RectangleElementConfig NAME=Clay__RectangleElementConfigArray DEFAULT_VALUE=&CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_RectangleElementConfig NAME=Clay__RectangleElementConfigArray DEFAULT_VALUE=&CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -661,7 +713,6 @@ typedef struct uint32_t length; Clay_RectangleElementConfig *internalArray; } Clay__RectangleElementConfigArray; - Clay__RectangleElementConfigArray Clay__RectangleElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__RectangleElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_RectangleElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_RectangleElementConfig), CLAY__ALIGNMENT(Clay_RectangleElementConfig), arena)}; } @@ -677,7 +728,7 @@ Clay_RectangleElementConfig *Clay__RectangleElementConfigArray_Add(Clay__Rectang Clay_TextElementConfig CLAY__TEXT_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_TextElementConfig) {}; -// __GENERATED__ template array_define,array_add TYPE=Clay_TextElementConfig NAME=Clay__TextElementConfigArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_TextElementConfig NAME=Clay__TextElementConfigArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -685,7 +736,6 @@ typedef struct uint32_t length; Clay_TextElementConfig *internalArray; } Clay__TextElementConfigArray; - Clay__TextElementConfigArray Clay__TextElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__TextElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_TextElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_TextElementConfig), CLAY__ALIGNMENT(Clay_TextElementConfig), arena)}; } @@ -701,7 +751,7 @@ Clay_TextElementConfig *Clay__TextElementConfigArray_Add(Clay__TextElementConfig Clay_ImageElementConfig CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_ImageElementConfig) {}; -// __GENERATED__ template array_define,array_add TYPE=Clay_ImageElementConfig NAME=Clay__ImageElementConfigArray DEFAULT_VALUE=&CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_ImageElementConfig NAME=Clay__ImageElementConfigArray DEFAULT_VALUE=&CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -709,7 +759,6 @@ typedef struct uint32_t length; Clay_ImageElementConfig *internalArray; } Clay__ImageElementConfigArray; - Clay__ImageElementConfigArray Clay__ImageElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__ImageElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ImageElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ImageElementConfig), CLAY__ALIGNMENT(Clay_ImageElementConfig), arena)}; } @@ -725,7 +774,7 @@ Clay_ImageElementConfig *Clay__ImageElementConfigArray_Add(Clay__ImageElementCon Clay_FloatingElementConfig CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_FloatingElementConfig) {}; -// __GENERATED__ template array_define,array_add TYPE=Clay_FloatingElementConfig NAME=Clay__FloatingElementConfigArray DEFAULT_VALUE=&CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_FloatingElementConfig NAME=Clay__FloatingElementConfigArray DEFAULT_VALUE=&CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -733,7 +782,6 @@ typedef struct uint32_t length; Clay_FloatingElementConfig *internalArray; } Clay__FloatingElementConfigArray; - Clay__FloatingElementConfigArray Clay__FloatingElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__FloatingElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_FloatingElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_FloatingElementConfig), CLAY__ALIGNMENT(Clay_FloatingElementConfig), arena)}; } @@ -749,7 +797,7 @@ Clay_FloatingElementConfig *Clay__FloatingElementConfigArray_Add(Clay__FloatingE Clay_CustomElementConfig CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_CustomElementConfig) {}; -// __GENERATED__ template array_define,array_add TYPE=Clay_CustomElementConfig NAME=Clay__CustomElementConfigArray DEFAULT_VALUE=&CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_CustomElementConfig NAME=Clay__CustomElementConfigArray DEFAULT_VALUE=&CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -757,7 +805,6 @@ typedef struct uint32_t length; Clay_CustomElementConfig *internalArray; } Clay__CustomElementConfigArray; - Clay__CustomElementConfigArray Clay__CustomElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__CustomElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_CustomElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_CustomElementConfig), CLAY__ALIGNMENT(Clay_CustomElementConfig), arena)}; } @@ -773,7 +820,7 @@ Clay_CustomElementConfig *Clay__CustomElementConfigArray_Add(Clay__CustomElement Clay_ScrollElementConfig CLAY__SCROLL_CONTAINER_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_ScrollElementConfig ) {}; -// __GENERATED__ template array_define,array_add TYPE=Clay_ScrollElementConfig NAME=Clay__ScrollElementConfigArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_ScrollElementConfig NAME=Clay__ScrollElementConfigArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -781,7 +828,6 @@ typedef struct uint32_t length; Clay_ScrollElementConfig *internalArray; } Clay__ScrollElementConfigArray; - Clay__ScrollElementConfigArray Clay__ScrollElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__ScrollElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ScrollElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ScrollElementConfig), CLAY__ALIGNMENT(Clay_ScrollElementConfig), arena)}; } @@ -795,15 +841,37 @@ Clay_ScrollElementConfig *Clay__ScrollElementConfigArray_Add(Clay__ScrollElement #pragma endregion // __GENERATED__ template +// __GENERATED__ template array_define_slice,array_allocate,array_add TYPE=Clay_String NAME=Clay__StringArray DEFAULT_VALUE=&CLAY__STRING_DEFAULT +#pragma region generated typedef struct { - uint32_t elementIndex; + uint32_t length; + Clay_String *internalArray; +} Clay__StringArraySlice; +Clay__StringArray Clay__StringArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return CLAY__INIT(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 struct +{ + Clay_String text; Clay_Dimensions preferredDimensions; + uint32_t elementIndex; + Clay__StringArraySlice wrappedLines; } Clay__TextElementData; Clay__TextElementData CLAY__TEXT_ELEMENT_DATA_DEFAULT = CLAY__INIT(Clay__TextElementData) {}; -// __GENERATED__ template array_define,array_get,array_add TYPE=Clay__TextElementData NAME=Clay__TextElementDataArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_DATA_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_get,array_add TYPE=Clay__TextElementData NAME=Clay__TextElementDataArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_DATA_DEFAULT #pragma region generated typedef struct { @@ -811,7 +879,6 @@ typedef struct uint32_t length; Clay__TextElementData *internalArray; } Clay__TextElementDataArray; - Clay__TextElementDataArray Clay__TextElementDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__TextElementDataArray){.capacity = capacity, .length = 0, .internalArray = (Clay__TextElementData *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__TextElementData), CLAY__ALIGNMENT(Clay__TextElementData), arena)}; } @@ -830,7 +897,7 @@ Clay__TextElementData *Clay__TextElementDataArray_Add(Clay__TextElementDataArray Clay_BorderElementConfig CLAY__BORDER_CONTAINER_ELEMENT_CONFIG_DEFAULT = CLAY__INIT(Clay_BorderElementConfig ) {}; -// __GENERATED__ template array_define,array_add TYPE=Clay_BorderElementConfig NAME=Clay__BorderElementConfigArray DEFAULT_VALUE=&CLAY__BORDER_CONTAINER_ELEMENT_CONFIG_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_BorderElementConfig NAME=Clay__BorderElementConfigArray DEFAULT_VALUE=&CLAY__BORDER_CONTAINER_ELEMENT_CONFIG_DEFAULT #pragma region generated typedef struct { @@ -838,7 +905,6 @@ typedef struct uint32_t length; Clay_BorderElementConfig *internalArray; } Clay__BorderElementConfigArray; - Clay__BorderElementConfigArray Clay__BorderElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__BorderElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_BorderElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_BorderElementConfig), CLAY__ALIGNMENT(Clay_BorderElementConfig), arena)}; } @@ -858,26 +924,26 @@ typedef struct uint16_t length; } Clay__LayoutElementChildren; -typedef struct t_Clay_LayoutElement +typedef struct { #ifdef CLAY_DEBUG Clay_String name; #endif union { Clay__LayoutElementChildren children; - Clay_String text; + Clay__TextElementData *textElementData; }; Clay_Dimensions dimensions; Clay_Dimensions minDimensions; Clay_LayoutConfig *layoutConfig; - Clay_ElementConfigUnion elementConfig; + Clay__ElementConfigArraySlice elementConfigs; + uint32_t configsEnabled; uint32_t id; - Clay__LayoutElementType elementType; } Clay_LayoutElement; Clay_LayoutElement CLAY__LAYOUT_ELEMENT_DEFAULT = CLAY__INIT(Clay_LayoutElement) {}; -// __GENERATED__ template array_define,array_add,array_get TYPE=Clay_LayoutElement NAME=Clay_LayoutElementArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add,array_get TYPE=Clay_LayoutElement NAME=Clay_LayoutElementArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_DEFAULT #pragma region generated typedef struct { @@ -885,7 +951,6 @@ typedef struct uint32_t length; Clay_LayoutElement *internalArray; } Clay_LayoutElementArray; - Clay_LayoutElementArray Clay_LayoutElementArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay_LayoutElementArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElement *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElement), CLAY__ALIGNMENT(Clay_LayoutElement), arena)}; } @@ -902,7 +967,7 @@ Clay_LayoutElement *Clay_LayoutElementArray_Get(Clay_LayoutElementArray *array, #pragma endregion // __GENERATED__ template -// __GENERATED__ template array_define,array_add,array_get,array_remove_swapback TYPE=Clay_LayoutElement* NAME=Clay__LayoutElementPointerArray DEFAULT_VALUE=CLAY__NULL +// __GENERATED__ template array_define,array_allocate,array_add,array_get_value,array_remove_swapback TYPE=Clay_LayoutElement* NAME=Clay__LayoutElementPointerArray DEFAULT_VALUE=CLAY__NULL #pragma region generated typedef struct { @@ -910,7 +975,6 @@ typedef struct uint32_t length; Clay_LayoutElement* *internalArray; } Clay__LayoutElementPointerArray; - Clay__LayoutElementPointerArray Clay__LayoutElementPointerArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__LayoutElementPointerArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElement* *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElement*), CLAY__ALIGNMENT(Clay_LayoutElement*), arena)}; } @@ -921,8 +985,8 @@ Clay_LayoutElement* *Clay__LayoutElementPointerArray_Add(Clay__LayoutElementPoin } return CLAY__NULL; } -Clay_LayoutElement* *Clay__LayoutElementPointerArray_Get(Clay__LayoutElementPointerArray *array, int index) { - return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : CLAY__NULL; +Clay_LayoutElement* Clay__LayoutElementPointerArray_Get(Clay__LayoutElementPointerArray *array, int index) { + return Clay__Array_RangeCheck(index, array->length) ? array->internalArray[index] : CLAY__NULL; } Clay_LayoutElement* Clay__LayoutElementPointerArray_RemoveSwapback(Clay__LayoutElementPointerArray *array, int index) { if (Clay__Array_RangeCheck(index, array->length)) { @@ -938,16 +1002,11 @@ Clay_LayoutElement* Clay__LayoutElementPointerArray_RemoveSwapback(Clay__LayoutE Clay_RenderCommand CLAY__RENDER_COMMAND_DEFAULT = CLAY__INIT(Clay_RenderCommand) {}; -// __GENERATED__ template array_define TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray +// __GENERATED__ template array_allocate,array_add,array_get TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray DEFAULT_VALUE=&CLAY__RENDER_COMMAND_DEFAULT #pragma region generated Clay_RenderCommandArray Clay_RenderCommandArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay_RenderCommandArray){.capacity = capacity, .length = 0, .internalArray = (Clay_RenderCommand *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_RenderCommand), CLAY__ALIGNMENT(Clay_RenderCommand), arena)}; } -#pragma endregion -// __GENERATED__ template - -// __GENERATED__ template array_add TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray DEFAULT_VALUE=&CLAY__RENDER_COMMAND_DEFAULT -#pragma region generated Clay_RenderCommand *Clay_RenderCommandArray_Add(Clay_RenderCommandArray *array, Clay_RenderCommand item) { if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; @@ -955,11 +1014,6 @@ Clay_RenderCommand *Clay_RenderCommandArray_Add(Clay_RenderCommandArray *array, } return &CLAY__RENDER_COMMAND_DEFAULT; } -#pragma endregion -// __GENERATED__ template - -// __GENERATED__ template array_get TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray DEFAULT_VALUE=&CLAY__RENDER_COMMAND_DEFAULT -#pragma region generated Clay_RenderCommand *Clay_RenderCommandArray_Get(Clay_RenderCommandArray *array, int index) { return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__RENDER_COMMAND_DEFAULT; } @@ -984,7 +1038,7 @@ typedef struct Clay__ScrollContainerDataInternal CLAY__SCROLL_CONTAINER_DEFAULT = CLAY__INIT(Clay__ScrollContainerDataInternal) {}; -// __GENERATED__ template define,array_add,array_get TYPE=Clay__ScrollContainerDataInternal NAME=Clay__ScrollContainerDataInternalArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add,array_get TYPE=Clay__ScrollContainerDataInternal NAME=Clay__ScrollContainerDataInternalArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT #pragma region generated typedef struct { @@ -992,7 +1046,6 @@ typedef struct uint32_t length; Clay__ScrollContainerDataInternal *internalArray; } Clay__ScrollContainerDataInternalArray; - Clay__ScrollContainerDataInternalArray Clay__ScrollContainerDataInternalArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__ScrollContainerDataInternalArray){.capacity = capacity, .length = 0, .internalArray = (Clay__ScrollContainerDataInternal *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__ScrollContainerDataInternal), CLAY__ALIGNMENT(Clay__ScrollContainerDataInternal), arena)}; } @@ -1031,7 +1084,7 @@ typedef struct Clay__DebugElementData CLAY__DEBUG_ELEMENT_DATA_DEFAULT = CLAY__INIT(Clay__DebugElementData) {}; -// __GENERATED__ template array_define,array_add,array_get TYPE=Clay__DebugElementData NAME=Clay__DebugElementDataArray DEFAULT_VALUE=&CLAY__DEBUG_ELEMENT_DATA_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add,array_get TYPE=Clay__DebugElementData NAME=Clay__DebugElementDataArray DEFAULT_VALUE=&CLAY__DEBUG_ELEMENT_DATA_DEFAULT #pragma region generated typedef struct { @@ -1039,7 +1092,6 @@ typedef struct uint32_t length; Clay__DebugElementData *internalArray; } Clay__DebugElementDataArray; - Clay__DebugElementDataArray Clay__DebugElementDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__DebugElementDataArray){.capacity = capacity, .length = 0, .internalArray = (Clay__DebugElementData *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__DebugElementData), CLAY__ALIGNMENT(Clay__DebugElementData), arena)}; } @@ -1056,11 +1108,13 @@ Clay__DebugElementData *Clay__DebugElementDataArray_Get(Clay__DebugElementDataAr #pragma endregion // __GENERATED__ template -typedef struct +typedef struct // todo get this struct into a single cache line { Clay_BoundingBox boundingBox; Clay_ElementId elementId; Clay_LayoutElement* layoutElement; + void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData); + intptr_t hoverFunctionUserData; int32_t nextIndex; uint32_t generation; Clay__DebugElementData *debugData; @@ -1068,7 +1122,7 @@ typedef struct Clay_LayoutElementHashMapItem CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT = CLAY__INIT(Clay_LayoutElementHashMapItem) { .layoutElement = &CLAY__LAYOUT_ELEMENT_DEFAULT }; -// __GENERATED__ template array_define,array_get,array_add TYPE=Clay_LayoutElementHashMapItem NAME=Clay__LayoutElementHashMapItemArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_get,array_add TYPE=Clay_LayoutElementHashMapItem NAME=Clay__LayoutElementHashMapItemArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT #pragma region generated typedef struct { @@ -1076,7 +1130,6 @@ typedef struct uint32_t length; Clay_LayoutElementHashMapItem *internalArray; } Clay__LayoutElementHashMapItemArray; - Clay__LayoutElementHashMapItemArray Clay__LayoutElementHashMapItemArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__LayoutElementHashMapItemArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElementHashMapItem *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElementHashMapItem), CLAY__ALIGNMENT(Clay_LayoutElementHashMapItem), arena)}; } @@ -1095,7 +1148,51 @@ Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Add(Clay__Lay typedef struct { - Clay_Dimensions dimensions; + Clay_String word; + uint32_t startOffset; + uint32_t length; + float width; +} Clay__MeasuredWord; + +Clay__MeasuredWord CLAY__MEASURED_WORD_DEFAULT = CLAY__INIT(Clay__MeasuredWord) {}; + +// __GENERATED__ template array_define,array_define_slice,array_allocate,array_get,array_get_slice,array_add TYPE=Clay__MeasuredWord NAME=Clay__MeasuredWordArray DEFAULT_VALUE=&CLAY__MEASURED_WORD_DEFAULT +#pragma region generated +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay__MeasuredWord *internalArray; +} Clay__MeasuredWordArray; +typedef struct +{ + uint32_t length; + Clay__MeasuredWord *internalArray; +} Clay__MeasuredWordArraySlice; +Clay__MeasuredWordArray Clay__MeasuredWordArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return CLAY__INIT(Clay__MeasuredWordArray){.capacity = capacity, .length = 0, .internalArray = (Clay__MeasuredWord *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__MeasuredWord), CLAY__ALIGNMENT(Clay__MeasuredWord), arena)}; +} +Clay__MeasuredWord *Clay__MeasuredWordArray_Get(Clay__MeasuredWordArray *array, int index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__MEASURED_WORD_DEFAULT; +} +Clay__MeasuredWord *Clay__MeasuredWordArraySlice_Get(Clay__MeasuredWordArraySlice *slice, int index) { + return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : &CLAY__MEASURED_WORD_DEFAULT; +} +Clay__MeasuredWord *Clay__MeasuredWordArray_Add(Clay__MeasuredWordArray *array, Clay__MeasuredWord item) { + if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__MEASURED_WORD_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +typedef struct +{ + Clay_Dimensions unwrappedDimensions; + Clay__MeasuredWordArraySlice measuredWords; + // Hash map data uint32_t id; int32_t nextIndex; uint32_t generation; @@ -1103,7 +1200,7 @@ typedef struct Clay__MeasureTextCacheItem CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT = CLAY__INIT(Clay__MeasureTextCacheItem) { }; -// __GENERATED__ template array_define,array_get,array_add,array_set TYPE=Clay__MeasureTextCacheItem NAME=Clay__MeasureTextCacheItemArray DEFAULT_VALUE=&CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_get,array_add,array_set TYPE=Clay__MeasureTextCacheItem NAME=Clay__MeasureTextCacheItemArray DEFAULT_VALUE=&CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT #pragma region generated typedef struct { @@ -1111,7 +1208,6 @@ typedef struct uint32_t length; Clay__MeasureTextCacheItem *internalArray; } Clay__MeasureTextCacheItemArray; - Clay__MeasureTextCacheItemArray Clay__MeasureTextCacheItemArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__MeasureTextCacheItemArray){.capacity = capacity, .length = 0, .internalArray = (Clay__MeasureTextCacheItem *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__MeasureTextCacheItem), CLAY__ALIGNMENT(Clay__MeasureTextCacheItem), arena)}; } @@ -1141,7 +1237,7 @@ void Clay__MeasureTextCacheItemArray_Set(Clay__MeasureTextCacheItemArray *array, #pragma endregion // __GENERATED__ template -// __GENERATED__ template array_define,array_get_value,array_add_value,array_set TYPE=int32_t NAME=Clay__int32_tArray DEFAULT_VALUE=-1 +// __GENERATED__ template array_define,array_allocate,array_get_value,array_add_value,array_set,array_remove_swapback TYPE=int32_t NAME=Clay__int32_tArray DEFAULT_VALUE=-1 #pragma region generated typedef struct { @@ -1149,7 +1245,6 @@ typedef struct uint32_t length; int32_t *internalArray; } Clay__int32_tArray; - Clay__int32_tArray Clay__int32_tArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__int32_tArray){.capacity = capacity, .length = 0, .internalArray = (int32_t *)Clay__Array_Allocate_Arena(capacity, sizeof(int32_t), CLAY__ALIGNMENT(int32_t), arena)}; } @@ -1174,11 +1269,18 @@ void Clay__int32_tArray_Set(Clay__int32_tArray *array, int index, int32_t value) #endif } } +int32_t Clay__int32_tArray_RemoveSwapback(Clay__int32_tArray *array, int index) { + if (Clay__Array_RangeCheck(index, array->length)) { + array->length--; + int32_t removed = array->internalArray[index]; + array->internalArray[index] = array->internalArray[array->length]; + return removed; + } + return -1; +} #pragma endregion // __GENERATED__ template -Clay_LayoutElement *Clay__openLayoutElement = CLAY__NULL; - typedef struct { Clay_LayoutElement *layoutElement; @@ -1188,7 +1290,7 @@ typedef struct Clay__LayoutElementTreeNode CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT = CLAY__INIT(Clay__LayoutElementTreeNode) {}; -// __GENERATED__ template array_define,array_add,array_get TYPE=Clay__LayoutElementTreeNode NAME=Clay__LayoutElementTreeNodeArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add,array_get TYPE=Clay__LayoutElementTreeNode NAME=Clay__LayoutElementTreeNodeArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT #pragma region generated typedef struct { @@ -1196,7 +1298,6 @@ typedef struct uint32_t length; Clay__LayoutElementTreeNode *internalArray; } Clay__LayoutElementTreeNodeArray; - Clay__LayoutElementTreeNodeArray Clay__LayoutElementTreeNodeArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__LayoutElementTreeNodeArray){.capacity = capacity, .length = 0, .internalArray = (Clay__LayoutElementTreeNode *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__LayoutElementTreeNode), CLAY__ALIGNMENT(Clay__LayoutElementTreeNode), arena)}; } @@ -1223,7 +1324,7 @@ typedef struct Clay__LayoutElementTreeRoot CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT = CLAY__INIT(Clay__LayoutElementTreeRoot) {}; -// __GENERATED__ template array_define,array_add,array_get TYPE=Clay__LayoutElementTreeRoot NAME=Clay__LayoutElementTreeRootArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT +// __GENERATED__ template array_define,array_allocate,array_add,array_get TYPE=Clay__LayoutElementTreeRoot NAME=Clay__LayoutElementTreeRootArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT #pragma region generated typedef struct { @@ -1231,7 +1332,6 @@ typedef struct uint32_t length; Clay__LayoutElementTreeRoot *internalArray; } Clay__LayoutElementTreeRootArray; - Clay__LayoutElementTreeRootArray Clay__LayoutElementTreeRootArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__LayoutElementTreeRootArray){.capacity = capacity, .length = 0, .internalArray = (Clay__LayoutElementTreeRoot *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__LayoutElementTreeRoot), CLAY__ALIGNMENT(Clay__LayoutElementTreeRoot), arena)}; } @@ -1248,7 +1348,7 @@ Clay__LayoutElementTreeRoot *Clay__LayoutElementTreeRootArray_Get(Clay__LayoutEl #pragma endregion // __GENERATED__ template -// __GENERATED__ template array_define TYPE=uint8_t NAME=Clay__CharArray DEFAULT_VALUE=0 +// __GENERATED__ template array_define,array_allocate TYPE=uint8_t NAME=Clay__CharArray DEFAULT_VALUE=0 #pragma region generated typedef struct { @@ -1256,7 +1356,6 @@ typedef struct uint32_t length; uint8_t *internalArray; } Clay__CharArray; - Clay__CharArray Clay__CharArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { return CLAY__INIT(Clay__CharArray){.capacity = capacity, .length = 0, .internalArray = (uint8_t *)Clay__Array_Allocate_Arena(capacity, sizeof(uint8_t), CLAY__ALIGNMENT(uint8_t), arena)}; } @@ -1271,43 +1370,7 @@ Clay_String Clay__WriteStringToCharBuffer(Clay__CharArray *buffer, Clay_String s return CLAY__INIT(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__INIT(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__INIT(Clay__PointerInfo) { .position = {-1, -1} }; +Clay_PointerInfo Clay__pointerInfo = CLAY__INIT(Clay_PointerInfo) { .position = {-1, -1} }; Clay_Dimensions Clay__layoutDimensions = CLAY__INIT(Clay_Dimensions){}; Clay_ElementId Clay__dynamicElementIndexBaseHash = CLAY__INIT(Clay_ElementId) { .id = 128476991, .stringId = { .length = 8, .chars = "Auto ID" } }; uint32_t Clay__dynamicElementIndex = 0; @@ -1321,14 +1384,16 @@ Clay_Arena Clay__internalArena; // Layout Elements / Render Commands Clay_LayoutElementArray Clay__layoutElements; Clay_RenderCommandArray Clay__renderCommands; -Clay__LayoutElementPointerArray Clay__openLayoutElementStack; +Clay__int32_tArray Clay__openLayoutElementStack; Clay__int32_tArray Clay__layoutElementChildren; Clay__int32_tArray Clay__layoutElementChildrenBuffer; Clay__TextElementDataArray Clay__textElementData; Clay__LayoutElementPointerArray Clay__imageElementPointers; -Clay__LayoutElementPointerArray Clay__layoutElementReusableBuffer; +Clay__int32_tArray Clay__reusableElementIndexBuffer; // Configs Clay__LayoutConfigArray Clay__layoutConfigs; +Clay__ElementConfigArray Clay__elementConfigBuffer; +Clay__ElementConfigArray Clay__elementConfigs; Clay__RectangleElementConfigArray Clay__rectangleElementConfigs; Clay__TextElementConfigArray Clay__textElementConfigs; Clay__ImageElementConfigArray Clay__imageElementConfigs; @@ -1338,6 +1403,7 @@ Clay__CustomElementConfigArray Clay__customElementConfigs; Clay__BorderElementConfigArray Clay__borderElementConfigs; // Misc Data Structures Clay__StringArray Clay__layoutElementIdStrings; +Clay__StringArray Clay__wrappedTextLines; Clay__LayoutElementTreeNodeArray Clay__layoutElementTreeNodeArray1; Clay__LayoutElementTreeRootArray Clay__layoutElementTreeRoots; Clay__LayoutElementHashMapItemArray Clay__layoutElementsHashMapInternal; @@ -1345,9 +1411,9 @@ Clay__int32_tArray Clay__layoutElementsHashMap; Clay__MeasureTextCacheItemArray Clay__measureTextHashMapInternal; Clay__int32_tArray Clay__measureTextHashMapInternalFreeList; Clay__int32_tArray Clay__measureTextHashMap; +Clay__MeasuredWordArray Clay__measuredWords; Clay__int32_tArray Clay__openClipElementStack; Clay__ElementIdArray Clay__pointerOverIds; -Clay__int32_tArray Clay__reusableElementIndexBuffer; Clay__ScrollContainerDataInternalArray Clay__scrollContainerDatas; Clay__BoolArray Clay__treeNodeVisited; Clay__CharArray Clay__dynamicStringData; @@ -1359,7 +1425,35 @@ Clay__DebugElementDataArray Clay__debugElementData; Clay_Dimensions (*Clay__MeasureText)(Clay_String *text, Clay_TextElementConfig *config); #endif -Clay_String LAST_HASH; +Clay_LayoutElement* Clay__GetOpenLayoutElement() { + return Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&Clay__openLayoutElementStack, Clay__openLayoutElementStack.length - 1)); +} + +bool Clay__ElementHasConfig(Clay_LayoutElement *element, Clay__ElementConfigType type) { + return (element->configsEnabled & type); +} + +Clay_ElementConfigUnion Clay__FindElementConfigWithType(Clay_LayoutElement *element, Clay__ElementConfigType type) { + for (int i = 0; i < element->elementConfigs.length; i++) { + Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&element->elementConfigs, i); + if (config->type == type) { + return config->config; + } + } + return CLAY__INIT(Clay_ElementConfigUnion) { NULL }; +} + +Clay_ElementId Clay__HashNumber(const uint32_t offset, const uint32_t seed) { + uint32_t hash = seed; + hash += (offset + 48); + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = seed, .stringId = CLAY__STRING_DEFAULT }; // Reserve the hash result of zero as "null id" +} Clay_ElementId Clay__HashString(Clay_String key, const uint32_t offset, const uint32_t seed) { uint32_t hash = 0; @@ -1381,17 +1475,9 @@ Clay_ElementId Clay__HashString(Clay_String key, const uint32_t offset, const ui base ^= (base >> 11); hash += (hash << 15); base += (base << 15); - #ifdef CLAY_DEBUG - LAST_HASH = key; - LAST_HASH.length = (int)offset; - #endif return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as "null id" } -uint32_t Clay__GetOpenLayoutElementId(void) { - return Clay__openLayoutElement ? Clay__openLayoutElement->id : 0; -} - Clay_ElementId Clay__Rehash(Clay_ElementId elementId, uint32_t number) { uint32_t id = elementId.baseId; id += number; @@ -1416,10 +1502,6 @@ uint32_t Clay__RehashWithNumber(uint32_t id, uint32_t number) { } uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *config) { - union { - float fontSize; - uint32_t bits; - } fontSizeBits = { .fontSize = (float)config->fontSize }; uint32_t hash = 0; uint64_t pointerAsNumber = (uint64_t)text->chars; @@ -1431,11 +1513,8 @@ uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *con hash += (hash << 10); hash ^= (hash >> 6); - hash += config->fontId; - hash += (hash << 10); - hash ^= (hash >> 6); - - hash += fontSizeBits.bits; + uint64_t configPointerAsNumber = (uint64_t)config; + hash += configPointerAsNumber; hash += (hash << 10); hash ^= (hash >> 6); @@ -1445,7 +1524,7 @@ uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *con return hash + 1; // Reserve the hash result of zero as "null id" } -Clay_Dimensions Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) { +Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) { uint32_t id = Clay__HashTextWithConfig(text, config); uint32_t hashBucket = id % Clay__measureTextHashMap.capacity; int32_t elementIndexPrevious = 0; @@ -1454,7 +1533,7 @@ Clay_Dimensions Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfi Clay__MeasureTextCacheItem *hashEntry = Clay__MeasureTextCacheItemArray_Get(&Clay__measureTextHashMapInternal, elementIndex); if (hashEntry->id == id) { hashEntry->generation = Clay__generation; - return hashEntry->dimensions; + return hashEntry; } // This element hasn't been seen in a few frames, delete the hash map item if (Clay__generation - hashEntry->generation > 2) { @@ -1473,19 +1552,61 @@ Clay_Dimensions Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfi elementIndex = hashEntry->nextIndex; } } - Clay_Dimensions measured = Clay__MeasureText(text, config); + uint32_t newItemIndex = 0; + Clay__MeasureTextCacheItem newCacheItem = CLAY__INIT(Clay__MeasureTextCacheItem) { .measuredWords = CLAY__INIT(Clay__MeasuredWordArraySlice) { .length = 0, .internalArray = &Clay__measuredWords.internalArray[Clay__measuredWords.length] }, .id = id, .generation = Clay__generation }; + Clay__MeasureTextCacheItem *measured = NULL; if (Clay__measureTextHashMapInternalFreeList.length > 0) { newItemIndex = Clay__int32_tArray_Get(&Clay__measureTextHashMapInternalFreeList, Clay__measureTextHashMapInternalFreeList.length - 1); Clay__measureTextHashMapInternalFreeList.length--; - Clay__MeasureTextCacheItemArray_Set(&Clay__measureTextHashMapInternal, newItemIndex, CLAY__INIT(Clay__MeasureTextCacheItem) { .dimensions = measured, .id = id, .generation = Clay__generation }); + Clay__MeasureTextCacheItemArray_Set(&Clay__measureTextHashMapInternal, newItemIndex, newCacheItem); + measured = Clay__MeasureTextCacheItemArray_Get(&Clay__measureTextHashMapInternal, newItemIndex); } else { if (Clay__measureTextHashMapInternal.length == Clay__measureTextHashMapInternal.capacity) { - return measured; + return NULL; } - Clay__MeasureTextCacheItemArray_Add(&Clay__measureTextHashMapInternal, CLAY__INIT(Clay__MeasureTextCacheItem) { .dimensions = measured, .id = id, .generation = Clay__generation }); + measured = Clay__MeasureTextCacheItemArray_Add(&Clay__measureTextHashMapInternal, newCacheItem); newItemIndex = Clay__measureTextHashMapInternal.length - 1; } + + uint32_t start = 0; + uint32_t end = 0; + float measuredWidth = 0; + float measuredHeight = 0; + float spaceWidth = Clay__MeasureText(&CLAY__SPACECHAR, config).width; + while (end < text->length) { + char current = text->chars[end]; + if (current == ' ' || current == '\n') { + uint32_t length = end - start; + Clay_String word = CLAY__INIT(Clay_String) { .length = (int)length, .chars = &text->chars[start] }; + Clay_Dimensions dimensions = Clay__MeasureText(&word, config); + if (current == ' ') { + dimensions.width += spaceWidth; + Clay__MeasuredWordArray_Add(&Clay__measuredWords, CLAY__INIT(Clay__MeasuredWord) { .word = word, .startOffset = start, .length = length + 1, .width = dimensions.width }); + } + if (current == '\n') { + Clay__MeasuredWordArray_Add(&Clay__measuredWords, CLAY__INIT(Clay__MeasuredWord) { .word = word, .startOffset = start, .length = length, .width = dimensions.width }); + Clay__MeasuredWordArray_Add(&Clay__measuredWords, CLAY__INIT(Clay__MeasuredWord) { .word = CLAY__INIT(Clay_String) { .length = 0, .chars = &text->chars[end] }, .startOffset = end + 1, .length = 0, .width = 0 }); + measured->measuredWords.length++; + } + measuredWidth += dimensions.width; + measuredHeight = dimensions.height; + measured->measuredWords.length++; + start = end + 1; + } + end++; + } + if (end - start > 0) { + Clay_String lastWord = CLAY__INIT(Clay_String) { .length = (int)(end - start), .chars = &text->chars[start] }; + Clay_Dimensions dimensions = Clay__MeasureText(&lastWord, config); + Clay__MeasuredWordArray_Add(&Clay__measuredWords, CLAY__INIT(Clay__MeasuredWord) { .word = lastWord, .startOffset = start, .length = end - start, .width = dimensions.width }); + measuredWidth += dimensions.width; + measuredHeight = dimensions.height; + measured->measuredWords.length++; + } + measured->unwrappedDimensions.width = measuredWidth; + measured->unwrappedDimensions.height = measuredHeight; + if (elementIndexPrevious != 0) { Clay__MeasureTextCacheItemArray_Get(&Clay__measureTextHashMapInternal, elementIndexPrevious)->nextIndex = newItemIndex; } else { @@ -1547,191 +1668,219 @@ Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) { return CLAY__NULL; } -Clay_LayoutElement *Clay__OpenElementWithParent(Clay_ElementId elementId, Clay__LayoutElementType commandType, Clay_LayoutConfig* layoutConfig, Clay_ElementConfigUnion elementConfig) { - Clay_LayoutElement layoutElement = CLAY__INIT(Clay_LayoutElement) { - #ifdef CLAY_DEBUG - .name = elementId.stringId, - #endif - .children = CLAY__INIT(Clay__LayoutElementChildren) { .length = 0 }, - .minDimensions = CLAY__INIT(Clay_Dimensions) { (float)layoutConfig->padding.x * 2, (float)layoutConfig->padding.y * 2 }, - .layoutConfig = layoutConfig, - .elementConfig = elementConfig, - .id = elementId.id, - .elementType = commandType, - }; - - Clay__openLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, layoutElement); - Clay__LayoutElementPointerArray_Add(&Clay__openLayoutElementStack, Clay__openLayoutElement); - Clay__AddHashMapItem(elementId, Clay__openLayoutElement); +void Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) { + Clay_LayoutElement *parentElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&Clay__openLayoutElementStack, Clay__openLayoutElementStack.length - 2)); + Clay_ElementId elementId = Clay__HashNumber(parentElement->children.length, parentElement->id); + openLayoutElement->id = elementId.id; + Clay__AddHashMapItem(elementId, openLayoutElement); Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); - return Clay__openLayoutElement; + #ifdef CLAY_DEBUG + openLayoutElement->name = elementId.stringId; + #endif } -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__int32_tArray_Add(&Clay__layoutElementChildrenBuffer, (int32_t)Clay__layoutElements.length - 1); - return element; -} +void Clay__ElementPostConfiguration() { + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + // ID + if (openLayoutElement->id == 0) { + Clay__GenerateIdForAnonymousElement(openLayoutElement); + } + // Layout Config + if (!openLayoutElement->layoutConfig) { + openLayoutElement->layoutConfig = &CLAY_LAYOUT_DEFAULT; + } -void Clay__OpenContainerElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig) { - Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER, layoutConfig, CLAY__INIT(Clay_ElementConfigUnion) { CLAY__NULL }); -} - -void Clay__OpenRectangleElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_RectangleElementConfig *rectangleConfig) { - Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE, layoutConfig, CLAY__INIT(Clay_ElementConfigUnion) { .rectangleElementConfig = rectangleConfig }); -} - -void Clay__OpenImageElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_ImageElementConfig *imageConfig) { - Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_IMAGE, layoutConfig, CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = imageConfig }); - Clay__LayoutElementPointerArray_Add(&Clay__imageElementPointers, Clay__openLayoutElement); -} - -void Clay__OpenBorderElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_BorderElementConfig *borderConfig) { - Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER, layoutConfig, CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = borderConfig }); -} - -void Clay__OpenCustomElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customConfig) { - Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM, layoutConfig, CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = customConfig }); -} - -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__INIT(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 (elementId.id == mapping->elementId) { - scrollOffset = mapping; - scrollOffset->layoutElement = scrollElement; - scrollOffset->openThisFrame = true; + // Loop through element configs and handle special cases + openLayoutElement->elementConfigs.internalArray = &Clay__elementConfigs.internalArray[Clay__elementConfigs.length]; + for (int elementConfigIndex = 0; elementConfigIndex < openLayoutElement->elementConfigs.length; elementConfigIndex++) { + Clay_ElementConfig *config = Clay__ElementConfigArray_Add(&Clay__elementConfigs, *Clay__ElementConfigArray_Get(&Clay__elementConfigBuffer, Clay__elementConfigBuffer.length - openLayoutElement->elementConfigs.length + elementConfigIndex)); + openLayoutElement->configsEnabled |= config->type; + switch (config->type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: break; + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: break; + case CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER: { + Clay_FloatingElementConfig *floatingConfig = config->config.floatingElementConfig; + // This looks dodgy but because of the auto generated root element the depth of the tree will always be at least 2 here + Clay_LayoutElement *hierarchicalParent = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&Clay__openLayoutElementStack, Clay__openLayoutElementStack.length - 2)); + if (!hierarchicalParent) { + break; + } + int clipElementId = 0; + if (floatingConfig->parentId == 0) { + // If no parent id was specified, attach to the elements direct hierarchical parent + Clay_FloatingElementConfig newConfig = *floatingConfig; + newConfig.parentId = hierarchicalParent->id; + floatingConfig = Clay__FloatingElementConfigArray_Add(&Clay__floatingElementConfigs, newConfig); + config->config.floatingElementConfig = floatingConfig; + if (Clay__openClipElementStack.length > 0) { + clipElementId = Clay__int32_tArray_Get(&Clay__openClipElementStack, (int)Clay__openClipElementStack.length - 1); + } + } else { + Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig->parentId); + if (!parentItem) { + Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Clay Warning: Couldn't find parent container to attach floating container to.") }); + } + } + Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { + .layoutElementIndex = (uint32_t)Clay__int32_tArray_Get(&Clay__openLayoutElementStack, Clay__openLayoutElementStack.length - 1), + .parentId = floatingConfig->parentId, + .clipElementId = (uint32_t)clipElementId, + .zIndex = floatingConfig->zIndex, + }); + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: { + Clay__int32_tArray_Add(&Clay__openClipElementStack, (int)openLayoutElement->id); + // Retrieve or create cached data to track scroll position across frames + Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL; + for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); + if (openLayoutElement->id == mapping->elementId) { + scrollOffset = mapping; + scrollOffset->layoutElement = openLayoutElement; + scrollOffset->openThisFrame = true; + } + } + if (!scrollOffset) { + Clay__ScrollContainerDataInternalArray_Add(&Clay__scrollContainerDatas, CLAY__INIT(Clay__ScrollContainerDataInternal){.layoutElement = openLayoutElement, .scrollOrigin = {-1,-1}, .elementId = openLayoutElement->id, .openThisFrame = true}); + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: break; + case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { + Clay__LayoutElementPointerArray_Add(&Clay__imageElementPointers, openLayoutElement); + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: break; } } - if (!scrollOffset) { - Clay__ScrollContainerDataInternalArray_Add(&Clay__scrollContainerDatas, CLAY__INIT(Clay__ScrollContainerDataInternal){.layoutElement = scrollElement, .scrollOrigin = {-1,-1}, .elementId = elementId.id, .openThisFrame = true}); - } -} - -void Clay__OpenFloatingElement(Clay_ElementId id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingConfig) { - Clay_LayoutElement *parent = Clay__openLayoutElement; - uint32_t originalParentId = floatingConfig->parentId; - if (floatingConfig->parentId == 0) { - Clay_FloatingElementConfig newConfig = *floatingConfig; - newConfig.parentId = Clay__openLayoutElement->id; - floatingConfig = Clay__FloatingElementConfigArray_Add(&Clay__floatingElementConfigs, newConfig); - } else { - Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig->parentId); - if (!parentItem) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(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__INIT(Clay_ElementConfigUnion) { .floatingElementConfig = floatingConfig }); - Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { - .layoutElementIndex = Clay__layoutElements.length - 1, - .parentId = parent->id, - .clipElementId = (uint32_t)(originalParentId == 0 ? (Clay__openClipElementStack.length > 0 ? Clay__int32_tArray_Get(&Clay__openClipElementStack, (int)Clay__openClipElementStack.length - 1) : 0) : 0), - .zIndex = floatingConfig->zIndex, - }); -} - -void Clay__AttachContainerChildren() { - Clay_LayoutConfig *layoutConfig = Clay__openLayoutElement->layoutConfig; - Clay__openLayoutElement->children.elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length]; - - if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { - for (int i = 0; i < 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); - // Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents - if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->horizontal) { - Clay__openLayoutElement->minDimensions.width += child->minDimensions.width; - } - 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); - } - 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; - Clay__openLayoutElement->minDimensions.width += childGap; - } - else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) { - for (int i = 0; i < 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); - // Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents - if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->vertical) { - Clay__openLayoutElement->minDimensions.height += child->minDimensions.height; - } - 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); - } - 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; - Clay__openLayoutElement->minDimensions.height += childGap; - } - - Clay__layoutElementChildrenBuffer.length -= Clay__openLayoutElement->children.length; + Clay__elementConfigBuffer.length -= openLayoutElement->elementConfigs.length; } void Clay__CloseElement() { - Clay_LayoutConfig *layoutConfig = Clay__openLayoutElement->layoutConfig; + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + Clay_LayoutConfig *layoutConfig = openLayoutElement->layoutConfig; + bool elementHasScrollHorizontal = false; + bool elementHasScrollVertical = false; + if (Clay__ElementHasConfig(openLayoutElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(openLayoutElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + elementHasScrollHorizontal = scrollConfig->horizontal; + elementHasScrollVertical = scrollConfig->vertical; + Clay__openClipElementStack.length--; + } + // Attach children to the current open element + openLayoutElement->children.elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length]; + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + openLayoutElement->dimensions.width = layoutConfig->padding.x * 2; + for (int i = 0; i < openLayoutElement->children.length; i++) { + int32_t childIndex = Clay__int32_tArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - openLayoutElement->children.length + i); + Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&Clay__layoutElements, childIndex); + openLayoutElement->dimensions.width += child->dimensions.width; + openLayoutElement->dimensions.height = CLAY__MAX(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 + if (!elementHasScrollHorizontal) { + openLayoutElement->minDimensions.width += child->minDimensions.width; + } + if (!elementHasScrollVertical) { + openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + layoutConfig->padding.y * 2); + } + Clay__int32_tArray_Add(&Clay__layoutElementChildren, childIndex); + } + float childGap = (float)(CLAY__MAX(openLayoutElement->children.length - 1, 0) * layoutConfig->childGap); + openLayoutElement->dimensions.width += childGap; // TODO this is technically a bug with childgap and scroll containers + openLayoutElement->minDimensions.width += childGap; + } + else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) { + openLayoutElement->dimensions.height = layoutConfig->padding.y * 2; + for (int i = 0; i < openLayoutElement->children.length; i++) { + int32_t childIndex = Clay__int32_tArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - openLayoutElement->children.length + i); + Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&Clay__layoutElements, childIndex); + openLayoutElement->dimensions.height += child->dimensions.height; + openLayoutElement->dimensions.width = CLAY__MAX(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 + if (!elementHasScrollVertical) { + openLayoutElement->minDimensions.height += child->minDimensions.height; + } + if (!elementHasScrollHorizontal) { + openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + layoutConfig->padding.x * 2); + } + Clay__int32_tArray_Add(&Clay__layoutElementChildren, childIndex); + } + float childGap = (float)(CLAY__MAX(openLayoutElement->children.length - 1, 0) * layoutConfig->childGap); + openLayoutElement->dimensions.height += childGap; // TODO this is technically a bug with childgap and scroll containers + openLayoutElement->minDimensions.height += childGap; + } + + Clay__layoutElementChildrenBuffer.length -= openLayoutElement->children.length; + + // Clamp element min and max width to the values configured in the layout if (layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) { if (layoutConfig->sizing.width.sizeMinMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier layoutConfig->sizing.width.sizeMinMax.max = CLAY__MAXFLOAT; } - Clay__openLayoutElement->dimensions.width += (float)layoutConfig->padding.x * 2; - Clay__openLayoutElement->dimensions.width = CLAY__MAX(CLAY__MIN(layoutConfig->sizing.width.sizeMinMax.max, Clay__openLayoutElement->dimensions.width), layoutConfig->sizing.width.sizeMinMax.min); - Clay__openLayoutElement->minDimensions.width += (float)layoutConfig->padding.x * 2; - Clay__openLayoutElement->minDimensions.width = CLAY__MAX(CLAY__MIN(layoutConfig->sizing.width.sizeMinMax.max, Clay__openLayoutElement->minDimensions.width), layoutConfig->sizing.width.sizeMinMax.min); + openLayoutElement->dimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.width, layoutConfig->sizing.width.sizeMinMax.min), layoutConfig->sizing.width.sizeMinMax.max); + openLayoutElement->minDimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.width, layoutConfig->sizing.width.sizeMinMax.min), layoutConfig->sizing.width.sizeMinMax.max); + } else { + openLayoutElement->dimensions.width = 0; } + + // Clamp element min and max height to the values configured in the layout if (layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) { if (layoutConfig->sizing.height.sizeMinMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier layoutConfig->sizing.height.sizeMinMax.max = CLAY__MAXFLOAT; } - Clay__openLayoutElement->dimensions.height += (float)layoutConfig->padding.y * 2; - Clay__openLayoutElement->dimensions.height = CLAY__MAX(CLAY__MIN(layoutConfig->sizing.height.sizeMinMax.max, Clay__openLayoutElement->dimensions.height), layoutConfig->sizing.height.sizeMinMax.min); - Clay__openLayoutElement->minDimensions.height += (float)layoutConfig->padding.x * 2; - Clay__openLayoutElement->minDimensions.height = CLAY__MAX(CLAY__MIN(layoutConfig->sizing.height.sizeMinMax.max, Clay__openLayoutElement->minDimensions.height), layoutConfig->sizing.height.sizeMinMax.min); + openLayoutElement->dimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.height, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); + openLayoutElement->minDimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.height, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); + } else { + openLayoutElement->dimensions.height = 0; } - Clay__LayoutElementPointerArray_RemoveSwapback(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1); - Clay__openLayoutElement = *Clay__LayoutElementPointerArray_Get(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1); + bool elementIsFloating = Clay__ElementHasConfig(openLayoutElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER); + + // Close the currently open element + int32_t closingElementIndex = Clay__int32_tArray_RemoveSwapback(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1); + openLayoutElement = Clay__GetOpenLayoutElement(); + + if (!elementIsFloating && Clay__openLayoutElementStack.length > 1) { + openLayoutElement->children.length++; + Clay__int32_tArray_Add(&Clay__layoutElementChildrenBuffer, closingElementIndex); + } } -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__INIT(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__INIT(Clay_Dimensions) { .width = textMeasured.height, .height = textMeasured.height }; // TODO not sure this is the best way to decide min width for text - Clay__TextElementDataArray_Add(&Clay__textElementData, CLAY__INIT(Clay__TextElementData) { .elementIndex = Clay__layoutElements.length - 1, .preferredDimensions = textMeasured }); - Clay__CloseElement(); +void Clay__OpenElement() { + Clay_LayoutElement layoutElement = CLAY__INIT(Clay_LayoutElement) {}; + Clay_LayoutElementArray_Add(&Clay__layoutElements, layoutElement); + Clay__int32_tArray_Add(&Clay__openLayoutElementStack, Clay__layoutElements.length - 1); } -void Clay__CloseElementWithChildren() { - Clay__AttachContainerChildren(); - Clay__CloseElement(); -} +void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig) { + Clay_LayoutElement *parentElement = Clay__GetOpenLayoutElement(); + parentElement->children.length++; -void Clay__CloseScrollElement() { - Clay__openClipElementStack.length--; - Clay__CloseElementWithChildren(); -} - -void Clay__CloseFloatingElement() { - Clay__AttachContainerChildren(); - Clay__CloseElement(); + Clay__OpenElement(); + Clay_LayoutElement * openLayoutElement = Clay__GetOpenLayoutElement(); + Clay__int32_tArray_Add(&Clay__layoutElementChildrenBuffer, Clay__layoutElements.length - 1); + Clay__MeasureTextCacheItem *textMeasured = Clay__MeasureTextCached(&text, textConfig); + Clay_ElementId elementId = Clay__HashString(CLAY_STRING("Text"), parentElement->children.length, parentElement->id); + openLayoutElement->id = elementId.id; + #ifdef CLAY_DEBUG + openLayoutElement->name = CLAY_STRING("Text"); + #endif + Clay__AddHashMapItem(elementId, openLayoutElement); + Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); + Clay_Dimensions textDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? textConfig->lineHeight : textMeasured->unwrappedDimensions.height }; + openLayoutElement->dimensions = textDimensions; + openLayoutElement->minDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->unwrappedDimensions.height, .height = textDimensions.height }; // TODO not sure this is the best way to decide min width for text + openLayoutElement->textElementData = Clay__TextElementDataArray_Add(&Clay__textElementData, CLAY__INIT(Clay__TextElementData) { .text = text, .preferredDimensions = textMeasured->unwrappedDimensions, .elementIndex = Clay__layoutElements.length - 1 }); + openLayoutElement->elementConfigs = CLAY__INIT(Clay__ElementConfigArraySlice) { + .length = 1, + .internalArray = Clay__ElementConfigArray_Add(&Clay__elementConfigs, CLAY__INIT(Clay_ElementConfig) { .type = CLAY__ELEMENT_CONFIG_TYPE_TEXT, .config = { .textElementConfig = textConfig }}) + }; + openLayoutElement->configsEnabled |= CLAY__ELEMENT_CONFIG_TYPE_TEXT; + openLayoutElement->layoutConfig = &CLAY_LAYOUT_DEFAULT; + // Close the currently open element + Clay__int32_tArray_RemoveSwapback(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1); } void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { @@ -1743,6 +1892,8 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { Clay_warnings = Clay__WarningArray_Allocate_Arena(100, arena); Clay__layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__elementConfigBuffer = Clay__ElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__elementConfigs = Clay__ElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__rectangleElementConfigs = Clay__RectangleElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); @@ -1752,13 +1903,13 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { Clay__borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__layoutElementIdStrings = Clay__StringArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__wrappedTextLines = 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__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__openLayoutElementStack = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__openLayoutElementStack = Clay__int32_tArray_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); // 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 @@ -1768,46 +1919,19 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { } void Clay__InitializePersistentMemory(Clay_Arena *arena) { + // Persistent memory - initialized once and not reset Clay__scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(10, arena); Clay__layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, 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__measureTextHashMapInternalFreeList = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__measureTextHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__measuredWords = Clay__MeasuredWordArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT * 2, 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; } -Clay_String Clay__IntToString(int integer) { - if (integer == 0) { - return CLAY__INIT(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__INIT(Clay_String) { .length = length, .chars = chars }; -} typedef enum { @@ -1816,21 +1940,18 @@ typedef enum CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER, } Clay__SizeDistributionType; -// Because of the max and min sizing properties, we can't predict ahead of time how (or if) all the excess width -// will actually be distributed. So we keep looping until either all the excess width is distributed or -// we have exhausted all our containers that can change size along this axis -float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay__LayoutElementPointerArray resizableContainerBuffer, Clay__SizeDistributionType distributionType) { - Clay__LayoutElementPointerArray remainingElements = Clay__layoutElementReusableBuffer; +float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay__int32_tArray resizableContainerBuffer, Clay__SizeDistributionType distributionType) { + Clay__int32_tArray remainingElements = Clay__openClipElementStack; remainingElements.length = 0; for (int i = 0; i < resizableContainerBuffer.length; ++i) { - Clay__LayoutElementPointerArray_Add(&remainingElements, *Clay__LayoutElementPointerArray_Get(&resizableContainerBuffer, i)); + Clay__int32_tArray_Add(&remainingElements, Clay__int32_tArray_Get(&resizableContainerBuffer, i)); } while (sizeToDistribute != 0 && remainingElements.length > 0) { float dividedSize = sizeToDistribute / (float)remainingElements.length; for (int childOffset = 0; childOffset < remainingElements.length; childOffset++) { - Clay_LayoutElement *childElement = *Clay__LayoutElementPointerArray_Get(&remainingElements, childOffset); + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&remainingElements, childOffset)); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height; @@ -1840,19 +1961,28 @@ float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay canDistribute = false; } // Currently, we don't support squishing aspect ratio images on their Y axis as it would break ratio - else if (!xAxis && childElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_IMAGE) { + else if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) { canDistribute = false; } else { switch (distributionType) { case CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER: break; case CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER: if (childSizing.type != CLAY__SIZING_TYPE_GROW) canDistribute = false; break; - case CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER: if ((childElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || (xAxis && !childElement->elementConfig.scrollElementConfig->horizontal) || (!xAxis && !childElement->elementConfig.scrollElementConfig->vertical))) canDistribute = false; break; + case CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER: { + if (Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + if ((xAxis && !scrollConfig->horizontal) || (!xAxis && !scrollConfig->vertical)) { + Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); + childOffset--; + continue; + } + } + } } } if (!canDistribute) { - Clay__LayoutElementPointerArray_RemoveSwapback(&remainingElements, childOffset); + Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); childOffset--; continue; } @@ -1861,7 +1991,7 @@ float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay *childSize = CLAY__MAX(CLAY__MAX(CLAY__MIN(childSizing.sizeMinMax.max, *childSize + dividedSize), childSizing.sizeMinMax.min), childMinSize); float diff = *childSize - oldChildSize; if (diff > -0.01 && diff < 0.01) { - Clay__LayoutElementPointerArray_RemoveSwapback(&remainingElements, childOffset); + Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); childOffset--; continue; } @@ -1873,7 +2003,7 @@ float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay void Clay__SizeContainersAlongAxis(bool xAxis) { Clay__int32_tArray bfsBuffer = Clay__layoutElementChildrenBuffer; - Clay__LayoutElementPointerArray resizableContainerBuffer = Clay__openLayoutElementStack; + Clay__int32_tArray resizableContainerBuffer = Clay__openLayoutElementStack; for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) { bfsBuffer.length = 0; Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); @@ -1881,8 +2011,9 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { Clay__int32_tArray_Add(&bfsBuffer, (int32_t)root->layoutElementIndex); // Size floating containers to their parents - if (rootElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER) { - Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(rootElement->elementConfig.floatingElementConfig->parentId); + if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER)) { + Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER).floatingElementConfig; + Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId); if (parentItem) { Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement; if (rootElement->layoutConfig->sizing.width.type == CLAY__SIZING_TYPE_GROW) { @@ -1915,12 +2046,12 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { 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) { + if (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && childElement->children.length > 0) { Clay__int32_tArray_Add(&bfsBuffer, childElementIndex); } - if (childSizing.type != CLAY__SIZING_TYPE_PERCENT && childSizing.type != CLAY__SIZING_TYPE_FIXED && (childElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT || (childElement->elementConfig.textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS))) { - Clay__LayoutElementPointerArray_Add(&resizableContainerBuffer, childElement); + if (childSizing.type != CLAY__SIZING_TYPE_PERCENT && childSizing.type != CLAY__SIZING_TYPE_FIXED && (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS))) { + Clay__int32_tArray_Add(&resizableContainerBuffer, childElementIndex); } if (sizingAlongAxis) { @@ -1963,8 +2094,9 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { // If the content is too large, compress the children as much as possible if (sizeToDistribute < 0) { // If the parent can scroll in the axis direction in this direction, don't compress children, just leave them alone - if (parent->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) { - if (((xAxis && parent->elementConfig.scrollElementConfig->horizontal) || (!xAxis && parent->elementConfig.scrollElementConfig->vertical))) { + if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + Clay_ScrollElementConfig *scrollElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + if (((xAxis && scrollElementConfig->horizontal) || (!xAxis && scrollElementConfig->vertical))) { continue; } } @@ -1979,14 +2111,14 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { } else if (sizeToDistribute > 0 && growContainerCount > 0) { float targetSize = (sizeToDistribute + growContainerContentSize) / growContainerCount; for (int childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) { - Clay_LayoutElement *childElement = *Clay__LayoutElementPointerArray_Get(&resizableContainerBuffer, childOffset); + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&resizableContainerBuffer, childOffset)); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; if (childSizing.type == CLAY__SIZING_TYPE_GROW) { float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; float *minSize = xAxis ? &childElement->minDimensions.width : &childElement->minDimensions.height; if (targetSize < *minSize) { growContainerContentSize -= *minSize; - Clay__LayoutElementPointerArray_RemoveSwapback(&resizableContainerBuffer, childOffset); + Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childOffset); growContainerCount--; targetSize = (sizeToDistribute + growContainerContentSize) / growContainerCount; childOffset = -1; @@ -1999,18 +2131,21 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { // Sizing along the non layout axis ("off axis") } else { for (int childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) { - Clay_LayoutElement *childElement = *Clay__LayoutElementPointerArray_Get(&resizableContainerBuffer, childOffset); + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&resizableContainerBuffer, childOffset)); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; - if (!xAxis && childElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_IMAGE) { + if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) { continue; // Currently we don't support resizing aspect ratio images on the Y axis because it would break the ratio } // If we're laying out the children of a scroll panel, grow containers expand to the height of the inner content, not the outer container float maxSize = parentSize - parentPadding * 2; - if (parent->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER && ((xAxis && parent->elementConfig.scrollElementConfig->horizontal) || (!xAxis && parent->elementConfig.scrollElementConfig->vertical))) { - maxSize = CLAY__MAX(maxSize, innerContentSize); + if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + Clay_ScrollElementConfig *scrollElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + if (((xAxis && scrollElementConfig->horizontal) || (!xAxis && scrollElementConfig->vertical))) { + maxSize = CLAY__MAX(maxSize, innerContentSize); + } } if (childSizing.type == CLAY__SIZING_TYPE_FIT) { *childSize = CLAY__MAX(childSizing.sizeMinMax.min, CLAY__MIN(*childSize, maxSize)); @@ -2023,119 +2158,54 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { } } -Clay_BoundingBox Clay__BoundingBoxWithRoundedValues(float x, float y, float width, float height) { - return CLAY__INIT(Clay_BoundingBox) { - (float)(int32_t)(x + (x > 0 ? 0.5f : -0.5f)), - (float)(int32_t)(y + (y > 0 ? 0.5f : -0.5f)), - (float)(int32_t)(width + (width > 0 ? 0.5f : -0.5f)), - (float)(int32_t)(height + (height > 0 ? 0.5f : -0.5f)) - }; -} - void Clay__CalculateFinalLayout() { // Calculate sizing along the X axis Clay__SizeContainersAlongAxis(true); // Wrap text - uint32_t originalTextLayoutElementDataLength = Clay__textElementData.length; - for (int i = 0; i < originalTextLayoutElementDataLength; ++i) { - Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&Clay__textElementData, i); + for (int textElementIndex = 0; textElementIndex < Clay__textElementData.length; ++textElementIndex) { + Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&Clay__textElementData, textElementIndex); + textElementData->wrappedLines = CLAY__INIT(Clay__StringArraySlice) { .length = 0, .internalArray = &Clay__wrappedTextLines.internalArray[Clay__wrappedTextLines.length] }; 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; - // Clone the style config to prevent pollution of other elements that share this config - containerElement->layoutConfig = Clay__LayoutConfigArray_Add(&Clay__layoutConfigs, *containerElement->layoutConfig); - containerElement->layoutConfig->layoutDirection = CLAY_TOP_TO_BOTTOM; - containerElement->children = CLAY__INIT(Clay__LayoutElementChildren) { // Note: this overwrites the text property - .elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length], - .length = 0, - }; - // Short circuit all wrap calculations if wrap mode is none - if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE || (containerElement->dimensions.width == textElementData->preferredDimensions.width && textConfig->wrapMode != CLAY_TEXT_WRAP_NEWLINES)) { - float lineHeight = textConfig->lineHeight != 0 ? textConfig->lineHeight : textElementData->preferredDimensions.height; - Clay_LayoutElementArray_Add(&Clay__layoutElements, CLAY__INIT(Clay_LayoutElement) { - .text = text, - .dimensions = { textElementData->preferredDimensions.width, lineHeight }, - .minDimensions = textElementData->preferredDimensions, - .layoutConfig = CLAY_LAYOUT(.sizing = { .height = CLAY_SIZING_FIXED(lineHeight) }), - .elementConfig = { .textElementConfig = containerElement->elementConfig.textElementConfig }, - .id = Clay__RehashWithNumber(containerElement->id, containerElement->children.length), - .elementType = CLAY__LAYOUT_ELEMENT_TYPE_TEXT, - }); - containerElement->children.length++; - Clay__int32_tArray_Add(&Clay__layoutElementChildren, (int32_t)Clay__layoutElements.length - 1); + Clay_TextElementConfig *textConfig = Clay__FindElementConfigWithType(containerElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig; + Clay__MeasureTextCacheItem *measureTextCacheItem = Clay__MeasureTextCached(&textElementData->text, textConfig); + float lineWidth = 0; + float lineHeight = textConfig->lineHeight > 0 ? textConfig->lineHeight : textElementData->preferredDimensions.height; + uint32_t lineLengthChars = 0; + uint32_t lineStartOffset = 0; + if (textElementData->preferredDimensions.width <= containerElement->dimensions.width) { + Clay__StringArray_Add(&Clay__wrappedTextLines, textElementData->text); + textElementData->wrappedLines.length++; continue; } - containerElement->dimensions.height = 0; - int lineStartIndex = 0; - int wordStartIndex = 0; - int wordEndIndex = 0; - Clay_Dimensions lineDimensions = CLAY__INIT(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' && textConfig->wrapMode <= CLAY_TEXT_WRAP_NEWLINES) || wordEndIndex == text.length) { - Clay_String stringToRender = CLAY__INIT(Clay_String) { .length = wordEndIndex - lineStartIndex, .chars = text.chars + lineStartIndex }; - Clay_String wordToMeasure = CLAY__INIT(Clay_String) { .length = wordEndIndex - wordStartIndex, .chars = text.chars + wordStartIndex }; - // Clip off trailing spaces and newline characters - Clay_Dimensions wordDimensions = Clay__MeasureTextCached(&wordToMeasure, textConfig); - lineDimensions.width = lineDimensions.width + wordDimensions.width + spaceWidth; - 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 && textConfig->wrapMode == CLAY_TEXT_WRAP_WORDS) { - lineDimensions.width -= spaceWidth; - // We can wrap at the most recent word start - if (wordStartIndex != lineStartIndex) { - stringToRender = CLAY__INIT(Clay_String) { .length = wordStartIndex - lineStartIndex - 1, .chars = text.chars + lineStartIndex }; - lineDimensions.width -= (wordDimensions.width + spaceWidth); - lineStartIndex = wordStartIndex; - wordStartIndex = lineStartIndex; - wordEndIndex = lineStartIndex; - containerElement->dimensions.width = CLAY__MAX(containerElement->dimensions.width, lineDimensions.width); - // The single word is larger than the entire container - just render it in place - } else { - lineStartIndex = wordEndIndex + 1; - wordStartIndex = lineStartIndex; - wordEndIndex = lineStartIndex; - containerElement->dimensions.width = CLAY__MAX(containerElement->dimensions.width, lineDimensions.width); - } - // If we're at a space character and the current phrase fits, just keep going - } else if (text.chars[wordEndIndex] == ' ') { - wordStartIndex = wordEndIndex + 1; - wordEndIndex = wordStartIndex; - continue; - // Newline or end of string - } else { - lineStartIndex = wordEndIndex + 1; - wordStartIndex = lineStartIndex; - wordEndIndex = lineStartIndex; + for (int wordIndex = 0; wordIndex < measureTextCacheItem->measuredWords.length; ++wordIndex) { + Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArraySlice_Get(&measureTextCacheItem->measuredWords, wordIndex); + // measuredWord->length == 0 means a newline character + if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) { + Clay__StringArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay_String) {.length = (int)lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] }); + textElementData->wrappedLines.length++; + if (lineLengthChars > 0 && measuredWord->length > 0) { + wordIndex--; } - float lineHeight = textConfig->lineHeight != 0 ? textConfig->lineHeight : lineDimensions.height; - Clay_LayoutElementArray_Add(&Clay__layoutElements, CLAY__INIT(Clay_LayoutElement) { - .text = stringToRender, - .dimensions = { lineDimensions.width, lineHeight }, - .minDimensions = { lineDimensions.width, lineDimensions.height }, - .layoutConfig = CLAY_LAYOUT(.sizing = { .height = CLAY_SIZING_FIXED(lineHeight) }), - .elementConfig = { .textElementConfig = containerElement->elementConfig.textElementConfig }, - .id = Clay__RehashWithNumber(containerElement->id, containerElement->children.length), - .elementType = CLAY__LAYOUT_ELEMENT_TYPE_TEXT, - }); - containerElement->dimensions.height += lineHeight; - containerElement->children.length++; - lineDimensions = CLAY__INIT(Clay_Dimensions) {}; - Clay__int32_tArray_Add(&Clay__layoutElementChildren, (int32_t)Clay__layoutElements.length - 1); + lineWidth = 0; + lineLengthChars = 0; + lineStartOffset = measuredWord->startOffset; } else { - // In the middle of a word - wordEndIndex++; + lineWidth += measuredWord->width; + lineLengthChars += measuredWord->length; } } + if (lineLengthChars > 0) { + Clay__StringArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay_String) {.length = (int)lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] }); + textElementData->wrappedLines.length++; + } + containerElement->dimensions.height = lineHeight * textElementData->wrappedLines.length; } // Scale vertical image heights according to aspect ratio for (int i = 0; i < Clay__imageElementPointers.length; ++i) { - Clay_LayoutElement* imageElement = *Clay__LayoutElementPointerArray_Get(&Clay__imageElementPointers, i); - Clay_ImageElementConfig *config = imageElement->elementConfig.imageElementConfig; + Clay_LayoutElement* imageElement = Clay__LayoutElementPointerArray_Get(&Clay__imageElementPointers, i); + Clay_ImageElementConfig *config = Clay__FindElementConfigWithType(imageElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE).imageElementConfig; imageElement->dimensions.height = (config->sourceDimensions.height / CLAY__MAX(config->sourceDimensions.width, 1)) * imageElement->dimensions.width; } @@ -2153,14 +2223,14 @@ void Clay__CalculateFinalLayout() { 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->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) { + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->children.length == 0) { 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__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]) }); // TODO fix before release + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]) }); } continue; } @@ -2203,8 +2273,8 @@ void Clay__CalculateFinalLayout() { Clay_Vector2 rootPosition = CLAY__INIT(Clay_Vector2) {}; Clay_LayoutElementHashMapItem *parentHashMapItem = Clay__GetHashMapItem(root->parentId); // Position root floating containers - if (rootElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER && parentHashMapItem) { - Clay_FloatingElementConfig *config = rootElement->elementConfig.floatingElementConfig; + if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER) && parentHashMapItem) { + Clay_FloatingElementConfig *config = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER).floatingElementConfig; Clay_Dimensions rootDimensions = rootElement->dimensions; Clay_BoundingBox parentBoundingBox = parentHashMapItem->boundingBox; // Set X position @@ -2280,9 +2350,9 @@ void Clay__CalculateFinalLayout() { if (!Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) { Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; - Clay_BoundingBox currentElementBoundingBox = Clay__BoundingBoxWithRoundedValues(currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height); - if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER) { - Clay_FloatingElementConfig *floatingElementConfig = currentElement->elementConfig.floatingElementConfig; + Clay_BoundingBox currentElementBoundingBox = CLAY__INIT(Clay_BoundingBox) { currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height }; + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER)) { + Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER).floatingElementConfig; Clay_Dimensions expand = floatingElementConfig->expand; currentElementBoundingBox.x -= expand.width; currentElementBoundingBox.width += expand.width * 2; @@ -2292,12 +2362,8 @@ void Clay__CalculateFinalLayout() { Clay__ScrollContainerDataInternal *scrollContainerData = CLAY__NULL; // Apply scroll offsets to container - if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) { - Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { - .boundingBox = currentElementBoundingBox, - .id = Clay__RehashWithNumber(currentElement->id, 10), - .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START, - }); + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; // This linear scan could theoretically be slow under very strange conditions, but I can't imagine a real UI with more than a few 10's of scroll containers for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { @@ -2305,11 +2371,10 @@ void Clay__CalculateFinalLayout() { if (mapping->layoutElement == currentElement) { scrollContainerData = mapping; mapping->boundingBox = currentElementBoundingBox; - Clay_ScrollElementConfig *config = mapping->layoutElement->elementConfig.scrollElementConfig; - if (config->horizontal) { + if (scrollConfig->horizontal) { scrollOffset.x = mapping->scrollPosition.x; } - if (config->vertical) { + if (scrollConfig->vertical) { scrollOffset.y = mapping->scrollPosition.y; } break; @@ -2317,57 +2382,114 @@ void Clay__CalculateFinalLayout() { } } - // Create the render command for this element - Clay_RenderCommand renderCommand = CLAY__INIT(Clay_RenderCommand) { - .boundingBox = currentElementBoundingBox, - .config = currentElement->elementConfig, - .id = currentElement->id, - .commandType = Clay__LayoutElementTypeToRenderCommandType[currentElement->elementType], - }; - Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(currentElement->id); if (hashMapItem) { - hashMapItem->boundingBox = renderCommand.boundingBox; + hashMapItem->boundingBox = currentElementBoundingBox; } - #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)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; - break; - } - case CLAY_RENDER_COMMAND_TYPE_TEXT: { - renderCommand.text = currentElement->text; - if (currentElement->minDimensions.height != currentElement->dimensions.height) { - renderCommand.boundingBox.y += (currentElement->dimensions.height - currentElement->minDimensions.height) / 2; + int sortedConfigIndexes[20]; + for (int elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) { + sortedConfigIndexes[elementConfigIndex] = elementConfigIndex; + } + int sortMax = currentElement->elementConfigs.length - 1; + while (sortMax > 0) { // dumb bubble sort + for (int i = 0; i < sortMax; ++i) { + int current = sortedConfigIndexes[i]; + int next = sortedConfigIndexes[i + 1]; + Clay__ElementConfigType currentType = Clay__ElementConfigArraySlice_Get(¤tElement->elementConfigs, current)->type; + Clay__ElementConfigType nextType = Clay__ElementConfigArraySlice_Get(¤tElement->elementConfigs, next)->type; + if (nextType == CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER || currentType == CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER) { + sortedConfigIndexes[i] = next; + sortedConfigIndexes[i + 1] = current; } - break; } - case CLAY_RENDER_COMMAND_TYPE_BORDER: { // We render borders on close because they need to render above children - shouldRender = false; - break; - } - default: break; - } - if (shouldRender) { - Clay_RenderCommandArray_Add(&Clay__renderCommands, renderCommand); - } - if (offscreen) { - // NOTE: You may be tempted to try an early return / continue if an element is off screen. Why bother calculating layout for its children, right? - // Unfortunately, a FLOATING_CONTAINER may be defined that attaches to a child or grandchild of this element, which is large enough to still - // be on screen, even if this element isn't. That depends on this element and it's children being laid out correctly (even if they are entirely off screen) + sortMax--; } - // Handle child alignment along the layout axis - if (currentElementTreeNode->layoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { - dfsBuffer.length += currentElement->children.length; + // Create the render commands for this element + for (int elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) { + Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(¤tElement->elementConfigs, sortedConfigIndexes[elementConfigIndex]); + Clay_RenderCommand renderCommand = CLAY__INIT(Clay_RenderCommand) { + .boundingBox = currentElementBoundingBox, + .config = elementConfig->config, + .id = currentElement->id, + }; + #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)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 (elementConfig->type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_BORDER; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_NONE; + shouldRender = false; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: { + shouldRender = false; + Clay_ElementConfigUnion configUnion = elementConfig->config; + Clay_TextElementConfig *textElementConfig = configUnion.textElementConfig; + float naturalLineHeight = currentElement->textElementData->preferredDimensions.height; + float finalLineHeight = textElementConfig->lineHeight > 0 ? textElementConfig->lineHeight : naturalLineHeight; + float lineHeightOffset = (finalLineHeight - naturalLineHeight) / 2; + float yPosition = lineHeightOffset; + for (int lineIndex = 0; lineIndex < currentElement->textElementData->wrappedLines.length; ++lineIndex) { + Clay_String wrappedLine = currentElement->textElementData->wrappedLines.internalArray[lineIndex]; // todo range check + if (wrappedLine.length == 0) { + continue; + } + Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { + .boundingBox = { currentElementBoundingBox.x, currentElementBoundingBox.y + yPosition, (float)50, naturalLineHeight }, // TODO width + .config = configUnion, + .text = wrappedLine, + .id = Clay__HashNumber(lineIndex, currentElement->id).id, + .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT, + }); + yPosition += finalLineHeight; + + if (currentElementBoundingBox.y + yPosition > Clay__layoutDimensions.height) { + break; + } + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM; + break; + } + default: break; + } + if (shouldRender) { + Clay_RenderCommandArray_Add(&Clay__renderCommands, renderCommand); + } + if (offscreen) { + // NOTE: You may be tempted to try an early return / continue if an element is off screen. Why bother calculating layout for its children, right? + // Unfortunately, a FLOATING_CONTAINER may be defined that attaches to a child or grandchild of this element, which is large enough to still + // be on screen, even if this element isn't. That depends on this element and it's children being laid out correctly (even if they are entirely off screen) + } + } + + // Setup initial on-axis alignment + if (!Clay__ElementHasConfig(currentElementTreeNode->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { Clay_Dimensions contentSize = CLAY__INIT(Clay_Dimensions) {0,0}; if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { for (int i = 0; i < currentElement->children.length; ++i) { @@ -2400,47 +2522,40 @@ void Clay__CalculateFinalLayout() { } if (scrollContainerData) { - scrollContainerData->contentSize = contentSize; + scrollContainerData->contentSize = CLAY__INIT(Clay_Dimensions) { contentSize.width + layoutConfig->padding.x * 2, contentSize.height + layoutConfig->padding.y * 2 }; } } - } else { + } + else { // DFS is returning upwards backwards - if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) { - Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { - .id = Clay__RehashWithNumber(currentElement->id, 11), - .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END, - }); - // Borders between elements are expressed as additional rectangle render commands - } else if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER) { - Clay_BoundingBox currentElementBoundingBox = Clay__BoundingBoxWithRoundedValues(currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height); - #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 + bool closeScrollElement = false; + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + closeScrollElement = true; + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + // todo get rid of this annoying duplication just for handling borders between elements + for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); + if (mapping->layoutElement == currentElement) { + if (scrollConfig->horizontal) { scrollOffset.x = mapping->scrollPosition.x; } + if (scrollConfig->vertical) { scrollOffset.y = mapping->scrollPosition.y; } + break; + } } - #endif - Clay_BorderElementConfig *borderConfig = currentElement->elementConfig.borderElementConfig; - - Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { - .boundingBox = currentElementBoundingBox, - .config = currentElement->elementConfig, - .id = currentElement->id, - .commandType = CLAY_RENDER_COMMAND_TYPE_BORDER, - }); - - // Render border elements between children + } + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER)) { + Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id); + Clay_BoundingBox currentElementBoundingBox = currentElementData->boundingBox; + Clay_BorderElementConfig *borderConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER).borderElementConfig; if (borderConfig->betweenChildren.width > 0 && borderConfig->betweenChildren.color.a > 0) { + Clay_RectangleElementConfig *rectangleConfig = Clay__StoreRectangleElementConfig(CLAY__INIT(Clay_RectangleElementConfig) {.color = borderConfig->betweenChildren.color}); 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 = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); if (i > 0) { Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { - .boundingBox = { currentElementBoundingBox.x + borderOffset.x, currentElementBoundingBox.y, (float)borderConfig->betweenChildren.width, currentElement->dimensions.height }, - .config = { CLAY_RECTANGLE_CONFIG(.color = borderConfig->betweenChildren.color) }, + .boundingBox = { currentElementBoundingBox.x + borderOffset.x + scrollOffset.x, currentElementBoundingBox.y + scrollOffset.y, (float)borderConfig->betweenChildren.width, currentElement->dimensions.height }, + .config = { rectangleConfig }, .id = Clay__RehashWithNumber(currentElement->id, 5 + i), .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE, }); @@ -2452,8 +2567,8 @@ void Clay__CalculateFinalLayout() { Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); if (i > 0) { Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { - .boundingBox = { currentElementBoundingBox.x, currentElementBoundingBox.y + borderOffset.y, currentElement->dimensions.width, (float)borderConfig->betweenChildren.width }, - .config = { CLAY_RECTANGLE_CONFIG(.color = borderConfig->betweenChildren.color) }, + .boundingBox = { currentElementBoundingBox.x + scrollOffset.x, currentElementBoundingBox.y + borderOffset.y + scrollOffset.y, currentElement->dimensions.width, (float)borderConfig->betweenChildren.width }, + .config = { rectangleConfig }, .id = Clay__RehashWithNumber(currentElement->id, 5 + i), .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE, }); @@ -2463,12 +2578,21 @@ void Clay__CalculateFinalLayout() { } } } + // This exists because the scissor needs to end _after_ borders between elements + if (closeScrollElement) { + Clay_RenderCommandArray_Add(&Clay__renderCommands, CLAY__INIT(Clay_RenderCommand) { + .id = Clay__RehashWithNumber(currentElement->id, 11), + .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END, + }); + } + dfsBuffer.length--; continue; } // Add children to the DFS buffer - if (currentElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { + if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { + dfsBuffer.length += currentElement->children.length; for (int i = 0; i < currentElement->children.length; ++i) { Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[i]); // Alignment along non layout axis @@ -2520,6 +2644,24 @@ void Clay__CalculateFinalLayout() { } } +void Clay__AttachId(Clay_ElementId elementId) { + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + openLayoutElement->id = elementId.id; + Clay__AddHashMapItem(elementId, openLayoutElement); + Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); + #ifdef CLAY_DEBUG + openLayoutElement->name = elementId.stringId; + #endif +} + +void Clay__AttachLayoutConfig(Clay_LayoutConfig *config) { + Clay__GetOpenLayoutElement()->layoutConfig = config; +} +void Clay__AttachElementConfig(Clay_ElementConfigUnion config, Clay__ElementConfigType type) { + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + openLayoutElement->elementConfigs.length++; + Clay__ElementConfigArray_Add(&Clay__elementConfigBuffer, CLAY__INIT(Clay_ElementConfig) { .type = type, .config = config }); +} inline Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay__LayoutConfigArray_Add(&Clay__layoutConfigs, config); } inline Clay_RectangleElementConfig * Clay__StoreRectangleElementConfig(Clay_RectangleElementConfig config) { return Clay__RectangleElementConfigArray_Add(&Clay__rectangleElementConfigs, config); } inline Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay__TextElementConfigArray_Add(&Clay__textElementConfigs, config); } @@ -2541,6 +2683,55 @@ const int CLAY__DEBUGVIEW_INDENT_WIDTH = 16; Clay_TextElementConfig Clay__DebugView_TextNameConfig = CLAY__INIT(Clay_TextElementConfig) {.textColor = {238, 226, 231, 255}, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }; Clay_LayoutConfig Clay__DebugView_ScrollViewItemLayoutConfig = CLAY__INIT(Clay_LayoutConfig) {}; +Clay_String Clay__IntToString(int integer) { + if (integer == 0) { + return CLAY__INIT(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__INIT(Clay_String) { .length = length, .chars = chars }; +} + +typedef struct +{ + Clay_String label; + Clay_Color color; +} Clay__DebugElementConfigTypeLabelConfig; + +Clay__DebugElementConfigTypeLabelConfig Clay__DebugGetElementConfigTypeLabel(Clay__ElementConfigType type) { + switch (type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Rectangle"), CLAY__INIT(Clay_Color) {243,134,48,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Text"), CLAY__INIT(Clay_Color) {105,210,231,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Image"), CLAY__INIT(Clay_Color) {121,189,154,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Floating"), CLAY__INIT(Clay_Color) {250,105,0,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Scroll"), CLAY__INIT(Clay_Color) {242,196,90,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Border"), CLAY__INIT(Clay_Color) {108,91,123, 255} }; + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Custom"), CLAY__INIT(Clay_Color) {11,72,107,255} }; + } + return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Error"), CLAY__INIT(Clay_Color) {0,0,0,255} }; +} + typedef struct { uint32_t rowCount; @@ -2549,23 +2740,6 @@ typedef struct // 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__INIT(Clay_LayoutConfig) { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT) }, .childGap = 6, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }}; Clay__RenderDebugLayoutData layoutData = {}; @@ -2578,8 +2752,8 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR 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 = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }), CLAY_BORDER_CONFIG(.top = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 } )) {} + CLAY(CLAY_IDI("Clay__DebugView_EmptyRowOuter", rootIndex), CLAY_LAYOUT({ .sizing = {.width = CLAY_SIZING_GROW({})}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2} })) { + CLAY(CLAY_IDI("Clay__DebugView_EmptyRow", rootIndex), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW({}), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }}), CLAY_BORDER({ .top = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 } })) {} } layoutData.rowCount++; } @@ -2587,24 +2761,23 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR 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(); + if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && currentElement->children.length > 0) { + Clay__CloseElement(); + Clay__CloseElement(); + Clay__CloseElement(); } dfsBuffer.length--; continue; } if (highlightedRowIndex == layoutData.rowCount) { - if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + 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_ElementId outerHash = Clay__Rehash(outerId, currentElement->id); Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id); Clay_BoundingBox currentElementBoundingBox = currentElementData->boundingBox; #ifndef CLAY_DISABLE_CULLING @@ -2615,75 +2788,77 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR 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(.textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16)); + CLAY(CLAY_IDI("Clay__DebugView_ElementOuter", currentElement->id), Clay__AttachLayoutConfig(&Clay__DebugView_ScrollViewItemLayoutConfig)) { + // Collapse icon / button + if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->children.length == 0)) { + CLAY(CLAY_IDI("Clay__DebugView_CollapseElement", currentElement->id), + CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} }), + CLAY_BORDER_OUTSIDE_RADIUS(1, CLAY__DEBUGVIEW_COLOR_3, 4) + ) { + CLAY_TEXT((currentElementData && currentElementData->debugData->collapsed) ? CLAY_STRING("+") : CLAY_STRING("-"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 })); } - } 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))) {} + } else { // Square dot for empty containers + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER } })) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(8), CLAY_SIZING_FIXED(8)} }), CLAY_RECTANGLE({ .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__INIT(Clay_Color){177, 147, 8, 255}), 4)) { - CLAY_TEXT(Clay__Rehash(elementDuplicateWarning, currentElement->id), CLAY_STRING("Duplicate ID"), CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16)); - } - } - - 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(.textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16)); - } + // Collisions and offscreen info + if (currentElementData) { + if (currentElementData->debugData->collision) { + CLAY(CLAY_LAYOUT({ .padding = { 8, 2 } }), CLAY_BORDER_OUTSIDE_RADIUS(1, (CLAY__INIT(Clay_Color){177, 147, 8, 255}), 4)) { + CLAY_TEXT(CLAY_STRING("Duplicate ID"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 })); } } - CLAY_TEXT(Clay__Rehash(elementNameId, currentElement->id), Clay__layoutElementIdStrings.internalArray[currentElementIndex], offscreen ? CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16) : &Clay__DebugView_TextNameConfig); - Clay_String elementTypeName = CLAY__INIT(Clay_String){}; - Clay_Color elementTypeColor = CLAY__INIT(Clay_Color){}; - switch (currentElement->elementType) { - case CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE: elementTypeName = CLAY_STRING("Rectangle"); elementTypeColor = (CLAY__INIT(Clay_Color) {243,134,48,255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER: elementTypeName = CLAY_STRING("Container"); elementTypeColor = (CLAY__INIT(Clay_Color) {53,92,125, 255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_TEXT: elementTypeName = CLAY_STRING("Text"); elementTypeColor = (CLAY__INIT(Clay_Color) {105,210,231,255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_IMAGE: elementTypeName = CLAY_STRING("Image"); elementTypeColor = (CLAY__INIT(Clay_Color) {121,189,154,255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER: elementTypeName = CLAY_STRING("Floating"); elementTypeColor = (CLAY__INIT(Clay_Color) {250,105,0,255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER: elementTypeName = CLAY_STRING("Scroll"); elementTypeColor = (CLAY__INIT(Clay_Color) {242,196,90,255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER: elementTypeName = CLAY_STRING("Border"); elementTypeColor = (CLAY__INIT(Clay_Color) {108,91,123, 255}); break; - case CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM: elementTypeName = CLAY_STRING("Custom"); elementTypeColor = (CLAY__INIT(Clay_Color) {11,72,107,255}); break; + if (offscreen) { + CLAY(CLAY_LAYOUT({ .padding = { 8, 2 } }), CLAY_BORDER_OUTSIDE_RADIUS(1, CLAY__DEBUGVIEW_COLOR_3, 4)) { + CLAY_TEXT(CLAY_STRING("Offscreen"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 })); + } } - Clay_Color backgroundColor = elementTypeColor; + } + Clay_String idString = Clay__layoutElementIdStrings.internalArray[currentElementIndex]; + if (idString.length > 0) { + CLAY_TEXT(idString, offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig); + } + for (int elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) { + Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(¤tElement->elementConfigs, elementConfigIndex); + Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(elementConfig->type); + Clay_Color backgroundColor = config.color; 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(.textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16)); - } + CLAY(CLAY_LAYOUT({ .padding = { 8, 2 } }), CLAY_RECTANGLE({ .color = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4) }), CLAY_BORDER_OUTSIDE_RADIUS(1, config.color, 4)) { + CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 })); } } } // Render the text contents below the element as a non-interactive row - if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { layoutData.rowCount++; - Clay_TextElementConfig *rawTextConfig = offscreen ? CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16) : &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__INIT(Clay_String) { .length = 40, .chars = currentElement->text.chars }) : currentElement->text, rawTextConfig); - if (currentElement->text.length > 40) { - CLAY_TEXT(CLAY_IDI("Clay__DebugView_TextEllipsis", currentElement->id), CLAY_STRING("..."), rawTextConfig); + Clay__TextElementData *textElementData = currentElement->textElementData; + Clay_TextElementConfig *rawTextConfig = offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig; + CLAY(CLAY_LAYOUT({ .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }), CLAY_RECTANGLE({})) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16)} })) {} + CLAY_TEXT(CLAY_STRING("\""), rawTextConfig); + CLAY_TEXT(textElementData->text.length > 40 ? (CLAY__INIT(Clay_String) { .length = 40, .chars = textElementData->text.chars }) : textElementData->text, rawTextConfig); + if (textElementData->text.length > 40) { + CLAY_TEXT(CLAY_STRING("..."), rawTextConfig); } - CLAY_TEXT(CLAY_IDI("Clay__DebugView_TextCloseQuote", currentElement->id), CLAY_STRING("\""), rawTextConfig); + CLAY_TEXT(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 = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 })); - 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)); + Clay__OpenElement(); + CLAY_LAYOUT({ .padding = { 8 } }); + Clay__ElementPostConfiguration(); + Clay__OpenElement(); + CLAY_BORDER({ .left = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 }}); + Clay__ElementPostConfiguration(); + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED( CLAY__DEBUGVIEW_INDENT_WIDTH)}, .childAlignment = { .x = CLAY_ALIGN_X_RIGHT } })) {} + Clay__OpenElement(); + CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM }); + Clay__ElementPostConfiguration(); } layoutData.rowCount++; - if (!(currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT || (currentElementData && currentElementData->debugData->collapsed))) { + if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_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 @@ -2692,11 +2867,12 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR } } - if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + if (Clay__pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME) { + Clay_ElementId collapseButtonId = Clay__HashString(CLAY_STRING("Clay__DebugView_CollapseElement"), 0, 0); 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) { + if (elementId->baseId == collapseButtonId.baseId) { Clay_LayoutElementHashMapItem *highlightedItem = Clay__GetHashMapItem(elementId->offset); highlightedItem->debugData->collapsed = !highlightedItem->debugData->collapsed; break; @@ -2706,99 +2882,102 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR } 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)) {} + CLAY(CLAY_ID("Clay__DebugView_ElementHighlight"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})} }), CLAY_FLOATING({ .zIndex = 65535, .parentId = highlightedElementId })) { + CLAY(CLAY_ID("Clay__DebugView_ElementHighlightRectangle"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})} }), CLAY_RECTANGLE({.color = Clay__debugViewHighlightColor })) {} } } return layoutData; } -void Clay__RenderDebugLayoutSizing(Clay_ElementId baseId, Clay_SizingAxis sizing, Clay_TextElementConfig *infoTextConfig) { +void Clay__RenderDebugLayoutSizing(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"); - } else if (sizing.type == CLAY__SIZING_TYPE_FIXED) { - sizingLabel = CLAY_STRING("FIXED"); - } - CLAY_TEXT(Clay__Rehash(baseId, 1), sizingLabel, infoTextConfig); + } + CLAY_TEXT(sizingLabel, infoTextConfig); if (sizing.type == CLAY__SIZING_TYPE_GROW || sizing.type == CLAY__SIZING_TYPE_FIT) { - CLAY_TEXT(Clay__Rehash(baseId, 2), CLAY_STRING("("), infoTextConfig); + CLAY_TEXT(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); + CLAY_TEXT(CLAY_STRING("min: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(sizing.sizeMinMax.min), infoTextConfig); if (sizing.sizeMinMax.max != CLAY__MAXFLOAT) { - CLAY_TEXT(Clay__Rehash(baseId, 5), CLAY_STRING(", "), infoTextConfig); + CLAY_TEXT(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_STRING("max: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(sizing.sizeMinMax.max), infoTextConfig); } - CLAY_TEXT(Clay__Rehash(baseId, 8), CLAY_STRING(")"), infoTextConfig); - } else { - CLAY_TEXT(Clay__Rehash(baseId, 2), CLAY_STRING("("), infoTextConfig); - CLAY_TEXT(Clay__Rehash(baseId, 4), Clay__IntToString(sizing.sizeMinMax.min), infoTextConfig); - CLAY_TEXT(Clay__Rehash(baseId, 8), CLAY_STRING(")"), infoTextConfig); + CLAY_TEXT(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)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER})) { - CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementConfigTitle", 1), title, CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .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(.textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE)); +void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__ElementConfigType type) { + Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(type); + Clay_Color backgroundColor = config.color; + backgroundColor.a = 90; + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } })) { + CLAY(CLAY_LAYOUT({ .padding = { 8, 2 } }), CLAY_RECTANGLE({ .color = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4) }), CLAY_BORDER_OUTSIDE_RADIUS(1, config.color, 4)) { + CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 })); + } + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({}) } })) {} + CLAY_TEXT(elementId, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .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_LOCAL("Color"), CLAY_LAYOUT(.childAlignment = {.y = CLAY_ALIGN_Y_CENTER})) { - CLAY_TEXT(CLAY_ID_LOCAL("Red"), CLAY_STRING("{ r: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("RedValue"), Clay__IntToString(color.r), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Green"), CLAY_STRING(", g: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("GreenValue"), Clay__IntToString(color.g), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Blue"), CLAY_STRING(", b: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("BlueValue"), Clay__IntToString(color.b), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Alpha"), CLAY_STRING(", a: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("AlphaValue"), Clay__IntToString(color.a), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Brace"), CLAY_STRING(" }"), textConfig); - CLAY_CONTAINER(CLAY_ID_LOCAL("Spacer"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(10) })) {} - CLAY_BORDER_CONTAINER(CLAY_ID_LOCAL("PreviewBorder"), 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_LOCAL("PreviewBackground"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)}), CLAY_RECTANGLE_CONFIG(.color = color, .cornerRadius = CLAY_CORNER_RADIUS(4))) {} + CLAY(CLAY_LAYOUT({ .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} })) { + CLAY_TEXT(CLAY_STRING("{ r: "), textConfig); + CLAY_TEXT(Clay__IntToString(color.r), textConfig); + CLAY_TEXT(CLAY_STRING(", g: "), textConfig); + CLAY_TEXT(Clay__IntToString(color.g), textConfig); + CLAY_TEXT(CLAY_STRING(", b: "), textConfig); + CLAY_TEXT(Clay__IntToString(color.b), textConfig); + CLAY_TEXT(CLAY_STRING(", a: "), textConfig); + CLAY_TEXT(Clay__IntToString(color.a), textConfig); + CLAY_TEXT(CLAY_STRING(" }"), textConfig); + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(10) } })) {} + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)} }), CLAY_BORDER_OUTSIDE_RADIUS(1, CLAY__DEBUGVIEW_COLOR_4, 4)) { + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)} }), CLAY_RECTANGLE({ .color = color, .cornerRadius = CLAY_CORNER_RADIUS(4) })) {} } } } void Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) { - CLAY_CONTAINER(CLAY_ID_LOCAL("CornerRadius"), CLAY_LAYOUT(.childAlignment = {.y = CLAY_ALIGN_Y_CENTER})) { - CLAY_TEXT(CLAY_ID_LOCAL("TopLeftLabel"), CLAY_STRING("{ topLeft: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("TopLeft"), Clay__IntToString(cornerRadius.topLeft), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("TopRightLabel"), CLAY_STRING(", topRight: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("TopRight"), Clay__IntToString(cornerRadius.topRight), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("BottomLeftLabel"), CLAY_STRING(", bottomLeft: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("BottomLeft"), Clay__IntToString(cornerRadius.bottomLeft), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("BottomRightLabel"), CLAY_STRING(", bottomRight: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("BottomRight"), Clay__IntToString(cornerRadius.bottomRight), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Brace"), CLAY_STRING(" }"), textConfig); + CLAY(CLAY_LAYOUT({ .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} })) { + CLAY_TEXT(CLAY_STRING("{ topLeft: "), textConfig); + CLAY_TEXT(Clay__IntToString(cornerRadius.topLeft), textConfig); + CLAY_TEXT(CLAY_STRING(", topRight: "), textConfig); + CLAY_TEXT(Clay__IntToString(cornerRadius.topRight), textConfig); + CLAY_TEXT(CLAY_STRING(", bottomLeft: "), textConfig); + CLAY_TEXT(Clay__IntToString(cornerRadius.bottomLeft), textConfig); + CLAY_TEXT(CLAY_STRING(", bottomRight: "), textConfig); + CLAY_TEXT(Clay__IntToString(cornerRadius.bottomRight), textConfig); + CLAY_TEXT(CLAY_STRING(" }"), textConfig); } } void Clay__RenderDebugViewBorder(int index, Clay_Border border, Clay_TextElementConfig *textConfig) { - CLAY_CONTAINER(CLAY_ID_LOCAL("BorderConfig"), CLAY_LAYOUT(.childAlignment = {.y = CLAY_ALIGN_Y_CENTER})) { - CLAY_TEXT(CLAY_ID_LOCAL("WidthLabel"), CLAY_STRING("{ width: "), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Width"), Clay__IntToString(border.width), textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("ColorLabel"), CLAY_STRING(", color: "), textConfig); + CLAY(CLAY_LAYOUT({ .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} })) { + CLAY_TEXT(CLAY_STRING("{ width: "), textConfig); + CLAY_TEXT(Clay__IntToString(border.width), textConfig); + CLAY_TEXT(CLAY_STRING(", color: "), textConfig); Clay__RenderDebugViewColor(border.color, textConfig); - CLAY_TEXT(CLAY_ID_LOCAL("Color"), CLAY_STRING(" }"), textConfig); + CLAY_TEXT(CLAY_STRING(" }"), textConfig); + } +} + +void HandleDebugViewCloseButtonInteraction(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData) { + if (pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME) { + Clay__debugModeEnabled = false; } } void Clay__RenderDebugView() { - Clay_ElementId closeButtonId = CLAY_ID("Clay__DebugViewTopHeaderCloseButtonOuter"); - if (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME) { + Clay_ElementId closeButtonId = Clay__HashString(CLAY_STRING("Clay__DebugViewTopHeaderCloseButtonOuter"), 0, 0); + 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) { @@ -2810,13 +2989,13 @@ void Clay__RenderDebugView() { uint32_t initialRootsLength = Clay__layoutElementTreeRoots.length; uint32_t initialElementsLength = Clay__layoutElements.length; - Clay_TextElementConfig *infoTextConfig = CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE); - Clay_TextElementConfig *infoTitleConfig = CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE); - uint32_t scrollId = CLAY_ID("Clay__DebugViewOuterScrollPane").id; + Clay_TextElementConfig *infoTextConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }); + Clay_TextElementConfig *infoTitleConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }); + Clay_ElementId scrollId = Clay__HashString(CLAY_STRING("Clay__DebugViewOuterScrollPane"), 0, 0); 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) { + if (scrollContainerData->elementId == scrollId.id) { scrollYOffset = scrollContainerData->scrollPosition.y; break; } @@ -2826,282 +3005,284 @@ void Clay__RenderDebugView() { highlightedRow = -1; } Clay__RenderDebugLayoutData layoutData = {}; - CLAY_FLOATING_CONTAINER(CLAY_ID("Clay__DebugView"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_FIXED((float)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(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .layoutDirection = CLAY_TOP_TO_BOTTOM )) { - CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewTopHeaderOuter"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}), 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__INIT(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__INIT(Clay_Color){217,91,67,80}))) { - CLAY_TEXT(CLAY_ID("Clay__DebugViewTopHeaderCloseButtonText"), CLAY_STRING("x"), CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16)); + CLAY(CLAY_ID("Clay__DebugView"), + CLAY_FLOATING({ .parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0, 0).id, .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_CENTER }}), + CLAY_LAYOUT({ .sizing = { CLAY_SIZING_FIXED((float)Clay__debugViewWidth) , CLAY_SIZING_FIXED(Clay__layoutDimensions.height) }, .layoutDirection = CLAY_TOP_TO_BOTTOM }), + CLAY_BORDER({ .bottom = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 }}) + ) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }), CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_2 })) { + CLAY_TEXT(CLAY_STRING("Clay Debug Tools"), infoTextConfig); + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({}) } })) {} + // Close button + CLAY(CLAY_BORDER_OUTSIDE_RADIUS(1, (CLAY__INIT(Clay_Color){217,91,67,255}), 4), + 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({ .color = {217,91,67,80} }), + Clay_OnHover(HandleDebugViewCloseButtonInteraction, 0) + ) { + CLAY_TEXT(CLAY_STRING("x"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 })); + } + } + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(1)} }), CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_3 })) {} + CLAY(Clay__AttachId(scrollId), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})} }), CLAY_SCROLL({ .horizontal = true, .vertical = true })) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})}, .layoutDirection = CLAY_TOP_TO_BOTTOM }), CLAY_RECTANGLE({ .color = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1 })) { + Clay_ElementId panelContentsId = Clay__HashString(CLAY_STRING("Clay__DebugViewPaneOuter"), 0, 0); + // Element list + CLAY(Clay__AttachId(panelContentsId), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})} }), CLAY_FLOATING({})) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})}, .padding = {.x = CLAY__DEBUGVIEW_OUTER_PADDING }, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + layoutData = Clay__RenderDebugLayoutElementsList((int32_t)initialRootsLength, highlightedRow); } } - } - 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(.horizontal = true, .vertical = true)) { - CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewPaneBackground"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .layoutDirection = CLAY_TOP_TO_BOTTOM), 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(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .padding = {.x = CLAY__DEBUGVIEW_OUTER_PADDING }, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - layoutData = Clay__RenderDebugLayoutElementsList((int32_t)initialRootsLength, highlightedRow); - } + float contentWidth = Clay__GetHashMapItem(panelContentsId.id)->layoutElement->dimensions.width; + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(contentWidth)}, .layoutDirection = CLAY_TOP_TO_BOTTOM })) {} + 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; } - float contentWidth = Clay__GetHashMapItem(panelContentsId.id)->layoutElement->dimensions.width; - CLAY_CONTAINER(CLAY_ID("Clay__DebugViewScrollPanelWidth"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(contentWidth)}, .layoutDirection = CLAY_TOP_TO_BOTTOM)) {} - 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.25f; - rowColor.g *= 1.25f; - rowColor.b *= 1.25f; - } - CLAY_RECTANGLE(CLAY_IDI("Clay__DebugViewStripe", i), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM), CLAY_RECTANGLE_CONFIG(.color = rowColor)) {} + if (i == highlightedRow) { + rowColor.r *= 1.25f; + rowColor.g *= 1.25f; + rowColor.b *= 1.25f; } + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }), CLAY_RECTANGLE({ .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(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .layoutDirection = CLAY_TOP_TO_BOTTOM), 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)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER})) { - 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(CLAY_LAYOUT({ .sizing = {.width = CLAY_SIZING_GROW({}), .height = CLAY_SIZING_FIXED(1)} }), CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_3 })) {} + if (Clay__debugSelectedElementId != 0) { + Clay_LayoutElementHashMapItem *selectedItem = Clay__GetHashMapItem(Clay__debugSelectedElementId); + CLAY( + CLAY_SCROLL({ .vertical = true }), + CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(300)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }), + CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_2 }), + CLAY_BORDER({ .betweenChildren = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 }}) + ) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} })) { + CLAY_TEXT(CLAY_STRING("Layout Config"), infoTextConfig); + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({}) } })) {} + if (selectedItem->elementId.stringId.length != 0) { + CLAY_TEXT(selectedItem->elementId.stringId, infoTitleConfig); + if (selectedItem->elementId.offset != 0) { + CLAY_TEXT(CLAY_STRING(" ("), infoTitleConfig); + CLAY_TEXT(Clay__IntToString(selectedItem->elementId.offset), infoTitleConfig); + CLAY_TEXT(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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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); - } + } + } + // Clay_LayoutConfig debug info + CLAY(CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .boundingBox + CLAY_TEXT(CLAY_STRING("Bounding Box"), infoTitleConfig); + CLAY(CLAY_LAYOUT({})) { + CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.x), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.y), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", width: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.width), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.height), infoTextConfig); + CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig); + } + // .layoutDirection + CLAY_TEXT(CLAY_STRING("Layout Direction"), infoTitleConfig); + Clay_LayoutConfig *layoutConfig = selectedItem->layoutElement->layoutConfig; + CLAY_TEXT(layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM ? CLAY_STRING("TOP_TO_BOTTOM") : CLAY_STRING("LEFT_TO_RIGHT"), infoTextConfig); + // .sizing + CLAY_TEXT(CLAY_STRING("Sizing"), infoTitleConfig); + CLAY(CLAY_LAYOUT({})) { + CLAY_TEXT(CLAY_STRING("width: "), infoTextConfig); + Clay__RenderDebugLayoutSizing(layoutConfig->sizing.width, infoTextConfig); + } + CLAY(CLAY_LAYOUT({})) { + CLAY_TEXT(CLAY_STRING("height: "), infoTextConfig); + Clay__RenderDebugLayoutSizing(layoutConfig->sizing.height, infoTextConfig); + } + // .padding + CLAY_TEXT(CLAY_STRING("Padding"), infoTitleConfig); + CLAY(CLAY_ID("Clay__DebugViewElementInfoPadding")) { + CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->padding.x), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->padding.y), infoTextConfig); + CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig); + } + // .childGap + CLAY_TEXT(CLAY_STRING("Child Gap"), infoTitleConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->childGap), infoTextConfig); + // .childAlignment + CLAY_TEXT(CLAY_STRING("Child Alignment"), infoTitleConfig); + CLAY(CLAY_LAYOUT({})) { + CLAY_TEXT(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"); } - 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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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); + CLAY_TEXT(alignX, infoTextConfig); + CLAY_TEXT(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(alignY, infoTextConfig); + CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig); + } + } + for (int elementConfigIndex = 0; elementConfigIndex < selectedItem->layoutElement->elementConfigs.length; ++elementConfigIndex) { + Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&selectedItem->layoutElement->elementConfigs, elementConfigIndex); + Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, elementConfig->type); + switch (elementConfig->type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: { + Clay_RectangleElementConfig *rectangleConfig = elementConfig->config.rectangleElementConfig; + CLAY(CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .color + CLAY_TEXT(CLAY_STRING("Color"), infoTitleConfig); + Clay__RenderDebugViewColor(rectangleConfig->color, infoTextConfig); + // .cornerRadius + CLAY_TEXT(CLAY_STRING("Corner Radius"), infoTitleConfig); + Clay__RenderDebugViewCornerRadius(rectangleConfig->cornerRadius, infoTextConfig); + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: { + Clay_TextElementConfig *textConfig = elementConfig->config.textElementConfig; + CLAY(CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .fontSize + CLAY_TEXT(CLAY_STRING("Font Size"), infoTitleConfig); + CLAY_TEXT(Clay__IntToString(textConfig->fontSize), infoTextConfig); + // .fontId + CLAY_TEXT(CLAY_STRING("Font ID"), infoTitleConfig); + CLAY_TEXT(Clay__IntToString(textConfig->fontId), infoTextConfig); + // .lineHeight + CLAY_TEXT(CLAY_STRING("Line Height"), infoTitleConfig); + CLAY_TEXT(textConfig->lineHeight == 0 ? CLAY_STRING("auto") : Clay__IntToString(textConfig->lineHeight), infoTextConfig); + // .letterSpacing + CLAY_TEXT(CLAY_STRING("Letter Spacing"), infoTitleConfig); + CLAY_TEXT(Clay__IntToString(textConfig->letterSpacing), infoTextConfig); + // .lineSpacing + CLAY_TEXT(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"); } - break; + CLAY_TEXT(wrapMode, infoTextConfig); + // .textColor + CLAY_TEXT(CLAY_STRING("Text Color"), infoTitleConfig); + Clay__RenderDebugViewColor(textConfig->textColor, infoTextConfig); } - 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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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); - // .lineHeight - CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontTitle", 3), CLAY_STRING("Line Height"), infoTitleConfig); - CLAY_TEXT(CLAY_IDI("Clay__DebugViewElementInfoRectangleFontBody", 3), textConfig->lineHeight == 0 ? CLAY_STRING("Auto") : Clay__IntToString(textConfig->lineHeight), 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__ELEMENT_CONFIG_TYPE_IMAGE: { + Clay_ImageElementConfig *imageConfig = elementConfig->config.imageElementConfig; + CLAY(CLAY_ID("Clay__DebugViewElementInfoImageBody"), CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .sourceDimensions + CLAY_TEXT(CLAY_STRING("Source Dimensions"), infoTitleConfig); + CLAY(CLAY_ID("Clay__DebugViewElementInfoImageDimensions")) { + CLAY_TEXT(CLAY_STRING("{ width: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(imageConfig->sourceDimensions.width), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(imageConfig->sourceDimensions.height), infoTextConfig); + CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig); } - break; + // Image Preview + CLAY_TEXT(CLAY_STRING("Preview"), infoTitleConfig); + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = imageConfig->sourceDimensions.width }) }}), Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = imageConfig }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) {} } - 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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: { + Clay_ScrollElementConfig *scrollConfig = elementConfig->config.scrollElementConfig; + CLAY(CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .vertical + CLAY_TEXT(CLAY_STRING("Vertical"), infoTitleConfig); + CLAY_TEXT(scrollConfig->vertical ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig); + // .horizontal + CLAY_TEXT(CLAY_STRING("Horizontal"), infoTitleConfig); + CLAY_TEXT(scrollConfig->horizontal ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig); + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER: { + Clay_FloatingElementConfig *floatingConfig = elementConfig->config.floatingElementConfig; + CLAY(CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .offset + CLAY_TEXT(CLAY_STRING("Offset"), infoTitleConfig); + CLAY(CLAY_LAYOUT({})) { + CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(floatingConfig->offset.x), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(floatingConfig->offset.y), infoTextConfig); + CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig); } - 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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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); + // .expand + CLAY_TEXT(CLAY_STRING("Expand"), infoTitleConfig); + CLAY(CLAY_LAYOUT({})) { + CLAY_TEXT(CLAY_STRING("{ width: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(floatingConfig->expand.width), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(floatingConfig->expand.height), infoTextConfig); + CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig); } - break; + // .zIndex + CLAY_TEXT(CLAY_STRING("z-index"), infoTitleConfig); + CLAY_TEXT(Clay__IntToString(floatingConfig->zIndex), infoTextConfig); + // .parentId + CLAY_TEXT(CLAY_STRING("Parent"), infoTitleConfig); + Clay_LayoutElementHashMapItem *hashItem = Clay__GetHashMapItem(floatingConfig->parentId); + CLAY_TEXT(hashItem->elementId.stringId, infoTextConfig); } - 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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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(.padding = {8, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM)) { - // .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; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: { + Clay_BorderElementConfig *borderConfig = elementConfig->config.borderElementConfig; + CLAY(CLAY_ID("Clay__DebugViewElementInfoBorderBody"), CLAY_LAYOUT({ .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, 8}, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .left + CLAY_TEXT(CLAY_STRING("Left Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(1, borderConfig->left, infoTextConfig); + // .right + CLAY_TEXT(CLAY_STRING("Right Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(2, borderConfig->right, infoTextConfig); + // .top + CLAY_TEXT(CLAY_STRING("Top Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(3, borderConfig->top, infoTextConfig); + // .bottom + CLAY_TEXT(CLAY_STRING("Bottom Border"), infoTitleConfig); + Clay__RenderDebugViewBorder(4, borderConfig->bottom, infoTextConfig); + // .betweenChildren + CLAY_TEXT(CLAY_STRING("Border Between Children"), infoTitleConfig); + Clay__RenderDebugViewBorder(5, borderConfig->betweenChildren, infoTextConfig); + // .cornerRadius + CLAY_TEXT(CLAY_STRING("Corner Radius"), infoTitleConfig); + Clay__RenderDebugViewCornerRadius(borderConfig->cornerRadius, infoTextConfig); } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: { + break; } } } } - else { - CLAY_SCROLL_CONTAINER(CLAY_ID("Clay__DebugViewWarningsScrollPane"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(300)}, .childGap = 6), CLAY_SCROLL_CONFIG(.horizontal = true, .vertical = true)) { - Clay_TextElementConfig *warningConfig = CLAY_TEXT_CONFIG(.textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE); - CLAY_RECTANGLE(CLAY_ID("Clay__DebugViewWarningsPane"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, .layoutDirection = CLAY_TOP_TO_BOTTOM), 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)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} )) { - 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 = (int)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)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER})) { - 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); - } - } + } else { + CLAY(CLAY_ID("Clay__DebugViewWarningsScrollPane"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_FIXED(300)}, .childGap = 6, .layoutDirection = CLAY_TOP_TO_BOTTOM }), CLAY_SCROLL({ .horizontal = true, .vertical = true }), CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_2 })) { + Clay_TextElementConfig *warningConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }); + CLAY(CLAY_ID("Clay__DebugViewWarningItemHeader"), CLAY_LAYOUT({ .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} })) { + CLAY_TEXT(CLAY_STRING("Warnings"), warningConfig); + } + CLAY(CLAY_ID("Clay__DebugViewWarningsTopBorder"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW({}), .height = CLAY_SIZING_FIXED(1)} }), CLAY_RECTANGLE({ .color = {200, 200, 200, 255} })) {} + int previousWarningsLength = (int)Clay_warnings.length; + for (int i = 0; i < previousWarningsLength; i++) { + Clay__Warning warning = Clay_warnings.internalArray[i]; + CLAY(CLAY_IDI("Clay__DebugViewWarningItem", i), CLAY_LAYOUT({ .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING}, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} })) { + CLAY_TEXT(warning.baseMessage, warningConfig); + if (warning.dynamicMessage.length > 0) { + CLAY_TEXT(warning.dynamicMessage, warningConfig); } } } @@ -3158,11 +3339,13 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) { } Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; 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 + Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO think of a way around this, maybe the fact that it's essentially a binary tree limits the cost, but the worst case is not great if (mapItem && Clay__PointIsInsideRect(position, mapItem->boundingBox)) { + if (mapItem->onHoverFunction) { + mapItem->onHoverFunction(mapItem->elementId, Clay__pointerInfo, mapItem->hoverFunctionUserData); + } Clay__ElementIdArray_Add(&Clay__pointerOverIds, mapItem->elementId); - - if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT) { + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { dfsBuffer.length--; continue; } @@ -3177,16 +3360,16 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) { } 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; + 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; + 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; } } } @@ -3208,7 +3391,7 @@ void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions) { CLAY_WASM_EXPORT("Clay_UpdateScrollContainers") void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime) { - bool isPointerActive = enableDragScrolling && (Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED || Clay__pointerInfo.state == CLAY__POINTER_INFO_PRESSED_THIS_FRAME); + bool isPointerActive = enableDragScrolling && (Clay__pointerInfo.state == CLAY_POINTER_INFO_PRESSED || Clay__pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME); // Don't apply scroll events to ancestors of the inner element int32_t highestPriorityElementIndex = -1; Clay__ScrollContainerDataInternal *highestPriorityScrollData = CLAY__NULL; @@ -3269,8 +3452,9 @@ void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDe if (highestPriorityElementIndex > -1 && highestPriorityScrollData) { Clay_LayoutElement *scrollElement = highestPriorityScrollData->layoutElement; - bool canScrollVertically = scrollElement->elementConfig.scrollElementConfig->vertical && highestPriorityScrollData->contentSize.height > scrollElement->dimensions.height; - bool canScrollHorizontally = scrollElement->elementConfig.scrollElementConfig->horizontal && highestPriorityScrollData->contentSize.width > scrollElement->dimensions.width; + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(scrollElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + bool canScrollVertically = scrollConfig->vertical && highestPriorityScrollData->contentSize.height > scrollElement->dimensions.height; + bool canScrollHorizontally = scrollConfig->horizontal && highestPriorityScrollData->contentSize.width > scrollElement->dimensions.width; // Handle wheel scroll if (canScrollVertically) { highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollPosition.y + scrollDelta.y * 10; @@ -3328,19 +3512,18 @@ void Clay_BeginLayout() { if (Clay__debugModeEnabled) { rootDimensions.width -= (float)Clay__debugViewWidth; } - Clay_ElementId rootElementId = CLAY_ID("Clay__RootContainer"); - Clay_LayoutElement rootLayoutElement = CLAY__INIT(Clay_LayoutElement) {.layoutConfig = CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)}), .id = rootElementId.id, }; - Clay__openLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, rootLayoutElement); - Clay__LayoutElementPointerArray_Add(&Clay__openLayoutElementStack, Clay__openLayoutElement); - Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { .layoutElementIndex = Clay__layoutElements.length - 1 }); - Clay__StringArray_Add(&Clay__layoutElementIdStrings, CLAY_STRING("Clay__RootContainer")); - Clay__AddHashMapItem(rootElementId, Clay__openLayoutElement); + Clay__OpenElement(); + CLAY_ID("Clay__RootContainer"); + CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)} }); + Clay__ElementPostConfiguration(); + Clay__int32_tArray_Add(&Clay__openLayoutElementStack, 0); + Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { .layoutElementIndex = 0 }); } CLAY_WASM_EXPORT("Clay_EndLayout") Clay_RenderCommandArray Clay_EndLayout() { - Clay__AttachContainerChildren(); + Clay__CloseElement(); if (Clay__debugModeEnabled) { #ifndef CLAY_DEBUG Clay__warningsEnabled = false; @@ -3354,6 +3537,35 @@ Clay_RenderCommandArray Clay_EndLayout() return Clay__renderCommands; } +CLAY_WASM_EXPORT("Clay_GetElementId") +Clay_ElementId Clay_GetElementId(Clay_String idString) { + return Clay__HashString(idString, 0, 0); +} + +bool Clay_Hovered() { + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + // If the element has no id attached at this point, we need to generate one + if (openLayoutElement->id == 0) { + Clay__GenerateIdForAnonymousElement(openLayoutElement); + } + for (int i = 0; i < Clay__pointerOverIds.length; ++i) { + if (Clay__ElementIdArray_Get(&Clay__pointerOverIds, i)->id == openLayoutElement->id) { + return true; + } + } + return false; +} + +void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData), intptr_t userData) { + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + if (openLayoutElement->id == 0) { + Clay__GenerateIdForAnonymousElement(openLayoutElement); + } + Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(openLayoutElement->id); + hashMapItem->onHoverFunction = onHoverFunction; + hashMapItem->hoverFunctionUserData = userData; +} + CLAY_WASM_EXPORT("Clay_PointerOver") bool Clay_PointerOver(Clay_ElementId elementId) { // TODO return priority for separating multiple results for (int i = 0; i < Clay__pointerOverIds.length; ++i) { @@ -3373,7 +3585,7 @@ Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id) { .scrollPosition = &scrollContainerData->scrollPosition, .scrollContainerDimensions = CLAY__INIT(Clay_Dimensions) { scrollContainerData->boundingBox.width, scrollContainerData->boundingBox.height }, .contentDimensions = scrollContainerData->contentSize, - .config = *scrollContainerData->layoutElement->elementConfig.scrollElementConfig, + .config = *Clay__FindElementConfigWithType(scrollContainerData->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig, .found = true }; } diff --git a/examples/clay-official-website/index.html b/examples/clay-official-website/index.html index 285fea2..ff6fb85 100644 --- a/examples/clay-official-website/index.html +++ b/examples/clay-official-website/index.html @@ -322,11 +322,13 @@ let length = memoryDataView.getUint32(scratchSpaceAddress + 4, true); let arrayOffset = memoryDataView.getUint32(scratchSpaceAddress + 8, true); let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }]; + let previousId = 0; for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { let entireRenderCommandMemory = new Uint32Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize)); let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition); let parentElement = scissorStack[scissorStack.length - 1]; let element = null; + let isMultiConfigElement = previousId === renderCommand.id.value; if (!elementCache[renderCommand.id.value]) { let elementType = 'div'; switch (renderCommand.commandType.value) { @@ -357,7 +359,7 @@ let elementData = elementCache[renderCommand.id.value]; element = elementData.element; - if (Array.prototype.indexOf.call(parentElement.element.children, element) !== parentElement.nextElementIndex) { + if (!isMultiConfigElement && Array.prototype.indexOf.call(parentElement.element.children, element) !== parentElement.nextElementIndex) { if (parentElement.nextElementIndex === 0) { parentElement.element.insertAdjacentElement('afterbegin', element); } else { @@ -367,8 +369,12 @@ elementData.exists = true; // Don't get me started. Cheaper to compare the render command memory than to update HTML elements - let dirty = MemoryIsDifferent(elementData.previousMemoryCommand, entireRenderCommandMemory, renderCommandSize); - parentElement.nextElementIndex++; + let dirty = MemoryIsDifferent(elementData.previousMemoryCommand, entireRenderCommandMemory, renderCommandSize) && !isMultiConfigElement; + if (!isMultiConfigElement) { + parentElement.nextElementIndex++; + } + + previousId = renderCommand.id.value; elementData.previousMemoryCommand = entireRenderCommandMemory; let offsetX = scissorStack.length > 0 ? scissorStack[scissorStack.length - 1].nextAllocation.x : 0; diff --git a/examples/clay-official-website/main.c b/examples/clay-official-website/main.c index fee4791..e21ac15 100644 --- a/examples/clay-official-website/main.c +++ b/examples/clay-official-website/main.c @@ -6,7 +6,7 @@ double windowWidth = 1024, windowHeight = 768; float modelPageOneZRotation = 0; -int ACTIVE_RENDERER_INDEX = 0; +uint32_t ACTIVE_RENDERER_INDEX = 0; const uint32_t FONT_ID_BODY_16 = 0; const uint32_t FONT_ID_TITLE_56 = 1; @@ -35,27 +35,27 @@ const Clay_Color COLOR_BLOB_BORDER_3 = (Clay_Color) {225, 138, 50, 255}; const Clay_Color COLOR_BLOB_BORDER_4 = (Clay_Color) {236, 159, 70, 255}; const Clay_Color COLOR_BLOB_BORDER_5 = (Clay_Color) {240, 189, 100, 255}; -#define RAYLIB_VECTOR2_TO_CLAY_VECTOR2(vector) (Clay_Vector2) { .x = vector.x, .y = vector.y } +#define RAYLIB_VECTOR2_TO_CLAY_VECTOR2(vector) (Clay_Vector2) { .x = (vector).x, .y = (vector).y } Clay_TextElementConfig headerTextConfig = (Clay_TextElementConfig) { .fontId = 2, .fontSize = 24, .textColor = {61, 26, 5, 255} }; Clay_TextElementConfig blobTextConfig = (Clay_TextElementConfig) { .fontId = 2, .fontSize = 30, .textColor = {244, 235, 230, 255} }; void LandingPageBlob(int index, int fontSize, Clay_Color color, Clay_String text, Clay_String imageURL) { - CLAY_BORDER_CONTAINER(CLAY_IDI("HeroBlob", index), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 480) }, .padding = {16, 16}, .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(2, color, 10)) { - CLAY_IMAGE(CLAY_IDI("CheckImage", index), CLAY_LAYOUT(.sizing = { CLAY_SIZING_FIXED(32) }), CLAY_IMAGE_CONFIG(.sourceDimensions = { 128, 128 }, .sourceURL = imageURL)) {} - CLAY_TEXT(CLAY_IDI("HeroBlobText", index), text, CLAY_TEXT_CONFIG(.fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color)); + CLAY(CLAY_IDI("HeroBlob", index), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 480 }) }, .padding = {16, 16}, .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }), CLAY_BORDER_OUTSIDE_RADIUS(2, color, 10)) { + CLAY(CLAY_IDI("CheckImage", index), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_FIXED(32) } }), CLAY_IMAGE({ .sourceDimensions = { 128, 128 }, .sourceURL = imageURL })) {} + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color })); } } void LandingPageDesktop() { - CLAY_CONTAINER(CLAY_ID("LandingPage1Desktop"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { .x = 50 })) { - 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("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(CLAY_ID("LandingPage1Desktop"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT({ .min = windowHeight - 70 }) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { .x = 50 } })) { + CLAY(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({ .left = { 2, COLOR_RED }, .right = { 2, COLOR_RED } })) { + CLAY(CLAY_ID("LeftText"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(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(CLAY_ID("LandingPageSpacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) } })) {} + CLAY_TEXT(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)) { + CLAY(CLAY_ID("HeroImageOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_PERCENT(0.45f) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 })) { LandingPageBlob(1, 32, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), CLAY_STRING("/clay/images/check_5.png")); LandingPageBlob(2, 32, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), CLAY_STRING("/clay/images/check_4.png")); LandingPageBlob(3, 32, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), CLAY_STRING("/clay/images/check_3.png")); @@ -67,13 +67,13 @@ void LandingPageDesktop() { } 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("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(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(CLAY_ID("LeftText"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(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(CLAY_ID("LandingPageSpacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) } })) {} + CLAY_TEXT(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)) { + CLAY(CLAY_ID("HeroImageOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW() }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 })) { LandingPageBlob(1, 28, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), CLAY_STRING("/clay/images/check_5.png")); LandingPageBlob(2, 28, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), CLAY_STRING("/clay/images/check_4.png")); LandingPageBlob(3, 28, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), CLAY_STRING("/clay/images/check_3.png")); @@ -84,71 +84,71 @@ void LandingPageMobile() { } void FeatureBlocksDesktop() { - CLAY_CONTAINER(CLAY_ID("FeatureBlocksOuter"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() })) { - CLAY_BORDER_CONTAINER(CLAY_ID("FeatureBlocksInner"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } ), CLAY_BORDER_CONFIG(.betweenChildren = { .width = 2, .color = COLOR_RED })) { - Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED ); - CLAY_CONTAINER(CLAY_ID("HFileBoxOuter"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 32}, .childGap = 8)) { - CLAY_RECTANGLE(CLAY_ID("HFileIncludeOuter"), CLAY_LAYOUT(.padding = {8, 4}), CLAY_RECTANGLE_CONFIG(.color = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8))) { - CLAY_TEXT(CLAY_IDI("HFileBoxText", 2), CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT)); + CLAY(CLAY_ID("FeatureBlocksOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() } })) { + CLAY(CLAY_ID("FeatureBlocksInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }), CLAY_BORDER({ .betweenChildren = { .width = 2, .color = COLOR_RED } })) { + Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED }); + CLAY(CLAY_ID("HFileBoxOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 32}, .childGap = 8 })) { + CLAY(CLAY_ID("HFileIncludeOuter"), CLAY_LAYOUT({ .padding = {8, 4} }), CLAY_RECTANGLE({ .color = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) })) { + CLAY_TEXT(CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT })); } - CLAY_TEXT(CLAY_ID("HFileSecondLine"), CLAY_STRING("~2000 lines of C99."), textConfig); - CLAY_TEXT(CLAY_IDI("HFileBoxText", 5), CLAY_STRING("Zero dependencies, including no C standard library."), textConfig); + CLAY_TEXT(CLAY_STRING("~2000 lines of C99."), textConfig); + CLAY_TEXT(CLAY_STRING("Zero dependencies, including no C standard library."), textConfig); } - CLAY_CONTAINER(CLAY_ID("BringYourOwnRendererOuter"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 50, .y = 32}, .childGap = 8)) { - CLAY_TEXT(CLAY_IDI("ZeroDependenciesText", 1), CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG(.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE)); - CLAY_TEXT(CLAY_IDI("ZeroDependenciesText", 2), CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig); - CLAY_TEXT(CLAY_IDI("ZeroDependenciesText", 3), CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig); + CLAY(CLAY_ID("BringYourOwnRendererOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 50, .y = 32}, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE })); + CLAY_TEXT(CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig); + CLAY_TEXT(CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig); } } } } void FeatureBlocksMobile() { - CLAY_BORDER_CONTAINER(CLAY_ID("FeatureBlocksInner"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() }, ), CLAY_BORDER_CONFIG(.betweenChildren = { .width = 2, .color = COLOR_RED })) { - Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED ); - CLAY_CONTAINER(CLAY_ID("HFileBoxOuter"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 32}, .childGap = 8)) { - CLAY_RECTANGLE(CLAY_ID("HFileIncludeOuter"), CLAY_LAYOUT(.padding = {8, 4}), CLAY_RECTANGLE_CONFIG(.color = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8))) { - CLAY_TEXT(CLAY_IDI("HFileBoxText", 2), CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT)); + CLAY(CLAY_ID("FeatureBlocksInner"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() } }), CLAY_BORDER({ .betweenChildren = { .width = 2, .color = COLOR_RED } })) { + Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED }); + CLAY(CLAY_ID("HFileBoxOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 32}, .childGap = 8 })) { + CLAY(CLAY_ID("HFileIncludeOuter"), CLAY_LAYOUT({ .padding = {8, 4} }), CLAY_RECTANGLE({ .color = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) })) { + CLAY_TEXT(CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT })); } - CLAY_TEXT(CLAY_ID("HFileSecondLine"), CLAY_STRING("~2000 lines of C99."), textConfig); - CLAY_TEXT(CLAY_IDI("HFileBoxText", 5), CLAY_STRING("Zero dependencies, including no C standard library."), textConfig); + CLAY_TEXT(CLAY_STRING("~2000 lines of C99."), textConfig); + CLAY_TEXT(CLAY_STRING("Zero dependencies, including no C standard library."), textConfig); } - CLAY_CONTAINER(CLAY_ID("BringYourOwnRendererOuter"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 16, .y = 32}, .childGap = 8)) { - CLAY_TEXT(CLAY_IDI("ZeroDependenciesText", 1), CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG(.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE)); - CLAY_TEXT(CLAY_IDI("ZeroDependenciesText", 2), CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig); - CLAY_TEXT(CLAY_IDI("ZeroDependenciesText", 3), CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig); + CLAY(CLAY_ID("BringYourOwnRendererOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 16, .y = 32}, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE })); + CLAY_TEXT(CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig); + CLAY_TEXT(CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig); } } } void DeclarativeSyntaxPageDesktop() { - CLAY_CONTAINER(CLAY_ID("SyntaxPageDesktop"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 50})) { - CLAY_BORDER_CONTAINER(CLAY_ID("SyntaxPage"), 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("SyntaxPageLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8)) { - CLAY_TEXT(CLAY_ID("SyntaxPageTextTitle"), CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG(.fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED)); - CLAY_CONTAINER(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) })) {} - CLAY_TEXT(CLAY_ID("SyntaxPageTextSubTitle1"), CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); - CLAY_TEXT(CLAY_ID("SyntaxPageTextSubTitle2"), CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); - 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(CLAY_ID("SyntaxPageDesktop"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT({ .min = windowHeight - 50 }) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 50} })) { + CLAY(CLAY_ID("SyntaxPage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = { 32, 32 }, .childGap = 32 }), CLAY_BORDER({ .left = { 2, COLOR_RED }, .right = { 2, COLOR_RED } })) { + CLAY(CLAY_ID("SyntaxPageLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED })); + CLAY(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) } })) {} + CLAY_TEXT(CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); + CLAY_TEXT(CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); + CLAY_TEXT(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("SyntaxPageRightImageInner"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 568) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png"))) {} + CLAY(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} })) { + CLAY(CLAY_ID("SyntaxPageRightImageInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 568 }) } }), CLAY_IMAGE({ .sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png") })) {} } } } } void DeclarativeSyntaxPageMobile() { - CLAY_CONTAINER(CLAY_ID("SyntaxPageDesktop"), 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 = {16, 32}, .childGap = 16)) { - CLAY_CONTAINER(CLAY_ID("SyntaxPageLeftText"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8)) { - CLAY_TEXT(CLAY_ID("SyntaxPageTextTitle"), CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG(.fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED)); - CLAY_CONTAINER(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 16) })) {} - CLAY_TEXT(CLAY_ID("SyntaxPageTextSubTitle1"), CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); - CLAY_TEXT(CLAY_ID("SyntaxPageTextSubTitle2"), CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), CLAY_TEXT_CONFIG(.fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED)); - 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(CLAY_ID("SyntaxPageDesktop"), 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 = {16, 32}, .childGap = 16 })) { + CLAY(CLAY_ID("SyntaxPageLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED })); + CLAY(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) } })) {} + CLAY_TEXT(CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); + CLAY_TEXT(CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); + CLAY_TEXT(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("SyntaxPageRightImageInner"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 568) }), CLAY_IMAGE_CONFIG(.sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png"))) {} + CLAY(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} })) { + CLAY(CLAY_ID("SyntaxPageRightImageInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 568 }) } }), CLAY_IMAGE({ .sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png") } )) {} } } } @@ -165,21 +165,21 @@ Clay_Color ColorLerp(Clay_Color a, Clay_Color b, float amount) { Clay_String LOREM_IPSUM_TEXT = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); 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("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)); + CLAY(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({ .color = COLOR_RED })) { + CLAY(CLAY_ID("PerformanceLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); + CLAY(CLAY_ID("PerformanceSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) }})) {} + CLAY_TEXT(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_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_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 })); } - CLAY_CONTAINER(CLAY_ID("PerformanceRightImageOuter"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER})) { - CLAY_BORDER_CONTAINER(CLAY_ID(""), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(400) }), CLAY_BORDER_CONFIG_ALL(.width = 2, .color = COLOR_LIGHT)) { - CLAY_RECTANGLE(CLAY_ID("AnimationDemoContainerLeft"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {32, 32}), CLAY_RECTANGLE_CONFIG(.color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue))) { - CLAY_TEXT(CLAY_ID("AnimationDemoTextLeft"), LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); + CLAY(CLAY_ID("PerformanceRightImageOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} })) { + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(400) } }), CLAY_BORDER_ALL({ .width = 2, .color = COLOR_LIGHT })) { + CLAY(CLAY_ID("AnimationDemoContainerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {32, 32} }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) })) { + CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); } - CLAY_RECTANGLE(CLAY_ID("AnimationDemoContainerRight"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {32, 32}), CLAY_RECTANGLE_CONFIG(.color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue))) { - CLAY_TEXT(CLAY_ID("AnimationDemoTextRight"), LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); + CLAY(CLAY_ID("AnimationDemoContainerRight"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {32, 32} }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) })) { + CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); } } } @@ -187,60 +187,70 @@ void HighPerformancePageDesktop(float lerpValue) { } 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("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)); + CLAY(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({ .color = COLOR_RED })) { + CLAY(CLAY_ID("PerformanceLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); + CLAY(CLAY_ID("PerformanceSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) }})) {} + CLAY_TEXT(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_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_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 })); } - CLAY_CONTAINER(CLAY_ID("PerformanceRightImageOuter"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW() }, .childAlignment = {CLAY_ALIGN_X_CENTER})) { - CLAY_BORDER_CONTAINER(CLAY_ID(""), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(400) }), CLAY_BORDER_CONFIG_ALL(.width = 2, .color = COLOR_LIGHT)) { - CLAY_RECTANGLE(CLAY_ID("AnimationDemoContainerLeft"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue))) { - CLAY_TEXT(CLAY_ID("AnimationDemoTextLeft"), LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); + CLAY(CLAY_ID("PerformanceRightImageOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .childAlignment = {CLAY_ALIGN_X_CENTER} })) { + CLAY(CLAY_ID(""), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(400) } }), CLAY_BORDER_ALL({ .width = 2, .color = COLOR_LIGHT })) { + CLAY(CLAY_ID("AnimationDemoContainerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {16, 16} }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) })) { + CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); } - CLAY_RECTANGLE(CLAY_ID("AnimationDemoContainerRight"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {16, 16}), CLAY_RECTANGLE_CONFIG(.color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue))) { - CLAY_TEXT(CLAY_ID("AnimationDemoTextRight"), LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG(.fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT)); + CLAY(CLAY_ID("AnimationDemoContainerRight"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = {16, 16} }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) })) { + CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); } } } } } -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 HandleRendererButtonInteraction(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData) { + if (pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME) { + ACTIVE_RENDERER_INDEX = (uint32_t)userData; } } -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)); - } +void RendererButtonActive(Clay_String text) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(300) }, .padding = {16, 16} }), + CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(10) }) + ) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT })); + } +} + +void RendererButtonInactive(Clay_String text, size_t rendererIndex) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(300)}, .padding = {16, 16} }), + CLAY_BORDER_OUTSIDE_RADIUS(2, COLOR_RED, 10), + CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .cornerRadius = CLAY_CORNER_RADIUS(10), .cursorPointer = true }), + Clay_OnHover(HandleRendererButtonInteraction, rendererIndex) + ) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); } } void RendererPageDesktop() { - CLAY_CONTAINER(CLAY_ID("RendererPageDesktop"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 50})) { - 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("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(CLAY_ID("RendererPageDesktop"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIT({ .min = windowHeight - 50 }) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {.x = 50} })) { + CLAY(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({ .left = { 2, COLOR_RED }, .right = { 2, COLOR_RED } })) { + CLAY(CLAY_ID("RendererLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED })); + CLAY(CLAY_ID("RendererSpacerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) }})) {} + CLAY_TEXT(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_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_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("RendererSpacerRight"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 32) })) {} + CLAY(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_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE })); + CLAY(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")); + RendererButtonActive(CLAY_STRING("HTML Renderer")); + RendererButtonInactive(CLAY_STRING("Canvas Renderer"), 1); } else { - RendererButtonInactive(CLAY_ID("RendererSelectButtonHTML"), 0, CLAY_STRING("HTML Renderer")); - RendererButtonActive(CLAY_IDI("RendererSelectButtonActive", 0), 1, CLAY_STRING("Canvas Renderer")); + RendererButtonInactive(CLAY_STRING("HTML Renderer"), 0); + RendererButtonActive(CLAY_STRING("Canvas Renderer")); } } } @@ -248,40 +258,40 @@ void RendererPageDesktop() { } 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("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(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({ .color = COLOR_LIGHT })) { + CLAY(CLAY_ID("RendererLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED })); + CLAY(CLAY_ID("RendererSpacerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) }})) {} + CLAY_TEXT(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_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_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("RendererSpacerRight"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(.max = 32) })) {} + CLAY(CLAY_ID("RendererRightText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 })) { + CLAY_TEXT(CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE })); + CLAY(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")); + RendererButtonActive(CLAY_STRING("HTML Renderer")); + RendererButtonInactive(CLAY_STRING("Canvas Renderer"), 1); } else { - RendererButtonInactive(CLAY_ID("RendererSelectButtonHTML"), 0, CLAY_STRING("HTML Renderer")); - RendererButtonActive(CLAY_IDI("RendererSelectButtonActive", 0), 1, CLAY_STRING("Canvas Renderer")); + RendererButtonInactive(CLAY_STRING("HTML Renderer"), 0); + RendererButtonActive(CLAY_STRING("Canvas Renderer")); } } } } 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(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({ .color = COLOR_RED })) { + CLAY(CLAY_ID("DebuggerLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) { + CLAY_TEXT(CLAY_STRING("Integrated Debug Tools"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT })); + CLAY(CLAY_ID("DebuggerSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 16 }) }})) {} + CLAY_TEXT(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_STRING("View your layout hierarchy and config in real time."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT })); + CLAY(CLAY_ID("DebuggerPageSpacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(32) } })) {} + CLAY_TEXT(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"))) {} + CLAY(CLAY_ID("DebuggerRightImageOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} })) { + CLAY(CLAY_ID("DebuggerPageRightImageInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW({ .max = 558 }) } }), CLAY_IMAGE({ .sourceDimensions = {1620, 1474}, .sourceURL = CLAY_STRING("/clay/images/debugger.png") })) {} } } } @@ -298,71 +308,76 @@ float animationLerpValue = -1.0f; 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); - CLAY_CONTAINER(CLAY_ID("Spacer"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW() })) {} - + CLAY(CLAY_ID("OuterContainer"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() } }), CLAY_RECTANGLE({ .color = COLOR_LIGHT })) { + CLAY(CLAY_ID("Header"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = { 32 } })) { + CLAY_TEXT(CLAY_STRING("Clay"), &headerTextConfig); + CLAY(CLAY_ID("Spacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW() } })) {} if (!mobileScreen) { - CLAY_RECTANGLE(CLAY_ID("LinkExamplesOuter"), &CLAY_LAYOUT_DEFAULT, CLAY_RECTANGLE_CONFIG(.link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples"), .color = {0,0,0,0})) { - CLAY_TEXT(CLAY_ID("LinkExamplesText"), CLAY_STRING("Examples"), CLAY_TEXT_CONFIG(.disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255})); + CLAY(CLAY_ID("LinkExamplesOuter"), CLAY_LAYOUT({ .padding = {8} }), CLAY_RECTANGLE({ .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples"), .color = {0,0,0,0} })) { + CLAY_TEXT(CLAY_STRING("Examples"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } - CLAY_RECTANGLE(CLAY_ID("LinkDocsOuter"), &CLAY_LAYOUT_DEFAULT, CLAY_RECTANGLE_CONFIG(.link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md"), .color = {0,0,0,0})) { - 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})); + CLAY(CLAY_ID("LinkDocsOuter"), CLAY_LAYOUT({ .padding = {8} }), CLAY_RECTANGLE({ .link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md"), .color = {0,0,0,0} })) { + CLAY_TEXT(CLAY_STRING("Docs"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } } - Clay_ElementId discordButtonId = CLAY_ID("HeaderButtonDiscord"); - CLAY_BORDER_CONTAINER(CLAY_ID("LinkDiscordOuter"), CLAY_LAYOUT(), CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(2, COLOR_RED, 10)) { - CLAY_RECTANGLE(discordButtonId, CLAY_LAYOUT(.padding = {16, 6}), CLAY_RECTANGLE_CONFIG(.cornerRadius = CLAY_CORNER_RADIUS(10), .link = CLAY_STRING("https://discord.gg/b4FTWkxdvT"), .color = Clay_PointerOver(discordButtonId) ? COLOR_LIGHT_HOVER : COLOR_LIGHT)) { - CLAY_TEXT(CLAY_ID("LinkDiscordText"), CLAY_STRING("Discord"), CLAY_TEXT_CONFIG(.disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255})); - } + CLAY(CLAY_LAYOUT({ .padding = {16, 6} }), + CLAY_RECTANGLE({ + .cornerRadius = CLAY_CORNER_RADIUS(10), + .link = CLAY_STRING("https://discord.gg/b4FTWkxdvT"), + .color = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT }), + CLAY_BORDER_OUTSIDE_RADIUS(2, COLOR_RED, 10) + ) { + CLAY_TEXT(CLAY_STRING("Discord"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } - 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})); - } + CLAY(CLAY_LAYOUT({ .padding = {16, 6} }), + CLAY_RECTANGLE({ .cornerRadius = CLAY_CORNER_RADIUS(10), .link = CLAY_STRING("https://github.com/nicbarker/clay"), .color = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT }), + CLAY_BORDER_OUTSIDE_RADIUS(2, COLOR_RED, 10) + ) { + CLAY_TEXT(CLAY_STRING("Github"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } } - CLAY_RECTANGLE(CLAY_ID("TopBorder1"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(4) }), CLAY_RECTANGLE_CONFIG(.color = COLOR_TOP_BORDER_5)) {} - CLAY_RECTANGLE(CLAY_ID("TopBorder2"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(4) }), CLAY_RECTANGLE_CONFIG(.color = COLOR_TOP_BORDER_4)) {} - CLAY_RECTANGLE(CLAY_ID("TopBorder3"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(4) }), CLAY_RECTANGLE_CONFIG(.color = COLOR_TOP_BORDER_3)) {} - CLAY_RECTANGLE(CLAY_ID("TopBorder4"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(4) }), CLAY_RECTANGLE_CONFIG(.color = COLOR_TOP_BORDER_2)) {} - CLAY_RECTANGLE(CLAY_ID("TopBorder5"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(4) }), CLAY_RECTANGLE_CONFIG(.color = COLOR_TOP_BORDER_1)) {} - CLAY_RECTANGLE(CLAY_ID("ScrollContainerBackgroundRectangle"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }), CLAY_RECTANGLE_CONFIG(.color = COLOR_LIGHT)) { - CLAY_SCROLL_CONTAINER(CLAY_ID("OuterScrollContainer"), CLAY_LAYOUT(.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }), CLAY_SCROLL_CONFIG(.vertical = true)) { - CLAY_BORDER_CONTAINER(CLAY_ID("ScrollContainerInner"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW() }), CLAY_BORDER_CONFIG(.betweenChildren = {2, COLOR_RED})) { - if (mobileScreen) { - LandingPageMobile(); - FeatureBlocksMobile(); - DeclarativeSyntaxPageMobile(); - HighPerformancePageMobile(lerpValue); - RendererPageMobile(); - } else { - LandingPageDesktop(); - FeatureBlocksDesktop(); - DeclarativeSyntaxPageDesktop(); - HighPerformancePageDesktop(lerpValue); - RendererPageDesktop(); - DebuggerPageDesktop(); - } - } + Clay_LayoutConfig topBorderConfig = (Clay_LayoutConfig) { .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(4) }}; + CLAY(CLAY_ID("TopBorder1"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_5 })) {} + CLAY(CLAY_ID("TopBorder2"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_4 })) {} + CLAY(CLAY_ID("TopBorder3"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_3 })) {} + CLAY(CLAY_ID("TopBorder4"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_2 })) {} + CLAY(CLAY_ID("TopBorder5"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_1 })) {} + CLAY(CLAY_ID("OuterScrollContainer"), + CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, .layoutDirection = CLAY_TOP_TO_BOTTOM }), + CLAY_SCROLL({ .vertical = true }), + CLAY_BORDER({ .betweenChildren = {2, COLOR_RED} }) + ) { + if (mobileScreen) { + LandingPageMobile(); + FeatureBlocksMobile(); + DeclarativeSyntaxPageMobile(); + HighPerformancePageMobile(lerpValue); + RendererPageMobile(); + } else { + LandingPageDesktop(); + FeatureBlocksDesktop(); + DeclarativeSyntaxPageDesktop(); + HighPerformancePageDesktop(lerpValue); + RendererPageDesktop(); + DebuggerPageDesktop(); } } } if (!mobileScreen) { - Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(CLAY_ID("OuterScrollContainer")); + Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("OuterScrollContainer"))); Clay_Color scrollbarColor = (Clay_Color){225, 138, 50, 120}; if (scrollbarData.mouseDown) { scrollbarColor = (Clay_Color){225, 138, 50, 200}; - } else if (Clay_PointerOver(CLAY_ID("ScrollBar"))) { + } else if (Clay_PointerOver(Clay_GetElementId(CLAY_STRING("ScrollBar")))) { 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").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)) {} - } + CLAY(CLAY_ID("ScrollBar"), + CLAY_FLOATING({ .offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("OuterScrollContainer")).id, .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }}), + CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(10), CLAY_SIZING_FIXED((scrollHeight / scrollData.contentDimensions.height) * scrollHeight)} }), + CLAY_RECTANGLE({ .cornerRadius = CLAY_CORNER_RADIUS(5), .color = scrollbarColor }) + ) {} } return Clay_EndLayout(); } @@ -385,14 +400,6 @@ CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(floa Clay_SetDebugModeEnabled(debugModeEnabled); } - if (isTouchDown || isMouseDown) { - if (Clay_PointerOver(CLAY_ID("RendererSelectButtonHTML"))) { - ACTIVE_RENDERER_INDEX = 0; - } else if (Clay_PointerOver(CLAY_ID("RendererSelectButtonCanvas"))) { - ACTIVE_RENDERER_INDEX = 1; - } - } - Clay__debugViewHighlightColor = (Clay_Color) {105,210,231, 120}; Clay_SetPointerState((Clay_Vector2) {mousePositionX, mousePositionY}, isMouseDown || isTouchDown); @@ -401,13 +408,13 @@ CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(floa scrollbarData.mouseDown = false; } - if (isMouseDown && !scrollbarData.mouseDown && Clay_PointerOver(CLAY_ID("ScrollBar"))) { - Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("OuterScrollContainer")); + if (isMouseDown && !scrollbarData.mouseDown && Clay_PointerOver(Clay_GetElementId(CLAY_STRING("ScrollBar")))) { + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("OuterScrollContainer"))); scrollbarData.clickOrigin = (Clay_Vector2) { mousePositionX, mousePositionY }; scrollbarData.positionOrigin = *scrollContainerData.scrollPosition; scrollbarData.mouseDown = true; } else if (scrollbarData.mouseDown) { - Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("OuterScrollContainer")); + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("OuterScrollContainer"))); if (scrollContainerData.contentDimensions.height > 0) { Clay_Vector2 ratio = (Clay_Vector2) { scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width, @@ -423,12 +430,12 @@ CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(floa } if (arrowKeyDownPressedThisFrame) { - Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("OuterScrollContainer")); + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("OuterScrollContainer"))); if (scrollContainerData.contentDimensions.height > 0) { scrollContainerData.scrollPosition->y = scrollContainerData.scrollPosition->y - 50; } } else if (arrowKeyUpPressedThisFrame) { - Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("OuterScrollContainer")); + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("OuterScrollContainer"))); if (scrollContainerData.contentDimensions.height > 0) { scrollContainerData.scrollPosition->y = scrollContainerData.scrollPosition->y + 50; } diff --git a/examples/cpp-project-example/CMakeLists.txt b/examples/cpp-project-example/CMakeLists.txt index 6ebaae7..8c6a754 100644 --- a/examples/cpp-project-example/CMakeLists.txt +++ b/examples/cpp-project-example/CMakeLists.txt @@ -8,5 +8,5 @@ add_executable(clay_examples_cpp_project_example main.cpp) target_include_directories(clay_examples_cpp_project_example PUBLIC .) -set(CMAKE_CXX_FLAGS_DEBUG "-Wall") +set(CMAKE_CXX_FLAGS_DEBUG "-Werror -Wall") set(CMAKE_CXX_FLAGS_RELEASE "-O3") \ No newline at end of file diff --git a/examples/cpp-project-example/main.cpp b/examples/cpp-project-example/main.cpp index 56062bd..8c26fd7 100644 --- a/examples/cpp-project-example/main.cpp +++ b/examples/cpp-project-example/main.cpp @@ -2,9 +2,14 @@ #define CLAY_IMPLEMENTATION #include "../../clay.h" +Clay_LayoutConfig layoutElement = Clay_LayoutConfig { .padding = {5} }; + int main(void) { uint64_t totalMemorySize = Clay_MinMemorySize(); Clay_Arena clayMemory = Clay_Arena { .label = CLAY_STRING("Clay Memory Arena"), .capacity = totalMemorySize, .memory = (char *)malloc(totalMemorySize) }; Clay_Initialize(clayMemory, Clay_Dimensions {1024,768}); + Clay_BeginLayout(); + CLAY(CLAY_RECTANGLE({ .color = {255,255,255,0} }), CLAY_LAYOUT(layoutElement)) {} + Clay_EndLayout(); return 0; } diff --git a/examples/raylib-sidebar-scrolling-container/CMakeLists.txt b/examples/raylib-sidebar-scrolling-container/CMakeLists.txt index f93f628..0e73526 100644 --- a/examples/raylib-sidebar-scrolling-container/CMakeLists.txt +++ b/examples/raylib-sidebar-scrolling-container/CMakeLists.txt @@ -20,7 +20,7 @@ FetchContent_MakeAvailable(raylib) add_executable(clay_examples_raylib_sidebar_scrolling_container main.c multi-compilation-unit.c) -target_compile_options(clay_examples_raylib_sidebar_scrolling_container PUBLIC -Wall -Werror -Wno-unknown-pragmas) +target_compile_options(clay_examples_raylib_sidebar_scrolling_container PUBLIC -DCLAY_DEBUG -DCLAY_OVERFLOW_TRAP -Wall -Werror -Wno-unknown-pragmas) 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 bef69d3..832eb87 100644 --- a/examples/raylib-sidebar-scrolling-container/main.c +++ b/examples/raylib-sidebar-scrolling-container/main.c @@ -13,11 +13,18 @@ Texture2D profilePicture; Clay_String profileText = CLAY_STRING("Profile Page one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen"); Clay_TextElementConfig headerTextConfig = (Clay_TextElementConfig) { .fontId = 1, .fontSize = 16, .textColor = {0,0,0,255} }; +void HandleHeaderButtonInteraction(Clay_ElementId elementId, Clay_PointerInfo pointerInfo, intptr_t userData) { + if (pointerInfo.state == CLAY_POINTER_INFO_PRESSED_THIS_FRAME) { + // Do some click handling + } +} + // Examples of re-usable "Components" -void RenderHeaderButton(uint16_t index, Clay_String text) { - 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); +void RenderHeaderButton(Clay_String text) { + CLAY(CLAY_LAYOUT({ .padding = {16, 8} }), + CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }), + Clay_OnHover(HandleHeaderButtonInteraction, 1)) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG(headerTextConfig)); } } @@ -26,93 +33,104 @@ Clay_RectangleElementConfig dropdownRectangleConfig = (Clay_RectangleElementConf Clay_TextElementConfig dropdownTextElementConfig = (Clay_TextElementConfig) { .fontSize = 24, .textColor = {255,255,255,255} }; 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(CLAY_IDI("ScrollContainerItem", index), CLAY_LAYOUT(dropdownTextItemLayout), CLAY_RECTANGLE(dropdownRectangleConfig)) { + CLAY_TEXT(CLAY_STRING("I'm a text field in a scroll container."), &dropdownTextElementConfig); } } Clay_RenderCommandArray CreateLayout() { 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})) { - 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"), profileText, CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {0, 0, 0, 255})); + CLAY(CLAY_ID("OuterContainer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, .padding = { 16, 16 }, .childGap = 16 }), CLAY_RECTANGLE({ .color = {200, 200, 200, 255} })) { + CLAY(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({ .color = {150, 150, 255, 255} })) { + CLAY(CLAY_ID("ProfilePictureOuter"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW() }, .padding = { 8, 8 }, .childGap = 8, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }), CLAY_RECTANGLE({ .color = {130, 130, 255, 255} })) { + CLAY(CLAY_ID("ProfilePicture"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) } }), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {60, 60} })) {} + CLAY_TEXT(profileText, CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0, 0, 0, 255} })); } - CLAY_RECTANGLE(CLAY_ID("SidebarBlob1"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }), CLAY_RECTANGLE_CONFIG(.color = {110, 110, 255, 255})) {} - CLAY_RECTANGLE(CLAY_ID("SidebarBlob2"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }), CLAY_RECTANGLE_CONFIG(.color = {110, 110, 255, 255})) {} - CLAY_RECTANGLE(CLAY_ID("SidebarBlob3"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }), CLAY_RECTANGLE_CONFIG(.color = {110, 110, 255, 255})) {} - CLAY_RECTANGLE(CLAY_ID("SidebarBlob4"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }), CLAY_RECTANGLE_CONFIG(.color = {110, 110, 255, 255})) {} + CLAY(CLAY_ID("SidebarBlob1"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }}), CLAY_RECTANGLE({ .color = {110, 110, 255, 255} })) {} + CLAY(CLAY_ID("SidebarBlob2"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }}), CLAY_RECTANGLE({ .color = {110, 110, 255, 255} })) {} + CLAY(CLAY_ID("SidebarBlob3"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }}), CLAY_RECTANGLE({ .color = {110, 110, 255, 255} })) {} + CLAY(CLAY_ID("SidebarBlob4"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(50) }}), CLAY_RECTANGLE({ .color = {110, 110, 255, 255} })) {} } // - CLAY_CONTAINER(CLAY_ID("RightPanel"), CLAY_LAYOUT(.layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW() }, .childGap = 16)) { - CLAY_RECTANGLE(CLAY_ID("HeaderBar"), CLAY_LAYOUT(.sizing = { .width = CLAY_SIZING_GROW() }, .childAlignment = { .x = CLAY_ALIGN_X_RIGHT }, .padding = {8, 8}, .childGap = 8), CLAY_RECTANGLE_CONFIG(.color = {180, 180, 180, 255})) { - RenderHeaderButton(1, CLAY_STRING("Header Item 1")); - RenderHeaderButton(2, CLAY_STRING("Header Item 2")); - RenderHeaderButton(3, CLAY_STRING("Header Item 3")); + CLAY(CLAY_ID("RightPanel"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, .childGap = 16 })) { + CLAY(CLAY_ID("HeaderBar"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW() }, .childAlignment = { .x = CLAY_ALIGN_X_RIGHT }, .padding = {8, 8}, .childGap = 8 }), CLAY_RECTANGLE({ .color = {180, 180, 180, 255} })) { + RenderHeaderButton(CLAY_STRING("Header Item 1")); + RenderHeaderButton(CLAY_STRING("Header Item 2")); + RenderHeaderButton(CLAY_STRING("Header Item 3")); } - 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) }), 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})); - } - } - } - - CLAY_TEXT(CLAY_ID("BodyText1"), - 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("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"), - CLAY_STRING("Faucibus purus in massa tempor nec. Nec ullamcorper sit amet risus nullam eget felis eget nunc. Diam vulputate ut pharetra sit amet aliquam id diam. Lacus suspendisse faucibus interdum posuere lorem. A diam sollicitudin tempor id. Amet massa vitae tortor condimentum lacinia. Aliquet nibh praesent tristique magna."), - CLAY_TEXT_CONFIG(.fontSize = 24, .lineHeight = 60, .textColor = {0,0,0,255})); - - CLAY_TEXT(CLAY_ID("BodyText3"), - CLAY_STRING("Suspendisse in est ante in nibh. Amet venenatis urna cursus eget nunc scelerisque viverra. Elementum sagittis vitae et leo duis ut diam quam nulla. Enim nulla aliquet porttitor lacus. Pellentesque habitant morbi tristique senectus et. Facilisi nullam vehicula ipsum a arcu cursus vitae.\nSem fringilla ut morbi tincidunt. Euismod quis viverra nibh cras pulvinar mattis nunc sed. Velit sed ullamcorper morbi tincidunt ornare massa. Varius quam quisque id diam vel quam. Nulla pellentesque dignissim enim sit amet venenatis. Enim lobortis scelerisque fermentum dui faucibus in. Pretium viverra suspendisse potenti nullam ac tortor vitae. Lectus vestibulum mattis ullamcorper velit sed. Eget mauris pharetra et ultrices neque ornare aenean euismod elementum. Habitant morbi tristique senectus et. Integer vitae justo eget magna fermentum iaculis eu. Semper quis lectus nulla at volutpat diam. Enim praesent elementum facilisis leo. Massa vitae tortor condimentum lacinia quis vel."), - CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {0,0,0,255})); - - 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("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})) {} - } -// - CLAY_TEXT(CLAY_ID("BodyText4"), - CLAY_STRING("Amet cursus sit amet dictum sit amet justo donec. Et malesuada fames ac turpis egestas maecenas. A lacus vestibulum sed arcu non odio euismod lacinia. Gravida neque convallis a cras. Dui nunc mattis enim ut tellus elementum sagittis vitae et. Orci sagittis eu volutpat odio facilisis mauris. Neque gravida in fermentum et sollicitudin ac orci. Ultrices dui sapien eget mi proin sed libero. Euismod quis viverra nibh cras pulvinar mattis. Diam volutpat commodo sed egestas egestas. In fermentum posuere urna nec tincidunt praesent semper. Integer eget aliquet nibh praesent tristique magna.\nId cursus metus aliquam eleifend mi in. Sed pulvinar proin gravida hendrerit lectus a. Etiam tempor orci eu lobortis elementum nibh tellus. Nullam vehicula ipsum a arcu cursus vitae. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Condimentum lacinia quis vel eros donec ac odio. Mattis pellentesque id nibh tortor id aliquet lectus. Turpis egestas integer eget aliquet nibh praesent tristique. Porttitor massa id neque aliquam vestibulum morbi. Mauris commodo quis imperdiet massa tincidunt nunc pulvinar sapien et. Nunc scelerisque viverra mauris in aliquam sem fringilla. Suspendisse ultrices gravida dictum fusce ut placerat orci nulla.\nLacus laoreet non curabitur gravida arcu ac tortor dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar. Tristique senectus et netus et malesuada fames ac. Nunc aliquet bibendum enim facilisis gravida. Egestas maecenas pharetra convallis posuere morbi leo urna molestie. Sapien nec sagittis aliquam malesuada bibendum arcu vitae elementum curabitur. Ac turpis egestas maecenas pharetra convallis posuere morbi leo urna. Viverra vitae congue eu consequat. Aliquet enim tortor at auctor urna. Ornare massa eget egestas purus viverra accumsan in nisl nisi. Elit pellentesque habitant morbi tristique senectus et netus et malesuada.\nSuspendisse ultrices gravida dictum fusce ut placerat orci nulla pellentesque. Lobortis feugiat vivamus at augue eget arcu. Vitae justo eget magna fermentum iaculis eu. Gravida rutrum quisque non tellus orci. Ipsum faucibus vitae aliquet nec. Nullam non nisi est sit amet. Nunc consequat interdum varius sit amet mattis vulputate enim. Sem fringilla ut morbi tincidunt augue interdum. Vitae purus faucibus ornare suspendisse. Massa tincidunt nunc pulvinar sapien et. Fringilla ut morbi tincidunt augue interdum velit euismod in. Donec massa sapien faucibus et. Est placerat in egestas erat imperdiet. Gravida rutrum quisque non tellus. Morbi non arcu risus quis varius quam quisque id diam. Habitant morbi tristique senectus et netus et malesuada fames ac. Eget lorem dolor sed viverra.\nOrnare massa eget egestas purus viverra. Varius vel pharetra vel turpis nunc eget lorem. Consectetur purus ut faucibus pulvinar elementum. Placerat in egestas erat imperdiet sed euismod nisi. Interdum velit euismod in pellentesque massa placerat duis ultricies lacus. Aliquam nulla facilisi cras fermentum odio eu. Est pellentesque elit ullamcorper dignissim cras tincidunt. Nunc sed id semper risus in hendrerit gravida rutrum. A pellentesque sit amet porttitor eget dolor morbi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Nisl nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Sed id semper risus in hendrerit gravida. Tincidunt praesent semper feugiat nibh. Aliquet lectus proin nibh nisl condimentum id venenatis a. Enim sit amet venenatis urna cursus eget. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Lacinia quis vel eros donec ac odio tempor orci. Donec pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu. Erat pellentesque adipiscing commodo elit at.\nEgestas sed sed risus pretium quam vulputate. Vitae congue mauris rhoncus aenean vel elit scelerisque mauris pellentesque. Aliquam malesuada bibendum arcu vitae elementum. Congue mauris rhoncus aenean vel elit scelerisque mauris. Pellentesque dignissim enim sit amet venenatis urna cursus. Et malesuada fames ac turpis egestas sed tempus urna. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Nibh cras pulvinar mattis nunc sed blandit libero. Fringilla est ullamcorper eget nulla facilisi etiam dignissim. Aenean euismod elementum nisi quis eleifend quam adipiscing vitae proin. Mauris pharetra et ultrices neque ornare aenean euismod elementum. Ornare quam viverra orci sagittis eu. Odio ut sem nulla pharetra diam sit amet nisl suscipit. Ornare lectus sit amet est. Ullamcorper sit amet risus nullam eget. Tincidunt lobortis feugiat vivamus at augue eget arcu dictum.\nUrna nec tincidunt praesent semper feugiat nibh. Ut venenatis tellus in metus vulputate eu scelerisque felis. Cursus risus at ultrices mi tempus. In pellentesque massa placerat duis ultricies lacus sed turpis. Platea dictumst quisque sagittis purus. Cras adipiscing enim eu turpis egestas. Egestas sed tempus urna et pharetra pharetra. Netus et malesuada fames ac turpis egestas integer eget aliquet. Ac turpis egestas sed tempus. Sed lectus vestibulum mattis ullamcorper velit sed. Ante metus dictum at tempor commodo ullamcorper a. Augue neque gravida in fermentum et sollicitudin ac. Praesent semper feugiat nibh sed pulvinar proin gravida. Metus aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices. Neque gravida in fermentum et sollicitudin ac orci phasellus egestas.\nRidiculus mus mauris vitae ultricies. Morbi quis commodo odio aenean. Duis ultricies lacus sed turpis. Non pulvinar neque laoreet suspendisse interdum consectetur. Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Volutpat est velit egestas dui id ornare arcu odio ut. Viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est. Vestibulum lectus mauris ultrices eros. Sed blandit libero volutpat sed cras ornare. Id leo in vitae turpis massa sed elementum tempus. Gravida dictum fusce ut placerat orci nulla pellentesque. Pretium quam vulputate dignissim suspendisse in. Nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Risus viverra adipiscing at in tellus. Turpis nunc eget lorem dolor sed viverra ipsum. Senectus et netus et malesuada fames ac. Habitasse platea dictumst vestibulum rhoncus est. Nunc sed id semper risus in hendrerit gravida. Felis eget velit aliquet sagittis id. Eget felis eget nunc lobortis.\nMaecenas pharetra convallis posuere morbi leo. Maecenas volutpat blandit aliquam etiam. A condimentum vitae sapien pellentesque habitant morbi tristique senectus et. Pulvinar mattis nunc sed blandit libero volutpat sed. Feugiat in ante metus dictum at tempor commodo ullamcorper. Vel pharetra vel turpis nunc eget lorem dolor. Est placerat in egestas erat imperdiet sed euismod. Quisque non tellus orci ac auctor augue mauris augue. Placerat vestibulum lectus mauris ultrices eros in cursus turpis. Enim nunc faucibus a pellentesque sit. Adipiscing vitae proin sagittis nisl. Iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Aliquam sem fringilla ut morbi.\nArcu odio ut sem nulla pharetra diam sit amet nisl. Non diam phasellus vestibulum lorem sed. At erat pellentesque adipiscing commodo elit at. Lacus luctus accumsan tortor posuere ac ut consequat. Et malesuada fames ac turpis egestas integer. Tristique magna sit amet purus. A condimentum vitae sapien pellentesque habitant. Quis varius quam quisque id diam vel quam. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Augue interdum velit euismod in pellentesque massa. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant. Vulputate eu scelerisque felis imperdiet. Nibh tellus molestie nunc non blandit massa. Velit euismod in pellentesque massa placerat. Sed cras ornare arcu dui. Ut sem viverra aliquet eget sit. Eu lobortis elementum nibh tellus molestie nunc non. Blandit libero volutpat sed cras ornare arcu dui vivamus.\nSit amet aliquam id diam maecenas. Amet risus nullam eget felis eget nunc lobortis mattis aliquam. Magna sit amet purus gravida. Egestas purus viverra accumsan in nisl nisi. Leo duis ut diam quam. Ante metus dictum at tempor commodo ullamcorper. Ac turpis egestas integer eget. Fames ac turpis egestas integer eget aliquet nibh. Sem integer vitae justo eget magna fermentum. Semper auctor neque vitae tempus quam pellentesque nec nam aliquam. Vestibulum mattis ullamcorper velit sed. Consectetur adipiscing elit duis tristique sollicitudin nibh. Massa id neque aliquam vestibulum morbi blandit cursus risus.\nCursus sit amet dictum sit amet justo donec enim diam. Egestas erat imperdiet sed euismod. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Habitasse platea dictumst vestibulum rhoncus est pellentesque elit. Duis ultricies lacus sed turpis tincidunt id aliquet risus feugiat. Faucibus ornare suspendisse sed nisi lacus sed viverra. Pretium fusce id velit ut tortor pretium viverra. Fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel. Senectus et netus et malesuada. Tellus pellentesque eu tincidunt tortor aliquam. Aenean sed adipiscing diam donec adipiscing tristique risus nec feugiat. Quis vel eros donec ac odio. Id interdum velit laoreet id donec ultrices tincidunt.\nMassa id neque aliquam vestibulum morbi blandit cursus risus at. Enim tortor at auctor urna nunc id cursus metus. Lorem ipsum dolor sit amet consectetur. At quis risus sed vulputate odio. Facilisis mauris sit amet massa vitae tortor condimentum lacinia quis. Et malesuada fames ac turpis egestas maecenas. Bibendum arcu vitae elementum curabitur vitae nunc sed velit dignissim. Viverra orci sagittis eu volutpat odio facilisis mauris. Adipiscing bibendum est ultricies integer quis auctor elit sed. Neque viverra justo nec ultrices dui sapien. Elementum nibh tellus molestie nunc non blandit massa enim. Euismod elementum nisi quis eleifend quam adipiscing vitae proin sagittis. Faucibus ornare suspendisse sed nisi. Quis viverra nibh cras pulvinar mattis nunc sed blandit. Tristique senectus et netus et. Magnis dis parturient montes nascetur ridiculus mus.\nDolor magna eget est lorem ipsum dolor. Nibh sit amet commodo nulla. Donec pretium vulputate sapien nec sagittis aliquam malesuada. Cras adipiscing enim eu turpis egestas pretium. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Mus mauris vitae ultricies leo integer. In nulla posuere sollicitudin aliquam ultrices sagittis orci. Quis hendrerit dolor magna eget. Nisl tincidunt eget nullam non. Vitae congue eu consequat ac felis donec et odio. Vivamus at augue eget arcu dictum varius duis at. Ornare quam viverra orci sagittis.\nErat nam at lectus urna duis convallis. Massa placerat duis ultricies lacus sed turpis tincidunt id aliquet. Est ullamcorper eget nulla facilisi etiam dignissim diam. Arcu vitae elementum curabitur vitae nunc sed velit dignissim sodales. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus. Neque viverra justo nec ultrices dui sapien eget mi proin. Viverra accumsan in nisl nisi scelerisque eu ultrices. Consequat interdum varius sit amet mattis. In aliquam sem fringilla ut morbi. Eget arcu dictum varius duis at. Nulla aliquet porttitor lacus luctus accumsan tortor posuere. Arcu bibendum at varius vel pharetra vel turpis. Hac habitasse platea dictumst quisque sagittis purus sit amet. Sapien eget mi proin sed libero enim sed. Quam elementum pulvinar etiam non quam lacus suspendisse faucibus interdum. Semper viverra nam libero justo. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Et malesuada fames ac turpis egestas maecenas pharetra convallis posuere.\nTurpis egestas sed tempus urna et pharetra pharetra massa. Gravida in fermentum et sollicitudin ac orci phasellus. Ornare suspendisse sed nisi lacus sed viverra tellus in. Fames ac turpis egestas maecenas pharetra convallis posuere. Mi proin sed libero enim sed faucibus turpis. Sit amet mauris commodo quis imperdiet massa tincidunt nunc. Ut etiam sit amet nisl purus in mollis nunc. Habitasse platea dictumst quisque sagittis purus sit amet volutpat consequat. Eget aliquet nibh praesent tristique magna. Sit amet est placerat in egestas erat. Commodo sed egestas egestas fringilla. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper. Dignissim convallis aenean et tortor at risus viverra. Morbi blandit cursus risus at ultrices mi. Ac turpis egestas integer eget aliquet nibh praesent tristique magna.\nVolutpat sed cras ornare arcu dui. Egestas erat imperdiet sed euismod nisi porta lorem mollis aliquam. Viverra justo nec ultrices dui sapien. Amet risus nullam eget felis eget nunc lobortis. Metus aliquam eleifend mi in. Ut eu sem integer vitae. Auctor elit sed vulputate mi sit amet. Nisl nisi scelerisque eu ultrices. Dictum fusce ut placerat orci nulla. Pellentesque habitant morbi tristique senectus et. Auctor elit sed vulputate mi sit. Tincidunt arcu non sodales neque. Mi in nulla posuere sollicitudin aliquam. Morbi non arcu risus quis varius quam quisque id diam. Cras adipiscing enim eu turpis egestas pretium aenean pharetra magna. At auctor urna nunc id cursus metus aliquam. Mauris a diam maecenas sed enim ut sem viverra. Nunc scelerisque viverra mauris in. In iaculis nunc sed augue lacus viverra vitae congue eu. Volutpat blandit aliquam etiam erat velit scelerisque in dictum non."), - CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {0,0,0,255})); + CLAY(CLAY_ID("MainContent"), + CLAY_SCROLL({ .vertical = true }), + CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {16, 16}, .childGap = 16, .sizing = { CLAY_SIZING_GROW() } }), + CLAY_RECTANGLE({ .color = {200, 200, 255, 255} })) + { + CLAY(CLAY_ID("FloatingContainer"), + CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = { 16, 16 }}), + CLAY_FLOATING({ .zIndex = 1, .attachment = { CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP }, .offset = {0, -16} }), + CLAY_BORDER_OUTSIDE({ .color = {80, 80, 80, 255}, .width = 2 }), + CLAY_RECTANGLE({ .color = {140,80, 200, 200 }}) + ) { + CLAY_TEXT(CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255,255,255,255} })); } + + CLAY_TEXT(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(CLAY_ID("Photos2"), CLAY_LAYOUT({ .childGap = 16, .padding = { 16, 16 }}), CLAY_RECTANGLE({ .color = {180, 180, 220, 255} })) { + CLAY(CLAY_ID("Picture4"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {120, 120} })) {} + CLAY(CLAY_ID("Picture5"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {120, 120} })) {} + CLAY(CLAY_ID("Picture6"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {120, 120} })) {} + } + + CLAY_TEXT(CLAY_STRING("Faucibus purus in massa tempor nec. Nec ullamcorper sit amet risus nullam eget felis eget nunc. Diam vulputate ut pharetra sit amet aliquam id diam. Lacus suspendisse faucibus interdum posuere lorem. A diam sollicitudin tempor id. Amet massa vitae tortor condimentum lacinia. Aliquet nibh praesent tristique magna."), + CLAY_TEXT_CONFIG({ .fontSize = 24, .lineHeight = 60, .textColor = {0,0,0,255} })); + + CLAY_TEXT(CLAY_STRING("Suspendisse in est ante in nibh. Amet venenatis urna cursus eget nunc scelerisque viverra. Elementum sagittis vitae et leo duis ut diam quam nulla. Enim nulla aliquet porttitor lacus. Pellentesque habitant morbi tristique senectus et. Facilisi nullam vehicula ipsum a arcu cursus vitae.\nSem fringilla ut morbi tincidunt. Euismod quis viverra nibh cras pulvinar mattis nunc sed. Velit sed ullamcorper morbi tincidunt ornare massa. Varius quam quisque id diam vel quam. Nulla pellentesque dignissim enim sit amet venenatis. Enim lobortis scelerisque fermentum dui faucibus in. Pretium viverra suspendisse potenti nullam ac tortor vitae. Lectus vestibulum mattis ullamcorper velit sed. Eget mauris pharetra et ultrices neque ornare aenean euismod elementum. Habitant morbi tristique senectus et. Integer vitae justo eget magna fermentum iaculis eu. Semper quis lectus nulla at volutpat diam. Enim praesent elementum facilisis leo. Massa vitae tortor condimentum lacinia quis vel."), + CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0,0,0,255} })); + + CLAY(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({ .color = {180, 180, 220, 255} })) { + CLAY(CLAY_ID("Picture2"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {120, 120} })) {} + CLAY(CLAY_ID("Picture1"), CLAY_LAYOUT({ .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .padding = {8, 8} }), CLAY_RECTANGLE({ .color = {170, 170, 220, 255} })) { + CLAY(CLAY_ID("ProfilePicture2"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {60, 60} })) {} + CLAY_TEXT(CLAY_STRING("Image caption below"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0,0,0,255} })); + } + CLAY(CLAY_ID("Picture3"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(120), .height = CLAY_SIZING_FIXED(120) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {120, 120} })) {} + } + + CLAY_TEXT(CLAY_STRING("Amet cursus sit amet dictum sit amet justo donec. Et malesuada fames ac turpis egestas maecenas. A lacus vestibulum sed arcu non odio euismod lacinia. Gravida neque convallis a cras. Dui nunc mattis enim ut tellus elementum sagittis vitae et. Orci sagittis eu volutpat odio facilisis mauris. Neque gravida in fermentum et sollicitudin ac orci. Ultrices dui sapien eget mi proin sed libero. Euismod quis viverra nibh cras pulvinar mattis. Diam volutpat commodo sed egestas egestas. In fermentum posuere urna nec tincidunt praesent semper. Integer eget aliquet nibh praesent tristique magna.\nId cursus metus aliquam eleifend mi in. Sed pulvinar proin gravida hendrerit lectus a. Etiam tempor orci eu lobortis elementum nibh tellus. Nullam vehicula ipsum a arcu cursus vitae. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Condimentum lacinia quis vel eros donec ac odio. Mattis pellentesque id nibh tortor id aliquet lectus. Turpis egestas integer eget aliquet nibh praesent tristique. Porttitor massa id neque aliquam vestibulum morbi. Mauris commodo quis imperdiet massa tincidunt nunc pulvinar sapien et. Nunc scelerisque viverra mauris in aliquam sem fringilla. Suspendisse ultrices gravida dictum fusce ut placerat orci nulla.\nLacus laoreet non curabitur gravida arcu ac tortor dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar. Tristique senectus et netus et malesuada fames ac. Nunc aliquet bibendum enim facilisis gravida. Egestas maecenas pharetra convallis posuere morbi leo urna molestie. Sapien nec sagittis aliquam malesuada bibendum arcu vitae elementum curabitur. Ac turpis egestas maecenas pharetra convallis posuere morbi leo urna. Viverra vitae congue eu consequat. Aliquet enim tortor at auctor urna. Ornare massa eget egestas purus viverra accumsan in nisl nisi. Elit pellentesque habitant morbi tristique senectus et netus et malesuada.\nSuspendisse ultrices gravida dictum fusce ut placerat orci nulla pellentesque. Lobortis feugiat vivamus at augue eget arcu. Vitae justo eget magna fermentum iaculis eu. Gravida rutrum quisque non tellus orci. Ipsum faucibus vitae aliquet nec. Nullam non nisi est sit amet. Nunc consequat interdum varius sit amet mattis vulputate enim. Sem fringilla ut morbi tincidunt augue interdum. Vitae purus faucibus ornare suspendisse. Massa tincidunt nunc pulvinar sapien et. Fringilla ut morbi tincidunt augue interdum velit euismod in. Donec massa sapien faucibus et. Est placerat in egestas erat imperdiet. Gravida rutrum quisque non tellus. Morbi non arcu risus quis varius quam quisque id diam. Habitant morbi tristique senectus et netus et malesuada fames ac. Eget lorem dolor sed viverra.\nOrnare massa eget egestas purus viverra. Varius vel pharetra vel turpis nunc eget lorem. Consectetur purus ut faucibus pulvinar elementum. Placerat in egestas erat imperdiet sed euismod nisi. Interdum velit euismod in pellentesque massa placerat duis ultricies lacus. Aliquam nulla facilisi cras fermentum odio eu. Est pellentesque elit ullamcorper dignissim cras tincidunt. Nunc sed id semper risus in hendrerit gravida rutrum. A pellentesque sit amet porttitor eget dolor morbi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Nisl nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Sed id semper risus in hendrerit gravida. Tincidunt praesent semper feugiat nibh. Aliquet lectus proin nibh nisl condimentum id venenatis a. Enim sit amet venenatis urna cursus eget. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Lacinia quis vel eros donec ac odio tempor orci. Donec pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu. Erat pellentesque adipiscing commodo elit at.\nEgestas sed sed risus pretium quam vulputate. Vitae congue mauris rhoncus aenean vel elit scelerisque mauris pellentesque. Aliquam malesuada bibendum arcu vitae elementum. Congue mauris rhoncus aenean vel elit scelerisque mauris. Pellentesque dignissim enim sit amet venenatis urna cursus. Et malesuada fames ac turpis egestas sed tempus urna. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim. Nibh cras pulvinar mattis nunc sed blandit libero. Fringilla est ullamcorper eget nulla facilisi etiam dignissim. Aenean euismod elementum nisi quis eleifend quam adipiscing vitae proin. Mauris pharetra et ultrices neque ornare aenean euismod elementum. Ornare quam viverra orci sagittis eu. Odio ut sem nulla pharetra diam sit amet nisl suscipit. Ornare lectus sit amet est. Ullamcorper sit amet risus nullam eget. Tincidunt lobortis feugiat vivamus at augue eget arcu dictum.\nUrna nec tincidunt praesent semper feugiat nibh. Ut venenatis tellus in metus vulputate eu scelerisque felis. Cursus risus at ultrices mi tempus. In pellentesque massa placerat duis ultricies lacus sed turpis. Platea dictumst quisque sagittis purus. Cras adipiscing enim eu turpis egestas. Egestas sed tempus urna et pharetra pharetra. Netus et malesuada fames ac turpis egestas integer eget aliquet. Ac turpis egestas sed tempus. Sed lectus vestibulum mattis ullamcorper velit sed. Ante metus dictum at tempor commodo ullamcorper a. Augue neque gravida in fermentum et sollicitudin ac. Praesent semper feugiat nibh sed pulvinar proin gravida. Metus aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices. Neque gravida in fermentum et sollicitudin ac orci phasellus egestas.\nRidiculus mus mauris vitae ultricies. Morbi quis commodo odio aenean. Duis ultricies lacus sed turpis. Non pulvinar neque laoreet suspendisse interdum consectetur. Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Volutpat est velit egestas dui id ornare arcu odio ut. Viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est. Vestibulum lectus mauris ultrices eros. Sed blandit libero volutpat sed cras ornare. Id leo in vitae turpis massa sed elementum tempus. Gravida dictum fusce ut placerat orci nulla pellentesque. Pretium quam vulputate dignissim suspendisse in. Nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Risus viverra adipiscing at in tellus. Turpis nunc eget lorem dolor sed viverra ipsum. Senectus et netus et malesuada fames ac. Habitasse platea dictumst vestibulum rhoncus est. Nunc sed id semper risus in hendrerit gravida. Felis eget velit aliquet sagittis id. Eget felis eget nunc lobortis.\nMaecenas pharetra convallis posuere morbi leo. Maecenas volutpat blandit aliquam etiam. A condimentum vitae sapien pellentesque habitant morbi tristique senectus et. Pulvinar mattis nunc sed blandit libero volutpat sed. Feugiat in ante metus dictum at tempor commodo ullamcorper. Vel pharetra vel turpis nunc eget lorem dolor. Est placerat in egestas erat imperdiet sed euismod. Quisque non tellus orci ac auctor augue mauris augue. Placerat vestibulum lectus mauris ultrices eros in cursus turpis. Enim nunc faucibus a pellentesque sit. Adipiscing vitae proin sagittis nisl. Iaculis at erat pellentesque adipiscing commodo elit at imperdiet. Aliquam sem fringilla ut morbi.\nArcu odio ut sem nulla pharetra diam sit amet nisl. Non diam phasellus vestibulum lorem sed. At erat pellentesque adipiscing commodo elit at. Lacus luctus accumsan tortor posuere ac ut consequat. Et malesuada fames ac turpis egestas integer. Tristique magna sit amet purus. A condimentum vitae sapien pellentesque habitant. Quis varius quam quisque id diam vel quam. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Augue interdum velit euismod in pellentesque massa. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant. Vulputate eu scelerisque felis imperdiet. Nibh tellus molestie nunc non blandit massa. Velit euismod in pellentesque massa placerat. Sed cras ornare arcu dui. Ut sem viverra aliquet eget sit. Eu lobortis elementum nibh tellus molestie nunc non. Blandit libero volutpat sed cras ornare arcu dui vivamus.\nSit amet aliquam id diam maecenas. Amet risus nullam eget felis eget nunc lobortis mattis aliquam. Magna sit amet purus gravida. Egestas purus viverra accumsan in nisl nisi. Leo duis ut diam quam. Ante metus dictum at tempor commodo ullamcorper. Ac turpis egestas integer eget. Fames ac turpis egestas integer eget aliquet nibh. Sem integer vitae justo eget magna fermentum. Semper auctor neque vitae tempus quam pellentesque nec nam aliquam. Vestibulum mattis ullamcorper velit sed. Consectetur adipiscing elit duis tristique sollicitudin nibh. Massa id neque aliquam vestibulum morbi blandit cursus risus.\nCursus sit amet dictum sit amet justo donec enim diam. Egestas erat imperdiet sed euismod. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Habitasse platea dictumst vestibulum rhoncus est pellentesque elit. Duis ultricies lacus sed turpis tincidunt id aliquet risus feugiat. Faucibus ornare suspendisse sed nisi lacus sed viverra. Pretium fusce id velit ut tortor pretium viverra. Fermentum odio eu feugiat pretium nibh ipsum consequat nisl vel. Senectus et netus et malesuada. Tellus pellentesque eu tincidunt tortor aliquam. Aenean sed adipiscing diam donec adipiscing tristique risus nec feugiat. Quis vel eros donec ac odio. Id interdum velit laoreet id donec ultrices tincidunt.\nMassa id neque aliquam vestibulum morbi blandit cursus risus at. Enim tortor at auctor urna nunc id cursus metus. Lorem ipsum dolor sit amet consectetur. At quis risus sed vulputate odio. Facilisis mauris sit amet massa vitae tortor condimentum lacinia quis. Et malesuada fames ac turpis egestas maecenas. Bibendum arcu vitae elementum curabitur vitae nunc sed velit dignissim. Viverra orci sagittis eu volutpat odio facilisis mauris. Adipiscing bibendum est ultricies integer quis auctor elit sed. Neque viverra justo nec ultrices dui sapien. Elementum nibh tellus molestie nunc non blandit massa enim. Euismod elementum nisi quis eleifend quam adipiscing vitae proin sagittis. Faucibus ornare suspendisse sed nisi. Quis viverra nibh cras pulvinar mattis nunc sed blandit. Tristique senectus et netus et. Magnis dis parturient montes nascetur ridiculus mus.\nDolor magna eget est lorem ipsum dolor. Nibh sit amet commodo nulla. Donec pretium vulputate sapien nec sagittis aliquam malesuada. Cras adipiscing enim eu turpis egestas pretium. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Mus mauris vitae ultricies leo integer. In nulla posuere sollicitudin aliquam ultrices sagittis orci. Quis hendrerit dolor magna eget. Nisl tincidunt eget nullam non. Vitae congue eu consequat ac felis donec et odio. Vivamus at augue eget arcu dictum varius duis at. Ornare quam viverra orci sagittis.\nErat nam at lectus urna duis convallis. Massa placerat duis ultricies lacus sed turpis tincidunt id aliquet. Est ullamcorper eget nulla facilisi etiam dignissim diam. Arcu vitae elementum curabitur vitae nunc sed velit dignissim sodales. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus. Neque viverra justo nec ultrices dui sapien eget mi proin. Viverra accumsan in nisl nisi scelerisque eu ultrices. Consequat interdum varius sit amet mattis. In aliquam sem fringilla ut morbi. Eget arcu dictum varius duis at. Nulla aliquet porttitor lacus luctus accumsan tortor posuere. Arcu bibendum at varius vel pharetra vel turpis. Hac habitasse platea dictumst quisque sagittis purus sit amet. Sapien eget mi proin sed libero enim sed. Quam elementum pulvinar etiam non quam lacus suspendisse faucibus interdum. Semper viverra nam libero justo. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Et malesuada fames ac turpis egestas maecenas pharetra convallis posuere.\nTurpis egestas sed tempus urna et pharetra pharetra massa. Gravida in fermentum et sollicitudin ac orci phasellus. Ornare suspendisse sed nisi lacus sed viverra tellus in. Fames ac turpis egestas maecenas pharetra convallis posuere. Mi proin sed libero enim sed faucibus turpis. Sit amet mauris commodo quis imperdiet massa tincidunt nunc. Ut etiam sit amet nisl purus in mollis nunc. Habitasse platea dictumst quisque sagittis purus sit amet volutpat consequat. Eget aliquet nibh praesent tristique magna. Sit amet est placerat in egestas erat. Commodo sed egestas egestas fringilla. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper. Dignissim convallis aenean et tortor at risus viverra. Morbi blandit cursus risus at ultrices mi. Ac turpis egestas integer eget aliquet nibh praesent tristique magna.\nVolutpat sed cras ornare arcu dui. Egestas erat imperdiet sed euismod nisi porta lorem mollis aliquam. Viverra justo nec ultrices dui sapien. Amet risus nullam eget felis eget nunc lobortis. Metus aliquam eleifend mi in. Ut eu sem integer vitae. Auctor elit sed vulputate mi sit amet. Nisl nisi scelerisque eu ultrices. Dictum fusce ut placerat orci nulla. Pellentesque habitant morbi tristique senectus et. Auctor elit sed vulputate mi sit. Tincidunt arcu non sodales neque. Mi in nulla posuere sollicitudin aliquam. Morbi non arcu risus quis varius quam quisque id diam. Cras adipiscing enim eu turpis egestas pretium aenean pharetra magna. At auctor urna nunc id cursus metus aliquam. Mauris a diam maecenas sed enim ut sem viverra. Nunc scelerisque viverra mauris in. In iaculis nunc sed augue lacus viverra vitae congue eu. Volutpat blandit aliquam etiam erat velit scelerisque in dictum non."), + CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {0,0,0,255} })); } } - 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("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(CLAY_ID("Blob4Floating2"), CLAY_FLOATING({ .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("SidebarBlob4")).id })) { + CLAY(CLAY_ID("ScrollContainer"), CLAY_LAYOUT({ .sizing = { .height = CLAY_SIZING_FIXED(200) }, .childGap = 2 }), CLAY_SCROLL({ .vertical = true })) { + CLAY(CLAY_ID("FloatingContainer2"), CLAY_LAYOUT({ }), CLAY_FLOATING({ .zIndex = 1 })) { + CLAY(CLAY_ID("FloatingContainerInner"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = {16, 16} }), CLAY_RECTANGLE({ .color = {140,80, 200, 200} })) { + CLAY_TEXT(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 < 10; i++) { + CLAY(CLAY_ID("ScrollContainerInner"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM }), CLAY_RECTANGLE({ .color = {160, 160, 160, 255} })) { + 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").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})) {} - }; + Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("MainContent"))); + if (scrollData.found) { + CLAY(CLAY_ID("ScrollBar"), + CLAY_FLOATING({ + .offset = { .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height }, + .zIndex = 1, + .parentId = Clay_GetElementId(CLAY_STRING("MainContent")).id, + .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP} + }) + ) { + CLAY(CLAY_ID("ScrollBarButton"), + CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height) }}), + CLAY_RECTANGLE({ .cornerRadius = {6}, .color = Clay_PointerOver(Clay__HashString(CLAY_STRING("ScrollBar"), 0, 0)) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150} }) + ) {} + } + } }; return Clay_EndLayout(); } @@ -147,13 +165,13 @@ void UpdateDrawFrame(void) scrollbarData.mouseDown = false; } - if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(CLAY_ID("ScrollBar"))) { - Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("MainContent")); + if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(Clay__HashString(CLAY_STRING("ScrollBar"), 0, 0))) { + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay__HashString(CLAY_STRING("MainContent"), 0, 0)); scrollbarData.clickOrigin = mousePosition; scrollbarData.positionOrigin = *scrollContainerData.scrollPosition; scrollbarData.mouseDown = true; } else if (scrollbarData.mouseDown) { - Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("MainContent")); + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay__HashString(CLAY_STRING("MainContent"), 0, 0)); if (scrollContainerData.contentDimensions.height > 0) { Clay_Vector2 ratio = (Clay_Vector2) { scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width, diff --git a/examples/raylib-sidebar-scrolling-container/multi-compilation-unit.c b/examples/raylib-sidebar-scrolling-container/multi-compilation-unit.c index 8d246be..e5df212 100644 --- a/examples/raylib-sidebar-scrolling-container/multi-compilation-unit.c +++ b/examples/raylib-sidebar-scrolling-container/multi-compilation-unit.c @@ -3,5 +3,5 @@ // NOTE: This file only exists to make sure that clay works when included in multiple translation units. void SatisfyCompiler() { - CLAY_CONTAINER(CLAY_ID("SatisfyCompiler"), CLAY_LAYOUT()) {} + CLAY(CLAY_ID("SatisfyCompiler"), CLAY_LAYOUT({})) {} } \ No newline at end of file diff --git a/generator/array_allocate.template.c b/generator/array_allocate.template.c new file mode 100644 index 0000000..8c28707 --- /dev/null +++ b/generator/array_allocate.template.c @@ -0,0 +1,3 @@ +$NAME$ $NAME$_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return CLAY__INIT($NAME$){.capacity = capacity, .length = 0, .internalArray = ($TYPE$ *)Clay__Array_Allocate_Arena(capacity, sizeof($TYPE$), CLAY__ALIGNMENT($TYPE$), arena)}; +} \ No newline at end of file diff --git a/generator/array_define.template.c b/generator/array_define.template.c index 1df342b..aac9c94 100644 --- a/generator/array_define.template.c +++ b/generator/array_define.template.c @@ -3,8 +3,4 @@ typedef struct uint32_t capacity; uint32_t length; $TYPE$ *internalArray; -} $NAME$; - -$NAME$ $NAME$_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { - return CLAY__INIT($NAME$){.capacity = capacity, .length = 0, .internalArray = ($TYPE$ *)Clay__Array_Allocate_Arena(capacity, sizeof($TYPE$), CLAY__ALIGNMENT($TYPE$), arena)}; -} \ No newline at end of file +} $NAME$; \ No newline at end of file diff --git a/generator/array_define_slice.template.c b/generator/array_define_slice.template.c new file mode 100644 index 0000000..680ffa2 --- /dev/null +++ b/generator/array_define_slice.template.c @@ -0,0 +1,5 @@ +typedef struct +{ + uint32_t length; + $TYPE$ *internalArray; +} $NAME$Slice; \ No newline at end of file diff --git a/generator/array_get_slice.template.c b/generator/array_get_slice.template.c new file mode 100644 index 0000000..ac02abb --- /dev/null +++ b/generator/array_get_slice.template.c @@ -0,0 +1,3 @@ +$TYPE$ *$NAME$Slice_Get($NAME$Slice *slice, int index) { + return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : $DEFAULT_VALUE$; +} \ No newline at end of file