From f48ee0b9fd46f1925a1198ec402c62076ba0cd02 Mon Sep 17 00:00:00 2001 From: Jefferey Schlueter <47044769+JeffereyAEL@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:49:19 -0500 Subject: [PATCH] Added C3 Bindings --- bindings/c3/README.md | 73 + bindings/c3/c-lang/source/clay.c | 4126 ++++++++++++++++++++ bindings/c3/project.json | 33 + bindings/c3/resources/Lexend-Regular.ttf | Bin 0 -> 77836 bytes bindings/c3/source/clay-depracated.c3 | 700 ++++ bindings/c3/source/clay-raylib-renderer.c3 | 448 +++ bindings/c3/source/clay.c3 | 478 +++ 7 files changed, 5858 insertions(+) create mode 100644 bindings/c3/README.md create mode 100644 bindings/c3/c-lang/source/clay.c create mode 100644 bindings/c3/project.json create mode 100644 bindings/c3/resources/Lexend-Regular.ttf create mode 100644 bindings/c3/source/clay-depracated.c3 create mode 100644 bindings/c3/source/clay-raylib-renderer.c3 create mode 100644 bindings/c3/source/clay.c3 diff --git a/bindings/c3/README.md b/bindings/c3/README.md new file mode 100644 index 0000000..4a70a27 --- /dev/null +++ b/bindings/c3/README.md @@ -0,0 +1,73 @@ +# Clay-C3-Bindings +C3 Bindings for [Clay](https://github.com/nicbarker/clay.git), a UI layout library written in C. +This directory contains the clay.c3 bindings file as well as a recreation of the clay-raylibs binding and the video-example raylib project. + +Special thanks to: +- [Christoffer L](https://github.com/lerno) C3's core developer (as I understand it) +- Book-reader in the [C3-lang Discord](https://discord.gg/qN76R87) + +## TODO: +- Find out how to build a static-lib with additional C sources + +## - C3 macros +Traditional Clay C Macro System + +```cpp +/* FILTER BUTTON */ +CLAY( + CLAY_ID("FilterButton"), + Clay_Hovered() ? CLAY_RECTANGLE({ + .color = Clay_Hovered() ? FIRE_ORANGE : (Clay_Color){80, 25, 200, 255}, + .cornerRadius = 8, + }) : 0, + CLAY_LAYOUT({ + .sizing = { + .width = CLAY_SIZING_FIT(), + .height = CLAY_SIZING_GROW() + }, + .padding = 10 + }) +) { + // define children... +} +``` + +Clay C3 Macro System +```cpp +/* FILTER BUTTON */ +clay::clay( + clay::id("FilterButton"), + clay::@bodyIf(clay::hovered(), clay::rectangle({ + .color = clay::hovered() ? FIRE_ORANGE : {80, 25, 200, 255}, + .cornerRadius = clay::cornerRadiusUni(8) + }) + ), + clay::layout({ + .sizing = { + .width = clay::sizingFit(), + .height = clay::sizingGrow() + }, + .padding = clay::paddingUni(8) + }) +){ + // define children... +}; +``` + +## To Get Started: +- Download c3c [here](https://c3-lang.org/getting-started/prebuilt-binaries/) +- If you wish to compile the website-example, I've already provided a target to build in the [project.json](project.json) +[Photo of project.json goes here] +- - set your `cd` to this project dir +- - The use the `c3c vendor-fetch raylib55` command to download a c3 compressed archive of raylib +- - - *once you have raylib55.c3l in the [lib](lib) folder you've got it right* +- - - (*note: for the current configuration you'll need to modify the default raylib module name in th raylib.c3i file in [build](build) directory from `raylib55::li` to `module raylib`*) +- - then simple use the command `c3c run video-example` to compile and run that video example +- - - (*note: to use the `c3c build ` command with video-example, you'll need to copy the resource folder into the [build](build) directory with the execute to run it +- - - `run` executes the build result from the project directory, somehow. This means that `run` will look for the resource folder in [c3](../c3), while `build` will look for it in [build](build)) + +## RESOURCES: +### - [C3](https://github.com/c3lang/c3c.git) (A C-a-like, that aims to bring modern language QA features and a revamped Macro system to C) +### - [Raylib](https://github.com/raysan5/raylib.git) (C Videogame and Graphical API) +### - [Lexend](https://github.com/googlefonts/lexend.git) (Accessible/ Dyslexic Friendly Font) + diff --git a/bindings/c3/c-lang/source/clay.c b/bindings/c3/c-lang/source/clay.c new file mode 100644 index 0000000..0136f32 --- /dev/null +++ b/bindings/c3/c-lang/source/clay.c @@ -0,0 +1,4126 @@ +// VERSION: 0.11 + +/* + NOTE: In order to use this library you must define + the following macro in exactly one file, _before_ including clay.h: + + #define CLAY_IMPLEMENTATION + #include "clay.h" + + See the examples folder for details. +*/ + +#include +#include +#include + +// ----------------------------------------- +// HEADER DECLARATIONS --------------------- +// ----------------------------------------- + +#ifndef CLAY_HEADER +#define CLAY_HEADER + +#if !( \ + (defined(__cplusplus) && __cplusplus >= 202002L) || \ + (defined(__STDC__) && __STDC__ == 1 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + defined(_MSC_VER) \ +) +#error "Clay requires C99, C++20, or MSVC" +#endif + +#ifdef CLAY_WASM +#define CLAY_WASM_EXPORT(name) __attribute__((export_name(name))) +#else +#define CLAY_WASM_EXPORT(null) +#endif + +// Public Macro API ------------------------ + +#define CLAY__WRAPPER_TYPE(type) Clay__##type##Wrapper +#define CLAY__WRAPPER_STRUCT(type) typedef struct { type wrapped; } CLAY__WRAPPER_TYPE(type) +#define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(CLAY__WRAPPER_TYPE(type)) { __VA_ARGS__ }).wrapped + +#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define CLAY_LAYOUT(...) Clay__AttachLayoutConfig(Clay__StoreLayoutConfig(CLAY__CONFIG_WRAPPER(Clay_LayoutConfig, __VA_ARGS__))) + +#define CLAY_RECTANGLE(...) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .rectangleElementConfig = Clay__StoreRectangleElementConfig(CLAY__CONFIG_WRAPPER(Clay_RectangleElementConfig, __VA_ARGS__)) }, CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE) + +#define CLAY_TEXT_CONFIG(...) Clay__StoreTextElementConfig(CLAY__CONFIG_WRAPPER(Clay_TextElementConfig, __VA_ARGS__)) + +#define CLAY_IMAGE(...) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(CLAY__CONFIG_WRAPPER(Clay_ImageElementConfig, __VA_ARGS__)) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE) + +#define CLAY_FLOATING(...) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .floatingElementConfig = Clay__StoreFloatingElementConfig(CLAY__CONFIG_WRAPPER(Clay_FloatingElementConfig, __VA_ARGS__)) }, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER) + +#define CLAY_CUSTOM_ELEMENT(...) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(CLAY__CONFIG_WRAPPER(Clay_CustomElementConfig, __VA_ARGS__)) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM) + +#define CLAY_SCROLL(...) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .scrollElementConfig = Clay__StoreScrollElementConfig(CLAY__CONFIG_WRAPPER(Clay_ScrollElementConfig, __VA_ARGS__)) }, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER) + +#define CLAY_BORDER(...) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__CONFIG_WRAPPER(Clay_BorderElementConfig, __VA_ARGS__)) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER) + +#define CLAY_BORDER_OUTSIDE(...) Clay__AttachElementConfig(CLAY__INIT(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_OUTSIDE_RADIUS(width, color, radius) Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(CLAY__INIT(Clay_BorderElementConfig) { .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .cornerRadius = CLAY_CORNER_RADIUS(radius) })}, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER) + +#define CLAY_BORDER_ALL(...) Clay__AttachElementConfig(CLAY__INIT(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__INIT(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 = CLAY_CORNER_RADIUS(radius)}) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER) + +#define CLAY_CORNER_RADIUS(radius) (CLAY__INIT(Clay_CornerRadius) { radius, radius, radius, radius }) + +#define CLAY_PADDING_ALL(padding) CLAY__CONFIG_WRAPPER(Clay_Padding, { padding, padding, padding, padding }) + +#define CLAY_SIZING_FIT(...) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { __VA_ARGS__ } }, .type = CLAY__SIZING_TYPE_FIT }) + +#define CLAY_SIZING_GROW(...) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { __VA_ARGS__ } }, .type = CLAY__SIZING_TYPE_GROW }) + +#define CLAY_SIZING_FIXED(fixedSize) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { fixedSize, fixedSize } }, .type = CLAY__SIZING_TYPE_FIXED }) + +#define CLAY_SIZING_PERCENT(percentOfParent) (CLAY__INIT(Clay_SizingAxis) { .size = { .percent = (percentOfParent) }, .type = CLAY__SIZING_TYPE_PERCENT }) + +#define CLAY_ID(label) Clay__AttachId(Clay__HashString(CLAY_STRING(label), 0, 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__AttachId(Clay__HashString(CLAY_STRING(label), index, Clay__GetParentElementId())) + +#define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof((s)[0])) - sizeof((s)[0])) + +#define CLAY__ENSURE_STRING_LITERAL(x) ("" x "") + +// Note: If an error led you here, it's because CLAY_STRING can only be used with string literals, i.e. CLAY_STRING("SomeString") and not CLAY_STRING(yourString) +#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }) + +#define CLAY_STRING_CONST(string) { .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) } + +static uint8_t CLAY__ELEMENT_DEFINITION_LATCH; + +// Publicly visible layout element macros ----------------------------------------------------- + +/* This macro looks scary on the surface, but is actually quite simple. + It turns a macro call like this: + + CLAY( + CLAY_RECTANGLE(), + CLAY_ID() + ) { + ...children declared here + } + + Into calls like this: + + Clay_OpenElement(); + CLAY_RECTANGLE(); + CLAY_ID(); + Clay_ElementPostConfiguration(); + ...children declared here + Clay_CloseElement(); + + The for loop will only ever run a single iteration, putting Clay__CloseElement() in the increment of the loop + means that it will run after the body - where the children are declared. It just exists to make sure you don't forget + to call Clay_CloseElement(). +*/ +#define CLAY(...) \ + for (\ + CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElement(), __VA_ARGS__, Clay__ElementPostConfiguration(), 0); \ + CLAY__ELEMENT_DEFINITION_LATCH < 1; \ + ++CLAY__ELEMENT_DEFINITION_LATCH, Clay__CloseElement() \ + ) + +#define CLAY_TEXT(text, textConfig) Clay__OpenTextElement(text, textConfig) + +#ifdef __cplusplus + +#define CLAY__INIT(type) type +#define CLAY__TYPEDEF(name, ...) typedef __VA_ARGS__ name; CLAY__WRAPPER_STRUCT(name) +#define CLAY__ALIGNMENT(type) alignof(type) +#define CLAY__POINTER_ALIGNMENT alignof(void *) + +#define CLAY_PACKED_ENUM enum : uint8_t + +#define CLAY__DEFAULT_STRUCT {} + +#else + +#define CLAY__INIT(type) (type) + +#define CLAY__ALIGNMENT_STRUCT(type) struct Clay__Align##type { char c; type x; } +#define CLAY__TYPEDEF(name, ...) typedef __VA_ARGS__ name; CLAY__ALIGNMENT_STRUCT(name); CLAY__WRAPPER_STRUCT(name) +#define CLAY__ALIGNMENT(type) (offsetof(struct Clay__Align##type, x)) +#define CLAY__POINTER_ALIGNMENT CLAY__ALIGNMENT(pointer) + +// NOTE: If you need to get the offset for other standard types in the future, add them here. +struct Clay__Alignpointer { char c; void *x; }; +CLAY__ALIGNMENT_STRUCT(bool); +CLAY__ALIGNMENT_STRUCT(uint8_t); +CLAY__ALIGNMENT_STRUCT(int32_t); + +#if defined(_MSC_VER) && !defined(__clang__) +#define CLAY_PACKED_ENUM __pragma(pack(push, 1)) enum __pragma(pack(pop)) +#else +#define CLAY_PACKED_ENUM enum __attribute__((__packed__)) +#endif + +#if __STDC_VERSION__ >= 202311L +#define CLAY__DEFAULT_STRUCT {} +#else +#define CLAY__DEFAULT_STRUCT {0} +#endif + +#endif // __cplusplus + +#ifdef __cplusplus +extern "C" { +#endif + +// Utility Structs ------------------------- +// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string, +// but it is also used to represent slices. +CLAY__TYPEDEF(Clay_String, struct { + int32_t length; + const char *chars; +}); + +CLAY__TYPEDEF(Clay__StringArray, struct { + int32_t capacity; + int32_t length; + Clay_String *internalArray; +}); + +typedef struct Clay_Context Clay_Context; + +CLAY__TYPEDEF(Clay_Arena, struct { + uintptr_t nextAllocation; + size_t capacity; + char *memory; +}); + +CLAY__TYPEDEF(Clay_Dimensions, struct { + float width, height; +}); + +CLAY__TYPEDEF(Clay_Vector2, struct { + float x, y; +}); + +CLAY__TYPEDEF(Clay_Color, struct { + float r, g, b, a; +}); + +CLAY__TYPEDEF(Clay_BoundingBox, struct { + float x, y, width, height; +}); + +// baseId + offset = id +CLAY__TYPEDEF(Clay_ElementId, struct { + uint32_t id; + uint32_t offset; + uint32_t baseId; + Clay_String stringId; +}); + +CLAY__TYPEDEF(Clay_CornerRadius, struct { + float topLeft; + float topRight; + float bottomLeft; + float bottomRight; +}); + +CLAY__TYPEDEF(Clay__ElementConfigType, CLAY_PACKED_ENUM { + CLAY__ELEMENT_CONFIG_TYPE_NONE = 0, + 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, +}); + +// Element Configs --------------------------- +// Layout +CLAY__TYPEDEF(Clay_LayoutDirection, CLAY_PACKED_ENUM { + CLAY_LEFT_TO_RIGHT, + CLAY_TOP_TO_BOTTOM, +}); + +CLAY__TYPEDEF(Clay_LayoutAlignmentX, CLAY_PACKED_ENUM { + CLAY_ALIGN_X_LEFT, + CLAY_ALIGN_X_RIGHT, + CLAY_ALIGN_X_CENTER, +}); + +CLAY__TYPEDEF(Clay_LayoutAlignmentY, CLAY_PACKED_ENUM { + CLAY_ALIGN_Y_TOP, + CLAY_ALIGN_Y_BOTTOM, + CLAY_ALIGN_Y_CENTER, +}); + +CLAY__TYPEDEF(Clay__SizingType, CLAY_PACKED_ENUM { + CLAY__SIZING_TYPE_FIT, + CLAY__SIZING_TYPE_GROW, + CLAY__SIZING_TYPE_PERCENT, + CLAY__SIZING_TYPE_FIXED, +}); + +CLAY__TYPEDEF(Clay_ChildAlignment, struct { + Clay_LayoutAlignmentX x; + Clay_LayoutAlignmentY y; +}); + +CLAY__TYPEDEF(Clay_SizingMinMax, struct { + float min; + float max; +}); + +CLAY__TYPEDEF(Clay_SizingAxis, struct { + union { + Clay_SizingMinMax minMax; + float percent; + } size; + Clay__SizingType type; +}); + +CLAY__TYPEDEF(Clay_Sizing, struct { + Clay_SizingAxis width; + Clay_SizingAxis height; +}); + +CLAY__TYPEDEF(Clay_Padding, struct { + uint16_t left; + uint16_t right; + uint16_t top; + uint16_t bottom; +}); + +CLAY__TYPEDEF(Clay_LayoutConfig, struct { + Clay_Sizing sizing; + Clay_Padding padding; + uint16_t childGap; + Clay_ChildAlignment childAlignment; + Clay_LayoutDirection layoutDirection; +}); + +extern Clay_LayoutConfig CLAY_LAYOUT_DEFAULT; + +// Rectangle +// NOTE: Not declared in the typedef as an ifdef inside macro arguments is UB +struct Clay_RectangleElementConfig { + Clay_Color color; + Clay_CornerRadius cornerRadius; + #ifdef CLAY_EXTEND_CONFIG_RECTANGLE + CLAY_EXTEND_CONFIG_RECTANGLE + #endif +}; +CLAY__TYPEDEF(Clay_RectangleElementConfig, struct Clay_RectangleElementConfig); + +// Text +CLAY__TYPEDEF(Clay_TextElementConfigWrapMode, enum { + CLAY_TEXT_WRAP_WORDS, + CLAY_TEXT_WRAP_NEWLINES, + CLAY_TEXT_WRAP_NONE, +}); + +struct Clay_TextElementConfig { + Clay_Color textColor; + uint16_t fontId; + uint16_t fontSize; + uint16_t letterSpacing; + uint16_t lineHeight; + Clay_TextElementConfigWrapMode wrapMode; + #ifdef CLAY_EXTEND_CONFIG_TEXT + CLAY_EXTEND_CONFIG_TEXT + #endif +}; +CLAY__TYPEDEF(Clay_TextElementConfig, struct Clay_TextElementConfig); + +// Image +struct Clay_ImageElementConfig { + void *imageData; + Clay_Dimensions sourceDimensions; + #ifdef CLAY_EXTEND_CONFIG_IMAGE + CLAY_EXTEND_CONFIG_IMAGE + #endif +}; +CLAY__TYPEDEF(Clay_ImageElementConfig, struct Clay_ImageElementConfig); + +// Floating +CLAY__TYPEDEF(Clay_FloatingAttachPointType, CLAY_PACKED_ENUM { + CLAY_ATTACH_POINT_LEFT_TOP, + 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__TYPEDEF(Clay_FloatingAttachPoints, struct { + Clay_FloatingAttachPointType element; + Clay_FloatingAttachPointType parent; +}); + +CLAY__TYPEDEF(Clay_PointerCaptureMode, enum { + CLAY_POINTER_CAPTURE_MODE_CAPTURE, +// CLAY_POINTER_CAPTURE_MODE_PARENT, TODO pass pointer through to attached parent + CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, +}); + +CLAY__TYPEDEF(Clay_FloatingElementConfig, struct { + Clay_Vector2 offset; + Clay_Dimensions expand; + uint16_t zIndex; + uint32_t parentId; + Clay_FloatingAttachPoints attachment; + Clay_PointerCaptureMode pointerCaptureMode; +}); + +// Custom +struct Clay_CustomElementConfig { + #ifndef CLAY_EXTEND_CONFIG_CUSTOM + void *customData; + #else + CLAY_EXTEND_CONFIG_CUSTOM + #endif +}; +CLAY__TYPEDEF(Clay_CustomElementConfig, struct Clay_CustomElementConfig); + +// Scroll +CLAY__TYPEDEF(Clay_ScrollElementConfig, struct { + bool horizontal; + bool vertical; +}); + +// Border +CLAY__TYPEDEF(Clay_Border, struct { + uint32_t width; + Clay_Color color; +}); + +struct Clay_BorderElementConfig { + Clay_Border left; + Clay_Border right; + Clay_Border top; + Clay_Border bottom; + Clay_Border betweenChildren; + Clay_CornerRadius cornerRadius; + #ifdef CLAY_EXTEND_CONFIG_BORDER + CLAY_EXTEND_CONFIG_BORDER + #endif +}; +CLAY__TYPEDEF(Clay_BorderElementConfig, struct Clay_BorderElementConfig); + +CLAY__TYPEDEF(Clay_ElementConfigUnion, union { + Clay_RectangleElementConfig *rectangleElementConfig; + Clay_TextElementConfig *textElementConfig; + Clay_ImageElementConfig *imageElementConfig; + Clay_FloatingElementConfig *floatingElementConfig; + Clay_CustomElementConfig *customElementConfig; + Clay_ScrollElementConfig *scrollElementConfig; + Clay_BorderElementConfig *borderElementConfig; +}); + +CLAY__TYPEDEF(Clay_ElementConfig, struct { + Clay__ElementConfigType type; + Clay_ElementConfigUnion config; +}); + +// Miscellaneous Structs & Enums --------------------------------- +CLAY__TYPEDEF(Clay_ScrollContainerData, struct { + // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. + // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. + Clay_Vector2 *scrollPosition; + Clay_Dimensions scrollContainerDimensions; + Clay_Dimensions contentDimensions; + Clay_ScrollElementConfig config; + // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. + bool found; +}); + +CLAY__TYPEDEF(Clay_ElementData, struct +{ + Clay_BoundingBox boundingBox; + // Indicates whether an actual Element matched the provided ID or if the default struct was returned. + bool found; +}); + +CLAY__TYPEDEF(Clay_RenderCommandType, CLAY_PACKED_ENUM { + CLAY_RENDER_COMMAND_TYPE_NONE, + CLAY_RENDER_COMMAND_TYPE_RECTANGLE, + CLAY_RENDER_COMMAND_TYPE_BORDER, + CLAY_RENDER_COMMAND_TYPE_TEXT, + CLAY_RENDER_COMMAND_TYPE_IMAGE, + CLAY_RENDER_COMMAND_TYPE_SCISSOR_START, + CLAY_RENDER_COMMAND_TYPE_SCISSOR_END, + CLAY_RENDER_COMMAND_TYPE_CUSTOM, +}); + +CLAY__TYPEDEF(Clay_RenderCommand, struct { + Clay_BoundingBox boundingBox; + Clay_ElementConfigUnion config; + 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; +}); + +CLAY__TYPEDEF(Clay_RenderCommandArray, struct { + int32_t capacity; + int32_t length; + Clay_RenderCommand *internalArray; +}); + +CLAY__TYPEDEF(Clay_PointerDataInteractionState, enum { + CLAY_POINTER_DATA_PRESSED_THIS_FRAME, + CLAY_POINTER_DATA_PRESSED, + CLAY_POINTER_DATA_RELEASED_THIS_FRAME, + CLAY_POINTER_DATA_RELEASED, +}); + +CLAY__TYPEDEF(Clay_PointerData, struct { + Clay_Vector2 position; + Clay_PointerDataInteractionState state; +}); + +CLAY__TYPEDEF(Clay_ErrorType, enum { + CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED, + CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED, + CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED, + CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED, + CLAY_ERROR_TYPE_DUPLICATE_ID, + CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND, + CLAY_ERROR_TYPE_INTERNAL_ERROR, +}); + +CLAY__TYPEDEF(Clay_ErrorData, struct { + Clay_ErrorType errorType; + Clay_String errorText; + uintptr_t userData; +}); + +CLAY__TYPEDEF(Clay_ErrorHandler, struct { + void (*errorHandlerFunction)(Clay_ErrorData errorText); + uintptr_t userData; +}); + +// Function Forward Declarations --------------------------------- +// Public API functions --- +uint32_t Clay_MinMemorySize(void); +Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset); +void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown); +Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler); +Clay_Context* Clay_GetCurrentContext(void); +void Clay_SetCurrentContext(Clay_Context* context); +void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime); +void Clay_SetLayoutDimensions(Clay_Dimensions dimensions); +void Clay_BeginLayout(void); +Clay_RenderCommandArray Clay_EndLayout(void); +Clay_ElementId Clay_GetElementId(Clay_String idString); +Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index); +Clay_ElementData Clay_GetElementData(Clay_ElementId id); +bool Clay_Hovered(void); +void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, intptr_t userData), intptr_t userData); +bool Clay_PointerOver(Clay_ElementId elementId); +Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id); +void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_String *text, Clay_TextElementConfig *config)); +void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId)); +Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index); +void Clay_SetDebugModeEnabled(bool enabled); +bool Clay_IsDebugModeEnabled(void); +void Clay_SetCullingEnabled(bool enabled); +int32_t Clay_GetMaxElementCount(void); +void Clay_SetMaxElementCount(int32_t maxElementCount); +int32_t Clay_GetMaxMeasureTextCacheWordCount(void); +void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount); +void Clay_ResetMeasureTextCache(void); + +// Internal API functions required by macros +void Clay__OpenElement(void); +void Clay__CloseElement(void); +Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config); +void Clay__ElementPostConfiguration(void); +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); +Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config); +Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config); +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); +void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig); +uint32_t Clay__GetParentElementId(void); + +extern Clay_Color Clay__debugViewHighlightColor; +extern uint32_t Clay__debugViewWidth; + +#ifdef __cplusplus +} +#endif + +#endif // CLAY_HEADER + +// ----------------------------------------- +// IMPLEMENTATION -------------------------- +// ----------------------------------------- +#ifdef CLAY_IMPLEMENTATION +#undef CLAY_IMPLEMENTATION + +#ifndef CLAY__NULL +#define CLAY__NULL 0 +#endif + +#ifndef CLAY__MAXFLOAT +#define CLAY__MAXFLOAT 3.40282346638528859812e+38F +#endif + +Clay_Context *Clay__currentContext; +int32_t Clay__defaultMaxElementCount = 8192; +int32_t Clay__defaultMaxMeasureTextWordCacheCount = 16384; + +void Clay__ErrorHandlerFunctionDefault(Clay_ErrorData errorText) { + (void) errorText; +} + +Clay_String CLAY__SPACECHAR = { .length = 1, .chars = " " }; +Clay_String CLAY__STRING_DEFAULT = { .length = 0, .chars = NULL }; + +CLAY__TYPEDEF(Clay_BooleanWarnings, struct { + bool maxElementsExceeded; + bool maxRenderCommandsExceeded; + bool maxTextMeasureCacheExceeded; +}); + +CLAY__TYPEDEF(Clay__Warning, struct { + Clay_String baseMessage; + Clay_String dynamicMessage; +}); + +Clay__Warning CLAY__WARNING_DEFAULT = CLAY__DEFAULT_STRUCT; + +CLAY__TYPEDEF(Clay__WarningArray, struct { + int32_t capacity; + int32_t length; + Clay__Warning *internalArray; +}); + +Clay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena); +Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item); +void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, uint32_t alignment, Clay_Arena *arena); +bool Clay__Array_RangeCheck(int32_t index, int32_t length); +bool Clay__Array_AddCapacityCheck(int32_t length, int32_t capacity); + +// __GENERATED__ template array_define,array_allocate TYPE=bool NAME=Clay__BoolArray +#pragma region generated +CLAY__TYPEDEF(Clay__BoolArray, struct +{ + int32_t capacity; + int32_t length; + bool *internalArray; +}); +Clay__BoolArray Clay__BoolArray_Allocate_Arena(int32_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)}; +} +#pragma endregion +// __GENERATED__ template + +Clay_ElementId CLAY__ELEMENT_ID_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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 +CLAY__TYPEDEF(Clay__ElementIdArray, struct +{ + int32_t capacity; + int32_t length; + Clay_ElementId *internalArray; +}); +Clay__ElementIdArray Clay__ElementIdArray_Allocate_Arena(int32_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)}; +} +Clay_ElementId *Clay__ElementIdArray_Get(Clay__ElementIdArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__ELEMENT_ID_DEFAULT; +} +Clay_ElementId *Clay__ElementIdArray_Add(Clay__ElementIdArray *array, Clay_ElementId item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__ELEMENT_ID_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_ElementConfig CLAY__ELEMENT_CONFIG_DEFAULT = {CLAY__ELEMENT_CONFIG_TYPE_NONE, CLAY__DEFAULT_STRUCT}; + +// __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 +CLAY__TYPEDEF(Clay__ElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_ElementConfig *internalArray; +}); +CLAY__TYPEDEF(Clay__ElementConfigArraySlice, struct +{ + int32_t length; + Clay_ElementConfig *internalArray; +}); +Clay__ElementConfigArray Clay__ElementConfigArray_Allocate_Arena(int32_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, int32_t 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_AddCapacityCheck(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, int32_t index) { + return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : &CLAY__ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = { .sizing = { .width = { .size = { .minMax = {0, CLAY__MAXFLOAT } }, .type = CLAY__SIZING_TYPE_FIT }, .height = { .size = { .minMax = {0, CLAY__MAXFLOAT } }, .type = CLAY__SIZING_TYPE_FIT } } }; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_LayoutConfig NAME=Clay__LayoutConfigArray DEFAULT_VALUE=&CLAY_LAYOUT_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__LayoutConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_LayoutConfig *internalArray; +}); +Clay__LayoutConfigArray Clay__LayoutConfigArray_Allocate_Arena(int32_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)}; +} +Clay_LayoutConfig *Clay__LayoutConfigArray_Add(Clay__LayoutConfigArray *array, Clay_LayoutConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY_LAYOUT_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_RectangleElementConfig CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_RectangleElementConfig NAME=Clay__RectangleElementConfigArray DEFAULT_VALUE=&CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__RectangleElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_RectangleElementConfig *internalArray; +}); +Clay__RectangleElementConfigArray Clay__RectangleElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_RectangleElementConfig *Clay__RectangleElementConfigArray_Add(Clay__RectangleElementConfigArray *array, Clay_RectangleElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_TextElementConfig CLAY__TEXT_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_TextElementConfig NAME=Clay__TextElementConfigArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__TextElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_TextElementConfig *internalArray; +}); +Clay__TextElementConfigArray Clay__TextElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_TextElementConfig *Clay__TextElementConfigArray_Add(Clay__TextElementConfigArray *array, Clay_TextElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__TEXT_ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_ImageElementConfig CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_ImageElementConfig NAME=Clay__ImageElementConfigArray DEFAULT_VALUE=&CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__ImageElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_ImageElementConfig *internalArray; +}); +Clay__ImageElementConfigArray Clay__ImageElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_ImageElementConfig *Clay__ImageElementConfigArray_Add(Clay__ImageElementConfigArray *array, Clay_ImageElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_FloatingElementConfig CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_FloatingElementConfig NAME=Clay__FloatingElementConfigArray DEFAULT_VALUE=&CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__FloatingElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_FloatingElementConfig *internalArray; +}); +Clay__FloatingElementConfigArray Clay__FloatingElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_FloatingElementConfig *Clay__FloatingElementConfigArray_Add(Clay__FloatingElementConfigArray *array, Clay_FloatingElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_CustomElementConfig CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_CustomElementConfig NAME=Clay__CustomElementConfigArray DEFAULT_VALUE=&CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__CustomElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_CustomElementConfig *internalArray; +}); +Clay__CustomElementConfigArray Clay__CustomElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_CustomElementConfig *Clay__CustomElementConfigArray_Add(Clay__CustomElementConfigArray *array, Clay_CustomElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_ScrollElementConfig CLAY__SCROLL_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_ScrollElementConfig NAME=Clay__ScrollElementConfigArray DEFAULT_VALUE=&CLAY__SCROLL_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__ScrollElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_ScrollElementConfig *internalArray; +}); +Clay__ScrollElementConfigArray Clay__ScrollElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_ScrollElementConfig *Clay__ScrollElementConfigArray_Add(Clay__ScrollElementConfigArray *array, Clay_ScrollElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__SCROLL_ELEMENT_CONFIG_DEFAULT; +} +#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 +CLAY__TYPEDEF(Clay__StringArraySlice, struct +{ + int32_t length; + Clay_String *internalArray; +}); +Clay__StringArray Clay__StringArray_Allocate_Arena(int32_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_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__STRING_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__WrappedTextLine, struct { + Clay_Dimensions dimensions; + Clay_String line; +}); + +Clay__WrappedTextLine CLAY__WRAPPED_TEXT_LINE_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_define_slice,array_allocate,array_add,array_get TYPE=Clay__WrappedTextLine NAME=Clay__WrappedTextLineArray DEFAULT_VALUE=&CLAY__WRAPPED_TEXT_LINE_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__WrappedTextLineArray, struct +{ + int32_t capacity; + int32_t length; + Clay__WrappedTextLine *internalArray; +}); +CLAY__TYPEDEF(Clay__WrappedTextLineArraySlice, struct +{ + int32_t length; + Clay__WrappedTextLine *internalArray; +}); +Clay__WrappedTextLineArray Clay__WrappedTextLineArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena) { + return CLAY__INIT(Clay__WrappedTextLineArray){.capacity = capacity, .length = 0, .internalArray = (Clay__WrappedTextLine *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__WrappedTextLine), CLAY__ALIGNMENT(Clay__WrappedTextLine), arena)}; +} +Clay__WrappedTextLine *Clay__WrappedTextLineArray_Add(Clay__WrappedTextLineArray *array, Clay__WrappedTextLine item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__WRAPPED_TEXT_LINE_DEFAULT; +} +Clay__WrappedTextLine *Clay__WrappedTextLineArray_Get(Clay__WrappedTextLineArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__WRAPPED_TEXT_LINE_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__TextElementData, struct { + Clay_String text; + Clay_Dimensions preferredDimensions; + int32_t elementIndex; + Clay__WrappedTextLineArraySlice wrappedLines; +}); + +Clay__TextElementData CLAY__TEXT_ELEMENT_DATA_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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 +CLAY__TYPEDEF(Clay__TextElementDataArray, struct +{ + int32_t capacity; + int32_t length; + Clay__TextElementData *internalArray; +}); +Clay__TextElementDataArray Clay__TextElementDataArray_Allocate_Arena(int32_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)}; +} +Clay__TextElementData *Clay__TextElementDataArray_Get(Clay__TextElementDataArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__TEXT_ELEMENT_DATA_DEFAULT; +} +Clay__TextElementData *Clay__TextElementDataArray_Add(Clay__TextElementDataArray *array, Clay__TextElementData item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__TEXT_ELEMENT_DATA_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +Clay_BorderElementConfig CLAY__BORDER_ELEMENT_CONFIG_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add TYPE=Clay_BorderElementConfig NAME=Clay__BorderElementConfigArray DEFAULT_VALUE=&CLAY__BORDER_ELEMENT_CONFIG_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__BorderElementConfigArray, struct +{ + int32_t capacity; + int32_t length; + Clay_BorderElementConfig *internalArray; +}); +Clay__BorderElementConfigArray Clay__BorderElementConfigArray_Allocate_Arena(int32_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)}; +} +Clay_BorderElementConfig *Clay__BorderElementConfigArray_Add(Clay__BorderElementConfigArray *array, Clay_BorderElementConfig item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__BORDER_ELEMENT_CONFIG_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__LayoutElementChildren, struct { + int32_t *elements; + uint16_t length; +}); + +CLAY__TYPEDEF(Clay_LayoutElement, struct { + union { + Clay__LayoutElementChildren children; + Clay__TextElementData *textElementData; + } childrenOrTextContent; + Clay_Dimensions dimensions; + Clay_Dimensions minDimensions; + Clay_LayoutConfig *layoutConfig; + Clay__ElementConfigArraySlice elementConfigs; + uint32_t configsEnabled; + uint32_t id; +}); + +Clay_LayoutElement CLAY__LAYOUT_ELEMENT_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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 +CLAY__TYPEDEF(Clay_LayoutElementArray, struct +{ + int32_t capacity; + int32_t length; + Clay_LayoutElement *internalArray; +}); +Clay_LayoutElementArray Clay_LayoutElementArray_Allocate_Arena(int32_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)}; +} +Clay_LayoutElement *Clay_LayoutElementArray_Add(Clay_LayoutElementArray *array, Clay_LayoutElement item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__LAYOUT_ELEMENT_DEFAULT; +} +Clay_LayoutElement *Clay_LayoutElementArray_Get(Clay_LayoutElementArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +// __GENERATED__ template array_define,array_allocate_pointer,array_add,array_get_value,array_remove_swapback TYPE=Clay_LayoutElement* NAME=Clay__LayoutElementPointerArray DEFAULT_VALUE=CLAY__NULL +#pragma region generated +CLAY__TYPEDEF(Clay__LayoutElementPointerArray, struct +{ + int32_t capacity; + int32_t length; + Clay_LayoutElement* *internalArray; +}); +Clay__LayoutElementPointerArray Clay__LayoutElementPointerArray_Allocate_Arena(int32_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__POINTER_ALIGNMENT, arena)}; +} +Clay_LayoutElement* *Clay__LayoutElementPointerArray_Add(Clay__LayoutElementPointerArray *array, Clay_LayoutElement* item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return CLAY__NULL; +} +Clay_LayoutElement* Clay__LayoutElementPointerArray_Get(Clay__LayoutElementPointerArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? array->internalArray[index] : CLAY__NULL; +} +Clay_LayoutElement* Clay__LayoutElementPointerArray_RemoveSwapback(Clay__LayoutElementPointerArray *array, int32_t index) { + if (Clay__Array_RangeCheck(index, array->length)) { + array->length--; + Clay_LayoutElement* removed = array->internalArray[index]; + array->internalArray[index] = array->internalArray[array->length]; + return removed; + } + return CLAY__NULL; +} +#pragma endregion +// __GENERATED__ template + +Clay_RenderCommand CLAY__RENDER_COMMAND_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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(int32_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)}; +} +Clay_RenderCommand *Clay_RenderCommandArray_Add(Clay_RenderCommandArray *array, Clay_RenderCommand item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__RENDER_COMMAND_DEFAULT; +} +Clay_RenderCommand *Clay_RenderCommandArray_Get(Clay_RenderCommandArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__RENDER_COMMAND_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__ScrollContainerDataInternal, struct { + Clay_LayoutElement *layoutElement; + Clay_BoundingBox boundingBox; + Clay_Dimensions contentSize; + Clay_Vector2 scrollOrigin; + Clay_Vector2 pointerOrigin; + Clay_Vector2 scrollMomentum; + Clay_Vector2 scrollPosition; + Clay_Vector2 previousDelta; + float momentumTime; + uint32_t elementId; + bool openThisFrame; + bool pointerScrollActive; +}); + +Clay__ScrollContainerDataInternal CLAY__SCROLL_CONTAINER_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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 +CLAY__TYPEDEF(Clay__ScrollContainerDataInternalArray, struct +{ + int32_t capacity; + int32_t length; + Clay__ScrollContainerDataInternal *internalArray; +}); +Clay__ScrollContainerDataInternalArray Clay__ScrollContainerDataInternalArray_Allocate_Arena(int32_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)}; +} +Clay__ScrollContainerDataInternal *Clay__ScrollContainerDataInternalArray_Add(Clay__ScrollContainerDataInternalArray *array, Clay__ScrollContainerDataInternal item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__SCROLL_CONTAINER_DEFAULT; +} +Clay__ScrollContainerDataInternal *Clay__ScrollContainerDataInternalArray_Get(Clay__ScrollContainerDataInternalArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__SCROLL_CONTAINER_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +// __GENERATED__ template array_remove_swapback TYPE=Clay__ScrollContainerDataInternal NAME=Clay__ScrollContainerDataInternalArray DEFAULT_VALUE=CLAY__SCROLL_CONTAINER_DEFAULT +#pragma region generated +Clay__ScrollContainerDataInternal Clay__ScrollContainerDataInternalArray_RemoveSwapback(Clay__ScrollContainerDataInternalArray *array, int32_t index) { + if (Clay__Array_RangeCheck(index, array->length)) { + array->length--; + Clay__ScrollContainerDataInternal removed = array->internalArray[index]; + array->internalArray[index] = array->internalArray[array->length]; + return removed; + } + return CLAY__SCROLL_CONTAINER_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__DebugElementData, struct { + bool collision; + bool collapsed; +}); + +Clay__DebugElementData CLAY__DEBUG_ELEMENT_DATA_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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 +CLAY__TYPEDEF(Clay__DebugElementDataArray, struct +{ + int32_t capacity; + int32_t length; + Clay__DebugElementData *internalArray; +}); +Clay__DebugElementDataArray Clay__DebugElementDataArray_Allocate_Arena(int32_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)}; +} +Clay__DebugElementData *Clay__DebugElementDataArray_Add(Clay__DebugElementDataArray *array, Clay__DebugElementData item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__DEBUG_ELEMENT_DATA_DEFAULT; +} +Clay__DebugElementData *Clay__DebugElementDataArray_Get(Clay__DebugElementDataArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__DEBUG_ELEMENT_DATA_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay_LayoutElementHashMapItem, 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_PointerData pointerInfo, intptr_t userData); + intptr_t hoverFunctionUserData; + int32_t nextIndex; + uint32_t generation; + Clay__DebugElementData *debugData; +}); + +Clay_LayoutElementHashMapItem CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT = { .layoutElement = &CLAY__LAYOUT_ELEMENT_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 +CLAY__TYPEDEF(Clay__LayoutElementHashMapItemArray, struct +{ + int32_t capacity; + int32_t length; + Clay_LayoutElementHashMapItem *internalArray; +}); +Clay__LayoutElementHashMapItemArray Clay__LayoutElementHashMapItemArray_Allocate_Arena(int32_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)}; +} +Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Get(Clay__LayoutElementHashMapItemArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT; +} +Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Add(Clay__LayoutElementHashMapItemArray *array, Clay_LayoutElementHashMapItem item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__MeasuredWord, struct { + int32_t startOffset; + int32_t length; + float width; + int32_t next; +}); + +Clay__MeasuredWord CLAY__MEASURED_WORD_DEFAULT = { .next = -1 }; + +// __GENERATED__ template array_define,array_allocate,array_get,array_set,array_add TYPE=Clay__MeasuredWord NAME=Clay__MeasuredWordArray DEFAULT_VALUE=&CLAY__MEASURED_WORD_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__MeasuredWordArray, struct +{ + int32_t capacity; + int32_t length; + Clay__MeasuredWord *internalArray; +}); +Clay__MeasuredWordArray Clay__MeasuredWordArray_Allocate_Arena(int32_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, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__MEASURED_WORD_DEFAULT; +} +void Clay__MeasuredWordArray_Set(Clay__MeasuredWordArray *array, int32_t index, Clay__MeasuredWord value) { + if (Clay__Array_RangeCheck(index, array->capacity)) { + array->internalArray[index] = value; + array->length = index < array->length ? array->length : index + 1; + } +} +Clay__MeasuredWord *Clay__MeasuredWordArray_Add(Clay__MeasuredWordArray *array, Clay__MeasuredWord item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__MEASURED_WORD_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__MeasureTextCacheItem, struct { + Clay_Dimensions unwrappedDimensions; + int32_t measuredWordsStartIndex; + bool containsNewlines; + // Hash map data + uint32_t id; + int32_t nextIndex; + uint32_t generation; +}); + +Clay__MeasureTextCacheItem CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT = { .measuredWordsStartIndex = -1 }; + +// __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 +CLAY__TYPEDEF(Clay__MeasureTextCacheItemArray, struct +{ + int32_t capacity; + int32_t length; + Clay__MeasureTextCacheItem *internalArray; +}); +Clay__MeasureTextCacheItemArray Clay__MeasureTextCacheItemArray_Allocate_Arena(int32_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)}; +} +Clay__MeasureTextCacheItem *Clay__MeasureTextCacheItemArray_Get(Clay__MeasureTextCacheItemArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT; +} +Clay__MeasureTextCacheItem *Clay__MeasureTextCacheItemArray_Add(Clay__MeasureTextCacheItemArray *array, Clay__MeasureTextCacheItem item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT; +} +void Clay__MeasureTextCacheItemArray_Set(Clay__MeasureTextCacheItemArray *array, int32_t index, Clay__MeasureTextCacheItem value) { + if (Clay__Array_RangeCheck(index, array->capacity)) { + array->internalArray[index] = value; + array->length = index < array->length ? array->length : index + 1; + } +} +#pragma endregion +// __GENERATED__ template + +// __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 +CLAY__TYPEDEF(Clay__int32_tArray, struct +{ + int32_t capacity; + int32_t length; + int32_t *internalArray; +}); +Clay__int32_tArray Clay__int32_tArray_Allocate_Arena(int32_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)}; +} +int32_t Clay__int32_tArray_Get(Clay__int32_tArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? array->internalArray[index] : -1; +} +void Clay__int32_tArray_Add(Clay__int32_tArray *array, int32_t item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + } +} +void Clay__int32_tArray_Set(Clay__int32_tArray *array, int32_t index, int32_t value) { + if (Clay__Array_RangeCheck(index, array->capacity)) { + array->internalArray[index] = value; + array->length = index < array->length ? array->length : index + 1; + } +} +int32_t Clay__int32_tArray_RemoveSwapback(Clay__int32_tArray *array, int32_t 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__TYPEDEF(Clay__LayoutElementTreeNode, struct { + Clay_LayoutElement *layoutElement; + Clay_Vector2 position; + Clay_Vector2 nextChildOffset; +}); + +Clay__LayoutElementTreeNode CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __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 +CLAY__TYPEDEF(Clay__LayoutElementTreeNodeArray, struct +{ + int32_t capacity; + int32_t length; + Clay__LayoutElementTreeNode *internalArray; +}); +Clay__LayoutElementTreeNodeArray Clay__LayoutElementTreeNodeArray_Allocate_Arena(int32_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)}; +} +Clay__LayoutElementTreeNode *Clay__LayoutElementTreeNodeArray_Add(Clay__LayoutElementTreeNodeArray *array, Clay__LayoutElementTreeNode item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT; +} +Clay__LayoutElementTreeNode *Clay__LayoutElementTreeNodeArray_Get(Clay__LayoutElementTreeNodeArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + +CLAY__TYPEDEF(Clay__LayoutElementTreeRoot, struct { + int32_t layoutElementIndex; + uint32_t parentId; // This can be zero in the case of the root layout tree + uint32_t clipElementId; // This can be zero if there is no clip element + int32_t zIndex; + Clay_Vector2 pointerOffset; // Only used when scroll containers are managed externally +}); + +Clay__LayoutElementTreeRoot CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT = CLAY__DEFAULT_STRUCT; + +// __GENERATED__ template array_define,array_allocate,array_add,array_get,array_set TYPE=Clay__LayoutElementTreeRoot NAME=Clay__LayoutElementTreeRootArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT +#pragma region generated +CLAY__TYPEDEF(Clay__LayoutElementTreeRootArray, struct +{ + int32_t capacity; + int32_t length; + Clay__LayoutElementTreeRoot *internalArray; +}); +Clay__LayoutElementTreeRootArray Clay__LayoutElementTreeRootArray_Allocate_Arena(int32_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)}; +} +Clay__LayoutElementTreeRoot *Clay__LayoutElementTreeRootArray_Add(Clay__LayoutElementTreeRootArray *array, Clay__LayoutElementTreeRoot item) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT; +} +Clay__LayoutElementTreeRoot *Clay__LayoutElementTreeRootArray_Get(Clay__LayoutElementTreeRootArray *array, int32_t index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT; +} +void Clay__LayoutElementTreeRootArray_Set(Clay__LayoutElementTreeRootArray *array, int32_t index, Clay__LayoutElementTreeRoot value) { + if (Clay__Array_RangeCheck(index, array->capacity)) { + array->internalArray[index] = value; + array->length = index < array->length ? array->length : index + 1; + } +} +#pragma endregion +// __GENERATED__ template + +// __GENERATED__ template array_define,array_allocate TYPE=uint8_t NAME=Clay__CharArray DEFAULT_VALUE=0 +#pragma region generated +CLAY__TYPEDEF(Clay__CharArray, struct +{ + int32_t capacity; + int32_t length; + uint8_t *internalArray; +}); +Clay__CharArray Clay__CharArray_Allocate_Arena(int32_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)}; +} +#pragma endregion +// __GENERATED__ template + +struct Clay_Context { + int32_t maxElementCount; + int32_t maxMeasureTextCacheWordCount; + bool warningsEnabled; + Clay_ErrorHandler errorHandler; + Clay_BooleanWarnings booleanWarnings; + Clay__WarningArray warnings; + + Clay_PointerData pointerInfo; + Clay_Dimensions layoutDimensions; + Clay_ElementId dynamicElementIndexBaseHash; + uint32_t dynamicElementIndex; + bool debugModeEnabled; + bool disableCulling; + bool externalScrollHandlingEnabled; + uint32_t debugSelectedElementId; + uint32_t generation; + uintptr_t arenaResetOffset; + Clay_Arena internalArena; + // Layout Elements / Render Commands + Clay_LayoutElementArray layoutElements; + Clay_RenderCommandArray renderCommands; + Clay__int32_tArray openLayoutElementStack; + Clay__int32_tArray layoutElementChildren; + Clay__int32_tArray layoutElementChildrenBuffer; + Clay__TextElementDataArray textElementData; + Clay__LayoutElementPointerArray imageElementPointers; + Clay__int32_tArray reusableElementIndexBuffer; + Clay__int32_tArray layoutElementClipElementIds; + // Configs + Clay__LayoutConfigArray layoutConfigs; + Clay__ElementConfigArray elementConfigBuffer; + Clay__ElementConfigArray elementConfigs; + Clay__RectangleElementConfigArray rectangleElementConfigs; + Clay__TextElementConfigArray textElementConfigs; + Clay__ImageElementConfigArray imageElementConfigs; + Clay__FloatingElementConfigArray floatingElementConfigs; + Clay__ScrollElementConfigArray scrollElementConfigs; + Clay__CustomElementConfigArray customElementConfigs; + Clay__BorderElementConfigArray borderElementConfigs; + // Misc Data Structures + Clay__StringArray layoutElementIdStrings; + Clay__WrappedTextLineArray wrappedTextLines; + Clay__LayoutElementTreeNodeArray layoutElementTreeNodeArray1; + Clay__LayoutElementTreeRootArray layoutElementTreeRoots; + Clay__LayoutElementHashMapItemArray layoutElementsHashMapInternal; + Clay__int32_tArray layoutElementsHashMap; + Clay__MeasureTextCacheItemArray measureTextHashMapInternal; + Clay__int32_tArray measureTextHashMapInternalFreeList; + Clay__int32_tArray measureTextHashMap; + Clay__MeasuredWordArray measuredWords; + Clay__int32_tArray measuredWordsFreeList; + Clay__int32_tArray openClipElementStack; + Clay__ElementIdArray pointerOverIds; + Clay__ScrollContainerDataInternalArray scrollContainerDatas; + Clay__BoolArray treeNodeVisited; + Clay__CharArray dynamicStringData; + Clay__DebugElementDataArray debugElementData; +}; + +struct Clay__AlignClay_Context { + char c; + Clay_Context x; +}; +typedef struct { + Clay_Context wrapped; +} Clay__Clay_ContextWrapper; + +Clay_Context* Clay__Context_Allocate_Arena(Clay_Arena *arena) { + uint32_t alignment = CLAY__ALIGNMENT(Clay_Context); + size_t totalSizeBytes = sizeof(Clay_Context); + uintptr_t nextAllocAddress = arena->nextAllocation + (uintptr_t)arena->memory; + uintptr_t arenaOffsetAligned = nextAllocAddress + (alignment - (nextAllocAddress & alignment)); + arenaOffsetAligned -= (uintptr_t)arena->memory; + if (arenaOffsetAligned + totalSizeBytes > arena->capacity) + { + return NULL; + } + arena->nextAllocation = arenaOffsetAligned + totalSizeBytes; + return (Clay_Context*)((uintptr_t)arena->memory + arenaOffsetAligned); +} + +Clay_String Clay__WriteStringToCharBuffer(Clay__CharArray *buffer, Clay_String string) { + for (int32_t i = 0; i < string.length; i++) { + buffer->internalArray[buffer->length + i] = string.chars[i]; + } + buffer->length += string.length; + return CLAY__INIT(Clay_String) { .length = string.length, .chars = (const char *)(buffer->internalArray + buffer->length - string.length) }; +} + +#ifdef CLAY_WASM + __attribute__((import_module("clay"), import_name("measureTextFunction"))) Clay_Dimensions Clay__MeasureText(Clay_String *text, Clay_TextElementConfig *config); + __attribute__((import_module("clay"), import_name("queryScrollOffsetFunction"))) Clay_Vector2 Clay__QueryScrollOffset(uint32_t elementId); +#else + Clay_Dimensions (*Clay__MeasureText)(Clay_String *text, Clay_TextElementConfig *config); + Clay_Vector2 (*Clay__QueryScrollOffset)(uint32_t elementId); +#endif + +Clay_LayoutElement* Clay__GetOpenLayoutElement(void) { + Clay_Context* context = Clay_GetCurrentContext(); + return Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_Get(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1)); +} + +uint32_t Clay__GetParentElementId(void) { + Clay_Context* context = Clay_GetCurrentContext(); + return Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_Get(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2))->id; +} + +bool Clay__ElementHasConfig(Clay_LayoutElement *element, Clay__ElementConfigType type) { + return (element->configsEnabled & type); +} + +Clay_ElementConfigUnion Clay__FindElementConfigWithType(Clay_LayoutElement *element, Clay__ElementConfigType type) { + for (int32_t 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; + uint32_t base = seed; + + for (int32_t i = 0; i < key.length; i++) { + base += key.chars[i]; + base += (base << 10); + base ^= (base >> 6); + } + hash = base; + hash += offset; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += (hash << 3); + base += (base << 3); + hash ^= (hash >> 11); + base ^= (base >> 11); + hash += (hash << 15); + base += (base << 15); + return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as "null id" +} + +Clay_ElementId Clay__Rehash(Clay_ElementId elementId, uint32_t number) { + uint32_t id = elementId.baseId; + id += number; + id += (id << 10); + id ^= (id >> 6); + + id += (id << 3); + id ^= (id >> 11); + id += (id << 15); + return CLAY__INIT(Clay_ElementId) { .id = id, .offset = number, .baseId = elementId.baseId, .stringId = elementId.stringId }; +} + +uint32_t Clay__RehashWithNumber(uint32_t id, uint32_t number) { + id += number; + id += (id << 10); + id ^= (id >> 6); + + id += (id << 3); + id ^= (id >> 11); + id += (id << 15); + return id; +} + +uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *config) { + uint32_t hash = 0; + uintptr_t pointerAsNumber = (uintptr_t)text->chars; + + hash += pointerAsNumber; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += text->length; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += config->fontId; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += config->fontSize; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += config->lineHeight; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += config->letterSpacing; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += config->wrapMode; + hash += (hash << 10); + hash ^= (hash >> 6); + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash + 1; // Reserve the hash result of zero as "null id" +} + +Clay__MeasuredWord *Clay__AddMeasuredWord(Clay__MeasuredWord word, Clay__MeasuredWord *previousWord) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->measuredWordsFreeList.length > 0) { + uint32_t newItemIndex = Clay__int32_tArray_Get(&context->measuredWordsFreeList, (int)context->measuredWordsFreeList.length - 1); + context->measuredWordsFreeList.length--; + Clay__MeasuredWordArray_Set(&context->measuredWords, (int)newItemIndex, word); + previousWord->next = (int32_t)newItemIndex; + return Clay__MeasuredWordArray_Get(&context->measuredWords, (int)newItemIndex); + } else { + previousWord->next = (int32_t)context->measuredWords.length; + return Clay__MeasuredWordArray_Add(&context->measuredWords, word); + } +} + +Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) { + Clay_Context* context = Clay_GetCurrentContext(); + #ifndef CLAY_WASM + if (!Clay__MeasureText) { + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED, + .errorText = CLAY_STRING("Clay's internal MeasureText function is null. You may have forgotten to call Clay_SetMeasureTextFunction(), or passed a NULL function pointer by mistake."), + .userData = context->errorHandler.userData }); + return NULL; + } + #endif + uint32_t id = Clay__HashTextWithConfig(text, config); + uint32_t hashBucket = id % (context->maxMeasureTextCacheWordCount / 32); + int32_t elementIndexPrevious = 0; + int32_t elementIndex = context->measureTextHashMap.internalArray[hashBucket]; + while (elementIndex != 0) { + Clay__MeasureTextCacheItem *hashEntry = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndex); + if (hashEntry->id == id) { + hashEntry->generation = context->generation; + return hashEntry; + } + // This element hasn't been seen in a few frames, delete the hash map item + if (context->generation - hashEntry->generation > 2) { + // Add all the measured words that were included in this measurement to the freelist + int32_t nextWordIndex = hashEntry->measuredWordsStartIndex; + while (nextWordIndex != -1) { + Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&context->measuredWords, nextWordIndex); + Clay__int32_tArray_Add(&context->measuredWordsFreeList, nextWordIndex); + nextWordIndex = measuredWord->next; + } + + int32_t nextIndex = hashEntry->nextIndex; + Clay__MeasureTextCacheItemArray_Set(&context->measureTextHashMapInternal, elementIndex, CLAY__INIT(Clay__MeasureTextCacheItem) { .measuredWordsStartIndex = -1 }); + Clay__int32_tArray_Add(&context->measureTextHashMapInternalFreeList, elementIndex); + if (elementIndexPrevious == 0) { + context->measureTextHashMap.internalArray[hashBucket] = nextIndex; + } else { + Clay__MeasureTextCacheItem *previousHashEntry = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndexPrevious); + previousHashEntry->nextIndex = nextIndex; + } + elementIndex = nextIndex; + } else { + elementIndexPrevious = elementIndex; + elementIndex = hashEntry->nextIndex; + } + } + + int32_t newItemIndex = 0; + Clay__MeasureTextCacheItem newCacheItem = { .measuredWordsStartIndex = -1, .id = id, .generation = context->generation }; + Clay__MeasureTextCacheItem *measured = NULL; + if (context->measureTextHashMapInternalFreeList.length > 0) { + newItemIndex = Clay__int32_tArray_Get(&context->measureTextHashMapInternalFreeList, context->measureTextHashMapInternalFreeList.length - 1); + context->measureTextHashMapInternalFreeList.length--; + Clay__MeasureTextCacheItemArray_Set(&context->measureTextHashMapInternal, newItemIndex, newCacheItem); + measured = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, newItemIndex); + } else { + if (context->measureTextHashMapInternal.length == context->measureTextHashMapInternal.capacity - 1) { + if (context->booleanWarnings.maxTextMeasureCacheExceeded) { + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED, + .errorText = CLAY_STRING("Clay ran out of capacity while attempting to measure text elements. Try using Clay_SetMaxElementCount() with a higher value."), + .userData = context->errorHandler.userData }); + context->booleanWarnings.maxTextMeasureCacheExceeded = true; + } + return &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT; + } + measured = Clay__MeasureTextCacheItemArray_Add(&context->measureTextHashMapInternal, newCacheItem); + newItemIndex = context->measureTextHashMapInternal.length - 1; + } + + int32_t start = 0; + int32_t end = 0; + float lineWidth = 0; + float measuredWidth = 0; + float measuredHeight = 0; + float spaceWidth = Clay__MeasureText(&CLAY__SPACECHAR, config).width; + Clay__MeasuredWord tempWord = { .next = -1 }; + Clay__MeasuredWord *previousWord = &tempWord; + while (end < text->length) { + if (context->measuredWords.length == context->measuredWords.capacity - 1) { + if (!context->booleanWarnings.maxTextMeasureCacheExceeded) { + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED, + .errorText = CLAY_STRING("Clay has run out of space in it's internal text measurement cache. Try using Clay_SetMaxMeasureTextCacheWordCount() (default 16384, with 1 unit storing 1 measured word)."), + .userData = context->errorHandler.userData }); + context->booleanWarnings.maxTextMeasureCacheExceeded = true; + } + return &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT; + } + char current = text->chars[end]; + if (current == ' ' || current == '\n') { + int32_t length = end - start; + Clay_String word = { .length = length, .chars = &text->chars[start] }; + Clay_Dimensions dimensions = Clay__MeasureText(&word, config); + measuredHeight = CLAY__MAX(measuredHeight, dimensions.height); + if (current == ' ') { + dimensions.width += spaceWidth; + previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = length + 1, .width = dimensions.width, .next = -1 }, previousWord); + lineWidth += dimensions.width; + } + if (current == '\n') { + if (length > 0) { + previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = length, .width = dimensions.width, .next = -1 }, previousWord); + } + previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = end + 1, .length = 0, .width = 0, .next = -1 }, previousWord); + lineWidth += dimensions.width; + measuredWidth = CLAY__MAX(lineWidth, measuredWidth); + measured->containsNewlines = true; + lineWidth = 0; + } + start = end + 1; + } + end++; + } + if (end - start > 0) { + Clay_String lastWord = { .length = end - start, .chars = &text->chars[start] }; + Clay_Dimensions dimensions = Clay__MeasureText(&lastWord, config); + Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = end - start, .width = dimensions.width, .next = -1 }, previousWord); + lineWidth += dimensions.width; + measuredHeight = CLAY__MAX(measuredHeight, dimensions.height); + } + measuredWidth = CLAY__MAX(lineWidth, measuredWidth); + + measured->measuredWordsStartIndex = tempWord.next; + measured->unwrappedDimensions.width = measuredWidth; + measured->unwrappedDimensions.height = measuredHeight; + + if (elementIndexPrevious != 0) { + Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndexPrevious)->nextIndex = newItemIndex; + } else { + context->measureTextHashMap.internalArray[hashBucket] = newItemIndex; + } + return measured; +} + +bool Clay__PointIsInsideRect(Clay_Vector2 point, Clay_BoundingBox rect) { + return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height; +} + +Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Clay_LayoutElement* layoutElement) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->layoutElementsHashMapInternal.length == context->layoutElementsHashMapInternal.capacity - 1) { + return NULL; + } + Clay_LayoutElementHashMapItem item = { .elementId = elementId, .layoutElement = layoutElement, .nextIndex = -1, .generation = context->generation + 1 }; + uint32_t hashBucket = elementId.id % context->layoutElementsHashMap.capacity; + int32_t hashItemPrevious = -1; + int32_t hashItemIndex = context->layoutElementsHashMap.internalArray[hashBucket]; + while (hashItemIndex != -1) { // Just replace collision, not a big deal - leave it up to the end user + Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, hashItemIndex); + if (hashItem->elementId.id == elementId.id) { // Collision - resolve based on generation + item.nextIndex = hashItem->nextIndex; + if (hashItem->generation <= context->generation) { // First collision - assume this is the "same" element + hashItem->generation = context->generation + 1; + hashItem->layoutElement = layoutElement; + hashItem->debugData->collision = false; + } else { // Multiple collisions this frame - two elements have the same ID + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_DUPLICATE_ID, + .errorText = CLAY_STRING("An element with this ID was already previously declared during this layout."), + .userData = context->errorHandler.userData }); + if (context->debugModeEnabled) { + hashItem->debugData->collision = true; + } + } + return hashItem; + } + hashItemPrevious = hashItemIndex; + hashItemIndex = hashItem->nextIndex; + } + Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Add(&context->layoutElementsHashMapInternal, item); + hashItem->debugData = Clay__DebugElementDataArray_Add(&context->debugElementData, CLAY__INIT(Clay__DebugElementData) CLAY__DEFAULT_STRUCT); + if (hashItemPrevious != -1) { + Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, hashItemPrevious)->nextIndex = (int32_t)context->layoutElementsHashMapInternal.length - 1; + } else { + context->layoutElementsHashMap.internalArray[hashBucket] = (int32_t)context->layoutElementsHashMapInternal.length - 1; + } + return hashItem; +} + +Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) { + Clay_Context* context = Clay_GetCurrentContext(); + uint32_t hashBucket = id % context->layoutElementsHashMap.capacity; + int32_t elementIndex = context->layoutElementsHashMap.internalArray[hashBucket]; + while (elementIndex != -1) { + Clay_LayoutElementHashMapItem *hashEntry = Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, elementIndex); + if (hashEntry->elementId.id == id) { + return hashEntry; + } + elementIndex = hashEntry->nextIndex; + } + return &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT; +} + +void Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) { + Clay_Context* context = Clay_GetCurrentContext(); + Clay_LayoutElement *parentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_Get(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2)); + Clay_ElementId elementId = Clay__HashNumber(parentElement->childrenOrTextContent.children.length, parentElement->id); + openLayoutElement->id = elementId.id; + Clay__AddHashMapItem(elementId, openLayoutElement); + Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId); +} + +void Clay__ElementPostConfiguration(void) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + // ID + if (openLayoutElement->id == 0) { + Clay__GenerateIdForAnonymousElement(openLayoutElement); + } + // Layout Config + if (!openLayoutElement->layoutConfig) { + openLayoutElement->layoutConfig = &CLAY_LAYOUT_DEFAULT; + } + + // Loop through element configs and handle special cases + openLayoutElement->elementConfigs.internalArray = &context->elementConfigs.internalArray[context->elementConfigs.length]; + for (int32_t elementConfigIndex = 0; elementConfigIndex < openLayoutElement->elementConfigs.length; elementConfigIndex++) { + Clay_ElementConfig *config = Clay__ElementConfigArray_Add(&context->elementConfigs, *Clay__ElementConfigArray_Get(&context->elementConfigBuffer, context->elementConfigBuffer.length - openLayoutElement->elementConfigs.length + elementConfigIndex)); + openLayoutElement->configsEnabled |= config->type; + switch (config->type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: + 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(&context->layoutElements, Clay__int32_tArray_Get(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2)); + if (!hierarchicalParent) { + break; + } + uint32_t 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(&context->floatingElementConfigs, newConfig); + config->config.floatingElementConfig = floatingConfig; + if (context->openClipElementStack.length > 0) { + clipElementId = Clay__int32_tArray_Get(&context->openClipElementStack, (int)context->openClipElementStack.length - 1); + } + } else { + Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig->parentId); + if (!parentItem) { + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND, + .errorText = CLAY_STRING("A floating element was declared with a parentId, but no element with that ID was found."), + .userData = context->errorHandler.userData }); + } else { + clipElementId = Clay__int32_tArray_Get(&context->layoutElementClipElementIds, parentItem->layoutElement - context->layoutElements.internalArray); + } + } + Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { + .layoutElementIndex = Clay__int32_tArray_Get(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1), + .parentId = floatingConfig->parentId, + .clipElementId = clipElementId, + .zIndex = floatingConfig->zIndex, + }); + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: { + Clay__int32_tArray_Add(&context->openClipElementStack, (int)openLayoutElement->id); + // Retrieve or create cached data to track scroll position across frames + Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL; + for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (openLayoutElement->id == mapping->elementId) { + scrollOffset = mapping; + scrollOffset->layoutElement = openLayoutElement; + scrollOffset->openThisFrame = true; + } + } + if (!scrollOffset) { + scrollOffset = Clay__ScrollContainerDataInternalArray_Add(&context->scrollContainerDatas, CLAY__INIT(Clay__ScrollContainerDataInternal){.layoutElement = openLayoutElement, .scrollOrigin = {-1,-1}, .elementId = openLayoutElement->id, .openThisFrame = true}); + } + if (context->externalScrollHandlingEnabled) { + scrollOffset->scrollPosition = Clay__QueryScrollOffset(scrollOffset->elementId); + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: break; + case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { + Clay__LayoutElementPointerArray_Add(&context->imageElementPointers, openLayoutElement); + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: + default: break; + } + } + context->elementConfigBuffer.length -= openLayoutElement->elementConfigs.length; +} + +void Clay__CloseElement(void) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + 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; + context->openClipElementStack.length--; + } + + // Attach children to the current open element + openLayoutElement->childrenOrTextContent.children.elements = &context->layoutElementChildren.internalArray[context->layoutElementChildren.length]; + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + openLayoutElement->dimensions.width = (float)(layoutConfig->padding.left + layoutConfig->padding.right); + for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) { + int32_t childIndex = Clay__int32_tArray_Get(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i); + Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex); + openLayoutElement->dimensions.width += child->dimensions.width; + openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + layoutConfig->padding.top + layoutConfig->padding.bottom); + // 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.top + layoutConfig->padding.bottom); + } + Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex); + } + float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.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 = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom); + for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) { + int32_t childIndex = Clay__int32_tArray_Get(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i); + Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex); + openLayoutElement->dimensions.height += child->dimensions.height; + openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + layoutConfig->padding.left + layoutConfig->padding.right); + // 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.left + layoutConfig->padding.right); + } + Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex); + } + float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.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; + } + + context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.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.size.minMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier + layoutConfig->sizing.width.size.minMax.max = CLAY__MAXFLOAT; + } + openLayoutElement->dimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.width, layoutConfig->sizing.width.size.minMax.min), layoutConfig->sizing.width.size.minMax.max); + openLayoutElement->minDimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.width, layoutConfig->sizing.width.size.minMax.min), layoutConfig->sizing.width.size.minMax.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.size.minMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier + layoutConfig->sizing.height.size.minMax.max = CLAY__MAXFLOAT; + } + openLayoutElement->dimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.height, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max); + openLayoutElement->minDimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.height, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max); + } else { + openLayoutElement->dimensions.height = 0; + } + + bool elementIsFloating = Clay__ElementHasConfig(openLayoutElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER); + + // Close the currently open element + int32_t closingElementIndex = Clay__int32_tArray_RemoveSwapback(&context->openLayoutElementStack, (int)context->openLayoutElementStack.length - 1); + openLayoutElement = Clay__GetOpenLayoutElement(); + + if (!elementIsFloating && context->openLayoutElementStack.length > 1) { + openLayoutElement->childrenOrTextContent.children.length++; + Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, closingElementIndex); + } +} + +void Clay__OpenElement(void) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) { + context->booleanWarnings.maxElementsExceeded = true; + return; + } + Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT; + Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement); + Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1); + if (context->openClipElementStack.length > 0) { + Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_Get(&context->openClipElementStack, (int)context->openClipElementStack.length - 1)); + } else { + Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0); + } +} + +void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) { + context->booleanWarnings.maxElementsExceeded = true; + return; + } + Clay_LayoutElement *parentElement = Clay__GetOpenLayoutElement(); + parentElement->childrenOrTextContent.children.length++; + + Clay__OpenElement(); + Clay_LayoutElement * openLayoutElement = Clay__GetOpenLayoutElement(); + Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, context->layoutElements.length - 1); + Clay__MeasureTextCacheItem *textMeasured = Clay__MeasureTextCached(&text, textConfig); + Clay_ElementId elementId = Clay__HashString(CLAY_STRING("Text"), parentElement->childrenOrTextContent.children.length, parentElement->id); + openLayoutElement->id = elementId.id; + Clay__AddHashMapItem(elementId, openLayoutElement); + Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId); + Clay_Dimensions textDimensions = { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? (float)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->childrenOrTextContent.textElementData = Clay__TextElementDataArray_Add(&context->textElementData, CLAY__INIT(Clay__TextElementData) { .text = text, .preferredDimensions = textMeasured->unwrappedDimensions, .elementIndex = context->layoutElements.length - 1 }); + openLayoutElement->elementConfigs = CLAY__INIT(Clay__ElementConfigArraySlice) { + .length = 1, + .internalArray = Clay__ElementConfigArray_Add(&context->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(&context->openLayoutElementStack, (int)context->openLayoutElementStack.length - 1); +} + +void Clay__InitializeEphemeralMemory(Clay_Context* context) { + int32_t maxElementCount = context->maxElementCount; + // Ephemeral Memory - reset every frame + Clay_Arena *arena = &context->internalArena; + arena->nextAllocation = context->arenaResetOffset; + + context->layoutElementChildrenBuffer = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->layoutElements = Clay_LayoutElementArray_Allocate_Arena(maxElementCount, arena); + context->warnings = Clay__WarningArray_Allocate_Arena(100, arena); + + context->layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(maxElementCount, arena); + context->elementConfigBuffer = Clay__ElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->elementConfigs = Clay__ElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->rectangleElementConfigs = Clay__RectangleElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->scrollElementConfigs = Clay__ScrollElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->customElementConfigs = Clay__CustomElementConfigArray_Allocate_Arena(maxElementCount, arena); + context->borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(maxElementCount, arena); + + context->layoutElementIdStrings = Clay__StringArray_Allocate_Arena(maxElementCount, arena); + context->wrappedTextLines = Clay__WrappedTextLineArray_Allocate_Arena(maxElementCount, arena); + context->layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(maxElementCount, arena); + context->layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(maxElementCount, arena); + context->layoutElementChildren = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->textElementData = Clay__TextElementDataArray_Allocate_Arena(maxElementCount, arena); + context->imageElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(maxElementCount, arena); + context->renderCommands = Clay_RenderCommandArray_Allocate_Arena(maxElementCount, arena); + context->treeNodeVisited = Clay__BoolArray_Allocate_Arena(maxElementCount, arena); + context->treeNodeVisited.length = context->treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list + context->openClipElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->reusableElementIndexBuffer = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->layoutElementClipElementIds = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->dynamicStringData = Clay__CharArray_Allocate_Arena(maxElementCount, arena); +} + +void Clay__InitializePersistentMemory(Clay_Context* context) { + // Persistent memory - initialized once and not reset + int32_t maxElementCount = context->maxElementCount; + int32_t maxMeasureTextCacheWordCount = context->maxMeasureTextCacheWordCount; + Clay_Arena *arena = &context->internalArena; + + context->scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(10, arena); + context->layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(maxElementCount, arena); + context->layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(maxElementCount, arena); + context->measureTextHashMapInternalFreeList = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->measuredWordsFreeList = Clay__int32_tArray_Allocate_Arena(maxMeasureTextCacheWordCount, arena); + context->measureTextHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); + context->measuredWords = Clay__MeasuredWordArray_Allocate_Arena(maxMeasureTextCacheWordCount, arena); + context->pointerOverIds = Clay__ElementIdArray_Allocate_Arena(maxElementCount, arena); + context->debugElementData = Clay__DebugElementDataArray_Allocate_Arena(maxElementCount, arena); + context->arenaResetOffset = arena->nextAllocation; +} + + +void Clay__CompressChildrenAlongAxis(bool xAxis, float totalSizeToDistribute, Clay__int32_tArray resizableContainerBuffer) { + Clay_Context* context = Clay_GetCurrentContext(); + Clay__int32_tArray largestContainers = context->openClipElementStack; + largestContainers.length = 0; + + while (totalSizeToDistribute > 0.1) { + float largestSize = 0; + float targetSize = 0; + for (int32_t i = 0; i < resizableContainerBuffer.length; ++i) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_Get(&resizableContainerBuffer, i)); + if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) { + continue; + } + float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height; + if ((childSize - largestSize) < 0.1 && (childSize - largestSize) > -0.1) { + Clay__int32_tArray_Add(&largestContainers, Clay__int32_tArray_Get(&resizableContainerBuffer, i)); + } else if (childSize > largestSize) { + targetSize = largestSize; + largestSize = childSize; + largestContainers.length = 0; + Clay__int32_tArray_Add(&largestContainers, Clay__int32_tArray_Get(&resizableContainerBuffer, i)); + } + else if (childSize > targetSize) { + targetSize = childSize; + } + } + + targetSize = CLAY__MAX(targetSize, (largestSize * largestContainers.length) - totalSizeToDistribute) / largestContainers.length; + for (int32_t childOffset = 0; childOffset < largestContainers.length; childOffset++) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_Get(&largestContainers, childOffset)); + float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; + float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height; + float oldChildSize = *childSize; + *childSize = CLAY__MAX(childMinSize, targetSize); + totalSizeToDistribute -= (oldChildSize - *childSize); + if (*childSize == childMinSize) { + Clay__int32_tArray_RemoveSwapback(&largestContainers, childOffset); + childOffset--; + } + } + + if (largestContainers.length == 0) { + break; + } + } +} + +void Clay__SizeContainersAlongAxis(bool xAxis) { + Clay_Context* context = Clay_GetCurrentContext(); + Clay__int32_tArray bfsBuffer = context->layoutElementChildrenBuffer; + Clay__int32_tArray resizableContainerBuffer = context->openLayoutElementStack; + for (int32_t rootIndex = 0; rootIndex < context->layoutElementTreeRoots.length; ++rootIndex) { + bfsBuffer.length = 0; + Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex); + Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex); + Clay__int32_tArray_Add(&bfsBuffer, (int32_t)root->layoutElementIndex); + + // Size floating containers to their parents + 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) { + rootElement->dimensions.width = parentLayoutElement->dimensions.width; + } + if (rootElement->layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_GROW) { + rootElement->dimensions.height = parentLayoutElement->dimensions.height; + } + } + } + + rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.size.minMax.min), rootElement->layoutConfig->sizing.width.size.minMax.max); + rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.size.minMax.min), rootElement->layoutConfig->sizing.height.size.minMax.max); + + for (int32_t i = 0; i < bfsBuffer.length; ++i) { + int32_t parentIndex = Clay__int32_tArray_Get(&bfsBuffer, i); + Clay_LayoutElement *parent = Clay_LayoutElementArray_Get(&context->layoutElements, parentIndex); + Clay_LayoutConfig *parentStyleConfig = parent->layoutConfig; + int32_t growContainerCount = 0; + float parentSize = xAxis ? parent->dimensions.width : parent->dimensions.height; + float parentPadding = (float)(xAxis ? (parent->layoutConfig->padding.left + parent->layoutConfig->padding.right) : (parent->layoutConfig->padding.top + parent->layoutConfig->padding.bottom)); + float innerContentSize = 0, growContainerContentSize = 0, totalPaddingAndChildGaps = parentPadding; + bool sizingAlongAxis = (xAxis && parentStyleConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) || (!xAxis && parentStyleConfig->layoutDirection == CLAY_TOP_TO_BOTTOM); + resizableContainerBuffer.length = 0; + float parentChildGap = parentStyleConfig->childGap; + + for (int32_t childOffset = 0; childOffset < parent->childrenOrTextContent.children.length; childOffset++) { + int32_t childElementIndex = parent->childrenOrTextContent.children.elements[childOffset]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, childElementIndex); + Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; + float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height; + + if (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && childElement->childrenOrTextContent.children.length > 0) { + Clay__int32_tArray_Add(&bfsBuffer, childElementIndex); + } + + 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) { + innerContentSize += (childSizing.type == CLAY__SIZING_TYPE_PERCENT ? 0 : childSize); + if (childSizing.type == CLAY__SIZING_TYPE_GROW) { + growContainerContentSize += childSize; + growContainerCount++; + } + if (childOffset > 0) { + innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child + totalPaddingAndChildGaps += parentChildGap; + } + } else { + innerContentSize = CLAY__MAX(childSize, innerContentSize); + } + } + + // Expand percentage containers to size + for (int32_t childOffset = 0; childOffset < parent->childrenOrTextContent.children.length; childOffset++) { + int32_t childElementIndex = parent->childrenOrTextContent.children.elements[childOffset]; + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, childElementIndex); + Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; + float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; + if (childSizing.type == CLAY__SIZING_TYPE_PERCENT) { + *childSize = (parentSize - totalPaddingAndChildGaps) * childSizing.size.percent; + if (sizingAlongAxis) { + innerContentSize += *childSize; + if (childOffset > 0) { + innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child + totalPaddingAndChildGaps += parentChildGap; + } + } else { + innerContentSize = CLAY__MAX(*childSize, innerContentSize); + } + } + } + + if (sizingAlongAxis) { + float sizeToDistribute = parentSize - parentPadding - innerContentSize; + // 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 (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; + } + } + // Scrolling containers preferentially compress before others + Clay__CompressChildrenAlongAxis(xAxis, -sizeToDistribute, resizableContainerBuffer); + // The content is too small, allow SIZING_GROW containers to expand + } else if (sizeToDistribute > 0 && growContainerCount > 0) { + float targetSize = (sizeToDistribute + growContainerContentSize) / (float)growContainerCount; + for (int32_t childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->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__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childOffset); + growContainerCount--; + targetSize = (sizeToDistribute + growContainerContentSize) / (float)growContainerCount; + childOffset = -1; + continue; + } + *childSize = targetSize; + } + } + } + // Sizing along the non layout axis ("off axis") + } else { + for (int32_t childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->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 && 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; + 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.size.minMax.min, CLAY__MIN(*childSize, maxSize)); + } else if (childSizing.type == CLAY__SIZING_TYPE_GROW) { + *childSize = CLAY__MIN(maxSize, childSizing.size.minMax.max); + } + } + } + } + } +} + +Clay_String Clay__IntToString(int32_t integer) { + if (integer == 0) { + return CLAY__INIT(Clay_String) { .length = 1, .chars = "0" }; + } + Clay_Context* context = Clay_GetCurrentContext(); + char *chars = (char *)(context->dynamicStringData.internalArray + context->dynamicStringData.length); + int32_t length = 0; + int32_t 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 (int32_t j = 0, k = length - 1; j < k; j++, k--) { + char temp = chars[j]; + chars[j] = chars[k]; + chars[k] = temp; + } + context->dynamicStringData.length += length; + return CLAY__INIT(Clay_String) { .length = length, .chars = chars }; +} + +void Clay__AddRenderCommand(Clay_RenderCommand renderCommand) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->renderCommands.length < context->renderCommands.capacity - 1) { + Clay_RenderCommandArray_Add(&context->renderCommands, renderCommand); + } else { + if (!context->booleanWarnings.maxRenderCommandsExceeded) { + context->booleanWarnings.maxRenderCommandsExceeded = true; + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED, + .errorText = CLAY_STRING("Clay ran out of capacity while attempting to create render commands. This is usually caused by a large amount of wrapping text elements while close to the max element capacity. Try using Clay_SetMaxElementCount() with a higher value."), + .userData = context->errorHandler.userData }); + } + } +} + +bool Clay__ElementIsOffscreen(Clay_BoundingBox *boundingBox) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->disableCulling) { + return false; + } + + return (boundingBox->x > (float)context->layoutDimensions.width) || + (boundingBox->y > (float)context->layoutDimensions.height) || + (boundingBox->x + boundingBox->width < 0) || + (boundingBox->y + boundingBox->height < 0); +} + +void Clay__CalculateFinalLayout() { + Clay_Context* context = Clay_GetCurrentContext(); + // Calculate sizing along the X axis + Clay__SizeContainersAlongAxis(true); + + // Wrap text + for (int32_t textElementIndex = 0; textElementIndex < context->textElementData.length; ++textElementIndex) { + Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&context->textElementData, textElementIndex); + textElementData->wrappedLines = CLAY__INIT(Clay__WrappedTextLineArraySlice) { .length = 0, .internalArray = &context->wrappedTextLines.internalArray[context->wrappedTextLines.length] }; + Clay_LayoutElement *containerElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)textElementData->elementIndex); + 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 ? (float)textConfig->lineHeight : textElementData->preferredDimensions.height; + int32_t lineLengthChars = 0; + int32_t lineStartOffset = 0; + if (!measureTextCacheItem->containsNewlines && textElementData->preferredDimensions.width <= containerElement->dimensions.width) { + Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { containerElement->dimensions, textElementData->text }); + textElementData->wrappedLines.length++; + continue; + } + int32_t wordIndex = measureTextCacheItem->measuredWordsStartIndex; + while (wordIndex != -1) { + if (context->wrappedTextLines.length > context->wrappedTextLines.capacity - 1) { + break; + } + Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&context->measuredWords, wordIndex); + // Only word on the line is too large, just render it anyway + if (lineLengthChars == 0 && lineWidth + measuredWord->width > containerElement->dimensions.width) { + Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { measuredWord->width, lineHeight }, { .length = measuredWord->length, .chars = &textElementData->text.chars[measuredWord->startOffset] } }); + textElementData->wrappedLines.length++; + wordIndex = measuredWord->next; + lineStartOffset = measuredWord->startOffset + measuredWord->length; + } + // measuredWord->length == 0 means a newline character + else if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) { + // Wrapped text lines list has overflowed, just render out the line + Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth, lineHeight }, { .length = lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } }); + textElementData->wrappedLines.length++; + if (lineLengthChars == 0 || measuredWord->length == 0) { + wordIndex = measuredWord->next; + } + lineWidth = 0; + lineLengthChars = 0; + lineStartOffset = measuredWord->startOffset; + } else { + lineWidth += measuredWord->width; + lineLengthChars += measuredWord->length; + wordIndex = measuredWord->next; + } + } + if (lineLengthChars > 0) { + Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth, lineHeight }, {.length = lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } }); + textElementData->wrappedLines.length++; + } + containerElement->dimensions.height = lineHeight * (float)textElementData->wrappedLines.length; + } + + // Scale vertical image heights according to aspect ratio + for (int32_t i = 0; i < context->imageElementPointers.length; ++i) { + Clay_LayoutElement* imageElement = Clay__LayoutElementPointerArray_Get(&context->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; + } + + // Propagate effect of text wrapping, image aspect scaling etc. on height of parents + Clay__LayoutElementTreeNodeArray dfsBuffer = context->layoutElementTreeNodeArray1; + dfsBuffer.length = 0; + for (int32_t i = 0; i < context->layoutElementTreeRoots.length; ++i) { + Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i); + context->treeNodeVisited.internalArray[dfsBuffer.length] = false; + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex) }); + } + while (dfsBuffer.length > 0) { + Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1); + Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement; + if (!context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) { + context->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 (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->childrenOrTextContent.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 (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; i++) { + context->treeNodeVisited.internalArray[dfsBuffer.length] = false; + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]) }); + } + continue; + } + dfsBuffer.length--; + + // DFS node has been visited, this is on the way back up to the root + Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig; + if (layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_PERCENT) { + continue; + } + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + // Resize any parent containers that have grown in height along their non layout axis + for (int32_t j = 0; j < currentElement->childrenOrTextContent.children.length; ++j) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[j]); + float childHeightWithPadding = CLAY__MAX(childElement->dimensions.height + layoutConfig->padding.top + layoutConfig->padding.bottom, currentElement->dimensions.height); + currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(childHeightWithPadding, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max); + } + } else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) { + // Resizing along the layout axis + float contentHeight = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom); + for (int32_t j = 0; j < currentElement->childrenOrTextContent.children.length; ++j) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[j]); + contentHeight += childElement->dimensions.height; + } + contentHeight += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap); + currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(contentHeight, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max); + } + } + + // Calculate sizing along the Y axis + Clay__SizeContainersAlongAxis(false); + + // Sort tree roots by z-index + int32_t sortMax = context->layoutElementTreeRoots.length - 1; + while (sortMax > 0) { // todo dumb bubble sort + for (int32_t i = 0; i < sortMax; ++i) { + Clay__LayoutElementTreeRoot current = *Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i); + Clay__LayoutElementTreeRoot next = *Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i + 1); + if (next.zIndex < current.zIndex) { + Clay__LayoutElementTreeRootArray_Set(&context->layoutElementTreeRoots, i, next); + Clay__LayoutElementTreeRootArray_Set(&context->layoutElementTreeRoots, i + 1, current); + } + } + sortMax--; + } + + // Calculate final positions and generate render commands + context->renderCommands.length = 0; + dfsBuffer.length = 0; + for (int32_t rootIndex = 0; rootIndex < context->layoutElementTreeRoots.length; ++rootIndex) { + dfsBuffer.length = 0; + Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex); + Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex); + Clay_Vector2 rootPosition = CLAY__DEFAULT_STRUCT; + Clay_LayoutElementHashMapItem *parentHashMapItem = Clay__GetHashMapItem(root->parentId); + // Position root floating containers + 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 + Clay_Vector2 targetAttachPosition = CLAY__DEFAULT_STRUCT; + switch (config->attachment.parent) { + case CLAY_ATTACH_POINT_LEFT_TOP: + case CLAY_ATTACH_POINT_LEFT_CENTER: + case CLAY_ATTACH_POINT_LEFT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x; break; + case CLAY_ATTACH_POINT_CENTER_TOP: + case CLAY_ATTACH_POINT_CENTER_CENTER: + case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + (parentBoundingBox.width / 2); break; + case CLAY_ATTACH_POINT_RIGHT_TOP: + case CLAY_ATTACH_POINT_RIGHT_CENTER: + case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + parentBoundingBox.width; break; + } + switch (config->attachment.element) { + case CLAY_ATTACH_POINT_LEFT_TOP: + case CLAY_ATTACH_POINT_LEFT_CENTER: + case CLAY_ATTACH_POINT_LEFT_BOTTOM: break; + case CLAY_ATTACH_POINT_CENTER_TOP: + case CLAY_ATTACH_POINT_CENTER_CENTER: + case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x -= (rootDimensions.width / 2); break; + case CLAY_ATTACH_POINT_RIGHT_TOP: + case CLAY_ATTACH_POINT_RIGHT_CENTER: + case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x -= rootDimensions.width; break; + } + switch (config->attachment.parent) { // I know I could merge the x and y switch statements, but this is easier to read + case CLAY_ATTACH_POINT_LEFT_TOP: + case CLAY_ATTACH_POINT_RIGHT_TOP: + case CLAY_ATTACH_POINT_CENTER_TOP: targetAttachPosition.y = parentBoundingBox.y; break; + case CLAY_ATTACH_POINT_LEFT_CENTER: + case CLAY_ATTACH_POINT_CENTER_CENTER: + case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y = parentBoundingBox.y + (parentBoundingBox.height / 2); break; + case CLAY_ATTACH_POINT_LEFT_BOTTOM: + case CLAY_ATTACH_POINT_CENTER_BOTTOM: + case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y = parentBoundingBox.y + parentBoundingBox.height; break; + } + switch (config->attachment.element) { + case CLAY_ATTACH_POINT_LEFT_TOP: + case CLAY_ATTACH_POINT_RIGHT_TOP: + case CLAY_ATTACH_POINT_CENTER_TOP: break; + case CLAY_ATTACH_POINT_LEFT_CENTER: + case CLAY_ATTACH_POINT_CENTER_CENTER: + case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y -= (rootDimensions.height / 2); break; + case CLAY_ATTACH_POINT_LEFT_BOTTOM: + case CLAY_ATTACH_POINT_CENTER_BOTTOM: + case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y -= rootDimensions.height; break; + } + targetAttachPosition.x += config->offset.x; + targetAttachPosition.y += config->offset.y; + rootPosition = targetAttachPosition; + } + if (root->clipElementId) { + Clay_LayoutElementHashMapItem *clipHashMapItem = Clay__GetHashMapItem(root->clipElementId); + if (clipHashMapItem) { + // Floating elements that are attached to scrolling contents won't be correctly positioned if external scroll handling is enabled, fix here + if (context->externalScrollHandlingEnabled) { + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(clipHashMapItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (mapping->layoutElement == clipHashMapItem->layoutElement) { + root->pointerOffset = mapping->scrollPosition; + if (scrollConfig->horizontal) { + rootPosition.x += mapping->scrollPosition.x; + } + if (scrollConfig->vertical) { + rootPosition.y += mapping->scrollPosition.y; + } + break; + } + } + } + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { + .boundingBox = clipHashMapItem->boundingBox, + .config = { .scrollElementConfig = Clay__StoreScrollElementConfig(CLAY__INIT(Clay_ScrollElementConfig)CLAY__DEFAULT_STRUCT) }, + .id = Clay__RehashWithNumber(rootElement->id, 10), // TODO need a better strategy for managing derived ids + .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START, + }); + } + } + Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = rootElement, .position = rootPosition, .nextChildOffset = { .x = (float)rootElement->layoutConfig->padding.left, .y = (float)rootElement->layoutConfig->padding.top } }); + + context->treeNodeVisited.internalArray[0] = false; + while (dfsBuffer.length > 0) { + Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1); + Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement; + Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig; + Clay_Vector2 scrollOffset = CLAY__DEFAULT_STRUCT; + + // This will only be run a single time for each element in downwards DFS order + if (!context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) { + context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; + + Clay_BoundingBox currentElementBoundingBox = { 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; + currentElementBoundingBox.y -= expand.height; + currentElementBoundingBox.height += expand.height * 2; + } + + Clay__ScrollContainerDataInternal *scrollContainerData = CLAY__NULL; + // Apply scroll offsets to container + 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 (int32_t i = 0; i < context->scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (mapping->layoutElement == currentElement) { + scrollContainerData = mapping; + mapping->boundingBox = currentElementBoundingBox; + if (scrollConfig->horizontal) { + scrollOffset.x = mapping->scrollPosition.x; + } + if (scrollConfig->vertical) { + scrollOffset.y = mapping->scrollPosition.y; + } + if (context->externalScrollHandlingEnabled) { + scrollOffset = CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT; + } + break; + } + } + } + + Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(currentElement->id); + if (hashMapItem) { + hashMapItem->boundingBox = currentElementBoundingBox; + } + + int32_t sortedConfigIndexes[20]; + for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) { + sortedConfigIndexes[elementConfigIndex] = elementConfigIndex; + } + sortMax = currentElement->elementConfigs.length - 1; + while (sortMax > 0) { // todo dumb bubble sort + for (int32_t i = 0; i < sortMax; ++i) { + int32_t current = sortedConfigIndexes[i]; + int32_t 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; + } + } + sortMax--; + } + + // Create the render commands for this element + for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) { + Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(¤tElement->elementConfigs, sortedConfigIndexes[elementConfigIndex]); + Clay_RenderCommand renderCommand = { + .boundingBox = currentElementBoundingBox, + .config = elementConfig->config, + .id = currentElement->id, + }; + + bool offscreen = Clay__ElementIsOffscreen(¤tElementBoundingBox); + // 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 shouldRender = !offscreen; + switch (elementConfig->type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: { + shouldRender = false; + 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; + shouldRender = true; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE; + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: { + if (!shouldRender) { + break; + } + shouldRender = false; + Clay_ElementConfigUnion configUnion = elementConfig->config; + Clay_TextElementConfig *textElementConfig = configUnion.textElementConfig; + float naturalLineHeight = currentElement->childrenOrTextContent.textElementData->preferredDimensions.height; + float finalLineHeight = textElementConfig->lineHeight > 0 ? (float)textElementConfig->lineHeight : naturalLineHeight; + float lineHeightOffset = (finalLineHeight - naturalLineHeight) / 2; + float yPosition = lineHeightOffset; + for (int32_t lineIndex = 0; lineIndex < currentElement->childrenOrTextContent.textElementData->wrappedLines.length; ++lineIndex) { + Clay__WrappedTextLine wrappedLine = currentElement->childrenOrTextContent.textElementData->wrappedLines.internalArray[lineIndex]; // todo range check + if (wrappedLine.line.length == 0) { + yPosition += finalLineHeight; + continue; + } + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { + .boundingBox = { currentElementBoundingBox.x, currentElementBoundingBox.y + yPosition, wrappedLine.dimensions.width, wrappedLine.dimensions.height }, // TODO width + .config = configUnion, + .text = wrappedLine.line, + .id = Clay__HashNumber(lineIndex, currentElement->id).id, + .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT, + }); + yPosition += finalLineHeight; + + if (!context->disableCulling && (currentElementBoundingBox.y + yPosition > context->layoutDimensions.height)) { + break; + } + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: { + renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM; + break; + } + default: break; + } + if (shouldRender) { + Clay__AddRenderCommand(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 = {0,0}; + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); + contentSize.width += childElement->dimensions.width; + contentSize.height = CLAY__MAX(contentSize.height, childElement->dimensions.height); + } + contentSize.width += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap); + float extraSpace = currentElement->dimensions.width - (float)(layoutConfig->padding.left + layoutConfig->padding.right) - contentSize.width; + switch (layoutConfig->childAlignment.x) { + case CLAY_ALIGN_X_LEFT: extraSpace = 0; break; + case CLAY_ALIGN_X_CENTER: extraSpace /= 2; break; + default: break; + } + currentElementTreeNode->nextChildOffset.x += extraSpace; + } else { + for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); + contentSize.width = CLAY__MAX(contentSize.width, childElement->dimensions.width); + contentSize.height += childElement->dimensions.height; + } + contentSize.height += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap); + float extraSpace = currentElement->dimensions.height - (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) - contentSize.height; + switch (layoutConfig->childAlignment.y) { + case CLAY_ALIGN_Y_TOP: extraSpace = 0; break; + case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break; + default: break; + } + currentElementTreeNode->nextChildOffset.y += extraSpace; + } + + if (scrollContainerData) { + scrollContainerData->contentSize = CLAY__INIT(Clay_Dimensions) { contentSize.width + (float)(layoutConfig->padding.left + layoutConfig->padding.right), contentSize.height + (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) }; + } + } + } + else { + // DFS is returning upwards backwards + 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; + for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (mapping->layoutElement == currentElement) { + if (scrollConfig->horizontal) { scrollOffset.x = mapping->scrollPosition.x; } + if (scrollConfig->vertical) { scrollOffset.y = mapping->scrollPosition.y; } + if (context->externalScrollHandlingEnabled) { + scrollOffset = CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT; + } + break; + } + } + } + + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER)) { + Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id); + Clay_BoundingBox currentElementBoundingBox = currentElementData->boundingBox; + + // 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 + if (!Clay__ElementIsOffscreen(¤tElementBoundingBox)) { + Clay_BorderElementConfig *borderConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER).borderElementConfig; + Clay_RenderCommand renderCommand = { + .boundingBox = currentElementBoundingBox, + .config = { .borderElementConfig = borderConfig }, + .id = Clay__RehashWithNumber(currentElement->id, 4), + .commandType = CLAY_RENDER_COMMAND_TYPE_BORDER, + }; + Clay__AddRenderCommand(renderCommand); + if (borderConfig->betweenChildren.width > 0 && borderConfig->betweenChildren.color.a > 0) { + Clay_RectangleElementConfig *rectangleConfig = Clay__StoreRectangleElementConfig(CLAY__INIT(Clay_RectangleElementConfig) {.color = borderConfig->betweenChildren.color}); + float halfGap = layoutConfig->childGap / 2; + Clay_Vector2 borderOffset = { (float)layoutConfig->padding.left - halfGap, (float)layoutConfig->padding.top - halfGap }; + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); + if (i > 0) { + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { + .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, + }); + } + borderOffset.x += (childElement->dimensions.width + (float)layoutConfig->childGap); + } + } else { + for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); + if (i > 0) { + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { + .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, + }); + } + borderOffset.y += (childElement->dimensions.height + (float)layoutConfig->childGap); + } + } + } + } + } + // This exists because the scissor needs to end _after_ borders between elements + if (closeScrollElement) { + Clay__AddRenderCommand(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 (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { + dfsBuffer.length += currentElement->childrenOrTextContent.children.length; + for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); + // Alignment along non layout axis + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + currentElementTreeNode->nextChildOffset.y = currentElement->layoutConfig->padding.top; + float whiteSpaceAroundChild = currentElement->dimensions.height - (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) - childElement->dimensions.height; + switch (layoutConfig->childAlignment.y) { + case CLAY_ALIGN_Y_TOP: break; + case CLAY_ALIGN_Y_CENTER: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild / 2; break; + case CLAY_ALIGN_Y_BOTTOM: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild; break; + } + } else { + currentElementTreeNode->nextChildOffset.x = currentElement->layoutConfig->padding.left; + float whiteSpaceAroundChild = currentElement->dimensions.width - (float)(layoutConfig->padding.left + layoutConfig->padding.right) - childElement->dimensions.width; + switch (layoutConfig->childAlignment.x) { + case CLAY_ALIGN_X_LEFT: break; + case CLAY_ALIGN_X_CENTER: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild / 2; break; + case CLAY_ALIGN_X_RIGHT: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild; break; + } + } + + Clay_Vector2 childPosition = { + currentElementTreeNode->position.x + currentElementTreeNode->nextChildOffset.x + scrollOffset.x, + currentElementTreeNode->position.y + currentElementTreeNode->nextChildOffset.y + scrollOffset.y, + }; + + // DFS buffer elements need to be added in reverse because stack traversal happens backwards + uint32_t newNodeIndex = dfsBuffer.length - 1 - i; + dfsBuffer.internalArray[newNodeIndex] = CLAY__INIT(Clay__LayoutElementTreeNode) { + .layoutElement = childElement, + .position = { childPosition.x, childPosition.y }, + .nextChildOffset = { .x = (float)childElement->layoutConfig->padding.left, .y = (float)childElement->layoutConfig->padding.top }, + }; + context->treeNodeVisited.internalArray[newNodeIndex] = false; + + // Update parent offsets + if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) { + currentElementTreeNode->nextChildOffset.x += childElement->dimensions.width + (float)layoutConfig->childGap; + } else { + currentElementTreeNode->nextChildOffset.y += childElement->dimensions.height + (float)layoutConfig->childGap; + } + } + } + } + + if (root->clipElementId) { + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { .id = Clay__RehashWithNumber(rootElement->id, 11), .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END }); + } + } +} + +void Clay__AttachId(Clay_ElementId elementId) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + openLayoutElement->id = elementId.id; + Clay__AddHashMapItem(elementId, openLayoutElement); + Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId); +} + +void Clay__AttachLayoutConfig(Clay_LayoutConfig *config) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + Clay__GetOpenLayoutElement()->layoutConfig = config; +} +void Clay__AttachElementConfig(Clay_ElementConfigUnion config, Clay__ElementConfigType type) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); + openLayoutElement->elementConfigs.length++; + Clay__ElementConfigArray_Add(&context->elementConfigBuffer, CLAY__INIT(Clay_ElementConfig) { .type = type, .config = config }); +} +Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay_GetCurrentContext()->layoutConfigs, config); } +Clay_RectangleElementConfig * Clay__StoreRectangleElementConfig(Clay_RectangleElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT : Clay__RectangleElementConfigArray_Add(&Clay_GetCurrentContext()->rectangleElementConfigs, config); } +Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__TEXT_ELEMENT_CONFIG_DEFAULT : Clay__TextElementConfigArray_Add(&Clay_GetCurrentContext()->textElementConfigs, config); } +Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay_GetCurrentContext()->imageElementConfigs, config); } +Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay_GetCurrentContext()->floatingElementConfigs, config); } +Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay_GetCurrentContext()->customElementConfigs, config); } +Clay_ScrollElementConfig * Clay__StoreScrollElementConfig(Clay_ScrollElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__SCROLL_ELEMENT_CONFIG_DEFAULT : Clay__ScrollElementConfigArray_Add(&Clay_GetCurrentContext()->scrollElementConfigs, config); } +Clay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY__BORDER_ELEMENT_CONFIG_DEFAULT : Clay__BorderElementConfigArray_Add(&Clay_GetCurrentContext()->borderElementConfigs, config); } + +#pragma region DebugTools +Clay_Color CLAY__DEBUGVIEW_COLOR_1 = {58, 56, 52, 255}; +Clay_Color CLAY__DEBUGVIEW_COLOR_2 = {62, 60, 58, 255}; +Clay_Color CLAY__DEBUGVIEW_COLOR_3 = {141, 133, 135, 255}; +Clay_Color CLAY__DEBUGVIEW_COLOR_4 = {238, 226, 231, 255}; +Clay_Color CLAY__DEBUGVIEW_COLOR_SELECTED_ROW = {102, 80, 78, 255}; +const int32_t CLAY__DEBUGVIEW_ROW_HEIGHT = 30; +const int32_t CLAY__DEBUGVIEW_OUTER_PADDING = 10; +const int32_t CLAY__DEBUGVIEW_INDENT_WIDTH = 16; +Clay_TextElementConfig Clay__DebugView_TextNameConfig = {.textColor = {238, 226, 231, 255}, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }; +Clay_LayoutConfig Clay__DebugView_ScrollViewItemLayoutConfig = CLAY__DEFAULT_STRUCT; + +CLAY__TYPEDEF(Clay__DebugElementConfigTypeLabelConfig, struct { + Clay_String label; + Clay_Color color; +}); + +Clay__DebugElementConfigTypeLabelConfig Clay__DebugGetElementConfigTypeLabel(Clay__ElementConfigType type) { + switch (type) { + case CLAY__ELEMENT_CONFIG_TYPE_RECTANGLE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Rectangle"), {243,134,48,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_TEXT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Text"), {105,210,231,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Image"), {121,189,154,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Floating"), {250,105,0,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Scroll"), {242,196,90,255} }; + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Border"), {108,91,123, 255} }; + case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Custom"), {11,72,107,255} }; + default: break; + } + return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Error"), {0,0,0,255} }; +} + +CLAY__TYPEDEF(Clay__RenderDebugLayoutData, struct { + int32_t rowCount; + int32_t selectedElementRowIndex; +}); + +// Returns row count +Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialRootsLength, int32_t highlightedRowIndex) { + Clay_Context* context = Clay_GetCurrentContext(); + Clay__int32_tArray dfsBuffer = context->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 = CLAY__DEFAULT_STRUCT; + + uint32_t highlightedElementId = 0; + + for (int32_t rootIndex = 0; rootIndex < initialRootsLength; ++rootIndex) { + dfsBuffer.length = 0; + Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex); + Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex); + context->treeNodeVisited.internalArray[0] = false; + if (rootIndex > 0) { + CLAY(CLAY_IDI("Clay__DebugView_EmptyRowOuter", rootIndex), CLAY_LAYOUT({ .sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2, 0} })) { + CLAY(CLAY_IDI("Clay__DebugView_EmptyRow", rootIndex), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }}), CLAY_BORDER({ .top = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 } })) {} + } + layoutData.rowCount++; + } + while (dfsBuffer.length > 0) { + int32_t currentElementIndex = Clay__int32_tArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1); + Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)currentElementIndex); + if (context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) { + if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && currentElement->childrenOrTextContent.children.length > 0) { + Clay__CloseElement(); + Clay__CloseElement(); + Clay__CloseElement(); + } + dfsBuffer.length--; + continue; + } + + if (highlightedRowIndex == layoutData.rowCount) { + if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + context->debugSelectedElementId = currentElement->id; + } + highlightedElementId = currentElement->id; + } + + context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; + Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id); + bool offscreen = Clay__ElementIsOffscreen(¤tElementData->boundingBox); + if (context->debugSelectedElementId == currentElement->id) { + layoutData.selectedElementRowIndex = layoutData.rowCount; + } + 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->childrenOrTextContent.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 { // 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) })) {} + } + } + // Collisions and offscreen info + if (currentElementData) { + if (currentElementData->debugData->collision) { + CLAY(CLAY_LAYOUT({ .padding = { 8, 8, 2, 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 })); + } + } + if (offscreen) { + CLAY(CLAY_LAYOUT({ .padding = { 8, 8, 2, 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_String idString = context->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 (int32_t 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(CLAY_LAYOUT({ .padding = { 8, 8, 2, 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 (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { + layoutData.rowCount++; + Clay__TextElementData *textElementData = currentElement->childrenOrTextContent.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__DEFAULT_STRUCT)) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16), CLAY__DEFAULT_STRUCT} })) {} + 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_STRING("\""), rawTextConfig); + } + } else if (currentElement->childrenOrTextContent.children.length > 0) { + 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), CLAY__DEFAULT_STRUCT}, .childAlignment = { .x = CLAY_ALIGN_X_RIGHT } })) {} + Clay__OpenElement(); + CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM }); + Clay__ElementPostConfiguration(); + } + + layoutData.rowCount++; + if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (currentElementData && currentElementData->debugData->collapsed))) { + for (int32_t i = currentElement->childrenOrTextContent.children.length - 1; i >= 0; --i) { + Clay__int32_tArray_Add(&dfsBuffer, currentElement->childrenOrTextContent.children.elements[i]); + context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked + } + } + } + } + + if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + Clay_ElementId collapseButtonId = Clay__HashString(CLAY_STRING("Clay__DebugView_CollapseElement"), 0, 0); + for (int32_t i = (int)context->pointerOverIds.length - 1; i >= 0; i--) { + Clay_ElementId *elementId = Clay__ElementIdArray_Get(&context->pointerOverIds, i); + if (elementId->baseId == collapseButtonId.baseId) { + Clay_LayoutElementHashMapItem *highlightedItem = Clay__GetHashMapItem(elementId->offset); + highlightedItem->debugData->collapsed = !highlightedItem->debugData->collapsed; + break; + } + } + } + + if (highlightedElementId) { + CLAY(CLAY_ID("Clay__DebugView_ElementHighlight"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }), CLAY_FLOATING({ .zIndex = 65535, .parentId = highlightedElementId })) { + CLAY(CLAY_ID("Clay__DebugView_ElementHighlightRectangle"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }), CLAY_RECTANGLE({.color = Clay__debugViewHighlightColor })) {} + } + } + return layoutData; +} + +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"); + } + CLAY_TEXT(sizingLabel, infoTextConfig); + if (sizing.type == CLAY__SIZING_TYPE_GROW || sizing.type == CLAY__SIZING_TYPE_FIT) { + CLAY_TEXT(CLAY_STRING("("), infoTextConfig); + if (sizing.size.minMax.min != 0) { + CLAY_TEXT(CLAY_STRING("min: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(sizing.size.minMax.min), infoTextConfig); + if (sizing.size.minMax.max != CLAY__MAXFLOAT) { + CLAY_TEXT(CLAY_STRING(", "), infoTextConfig); + } + } + if (sizing.size.minMax.max != CLAY__MAXFLOAT) { + CLAY_TEXT(CLAY_STRING("max: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(sizing.size.minMax.max), infoTextConfig); + } + CLAY_TEXT(CLAY_STRING(")"), infoTextConfig); + } +} + +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(0)}, .padding = CLAY_PADDING_ALL(CLAY__DEBUGVIEW_OUTER_PADDING), .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } })) { + CLAY(CLAY_LAYOUT({ .padding = { 8, 8, 2, 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 = { .width = CLAY_SIZING_GROW(0) } })) {} + CLAY_TEXT(elementId, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE })); + } +} + +void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) { + 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__DEFAULT_STRUCT } })) {} + 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(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(int32_t index, Clay_Border border, Clay_TextElementConfig *textConfig) { + (void) index; + 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_STRING(" }"), textConfig); + } +} + +void HandleDebugViewCloseButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, intptr_t userData) { + Clay_Context* context = Clay_GetCurrentContext(); + (void) elementId; (void) pointerInfo; (void) userData; + if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + context->debugModeEnabled = false; + } +} + +void Clay__RenderDebugView() { + Clay_Context* context = Clay_GetCurrentContext(); + Clay_ElementId closeButtonId = Clay__HashString(CLAY_STRING("Clay__DebugViewTopHeaderCloseButtonOuter"), 0, 0); + if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + for (int32_t i = 0; i < context->pointerOverIds.length; ++i) { + Clay_ElementId *elementId = Clay__ElementIdArray_Get(&context->pointerOverIds, i); + if (elementId->id == closeButtonId.id) { + context->debugModeEnabled = false; + return; + } + } + } + + uint32_t initialRootsLength = context->layoutElementTreeRoots.length; + uint32_t initialElementsLength = context->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 }); + Clay_ElementId scrollId = Clay__HashString(CLAY_STRING("Clay__DebugViewOuterScrollPane"), 0, 0); + float scrollYOffset = 0; + bool pointerInDebugView = context->pointerInfo.position.y < context->layoutDimensions.height - 300; + for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) { + Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (scrollContainerData->elementId == scrollId.id) { + if (!context->externalScrollHandlingEnabled) { + scrollYOffset = scrollContainerData->scrollPosition.y; + } else { + pointerInDebugView = context->pointerInfo.position.y + scrollContainerData->scrollPosition.y < context->layoutDimensions.height - 300; + } + break; + } + } + int32_t highlightedRow = pointerInDebugView + ? (int32_t)((context->pointerInfo.position.y - scrollYOffset) / (float)CLAY__DEBUGVIEW_ROW_HEIGHT) - 1 + : -1; + if (context->pointerInfo.position.x < context->layoutDimensions.width - (float)Clay__debugViewWidth) { + highlightedRow = -1; + } + Clay__RenderDebugLayoutData layoutData = CLAY__DEFAULT_STRUCT; + CLAY(CLAY_ID("Clay__DebugView"), + CLAY_FLOATING({ .zIndex = 65000, .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(context->layoutDimensions.height) }, .layoutDirection = CLAY_TOP_TO_BOTTOM }), + CLAY_BORDER({ .bottom = { .width = 1, .color = CLAY__DEBUGVIEW_COLOR_3 }}) + ) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_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(0) } })) {} + // 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(0), CLAY_SIZING_FIXED(1)} }), CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_3 })) {} + CLAY(Clay__AttachId(scrollId), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }), CLAY_SCROLL({ .horizontal = true, .vertical = true })) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .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(0), CLAY_SIZING_GROW(0)} }), CLAY_FLOATING({ .zIndex = 65001, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH })) { + CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = { CLAY__DEBUGVIEW_OUTER_PADDING, 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), CLAY__DEFAULT_STRUCT}, .layoutDirection = CLAY_TOP_TO_BOTTOM })) {} + for (int32_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(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }), CLAY_RECTANGLE({ .color = rowColor })) {} + } + } + } + CLAY(CLAY_LAYOUT({ .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }), CLAY_RECTANGLE({ .color = CLAY__DEBUGVIEW_COLOR_3 })) {} + if (context->debugSelectedElementId != 0) { + Clay_LayoutElementHashMapItem *selectedItem = Clay__GetHashMapItem(context->debugSelectedElementId); + CLAY( + CLAY_SCROLL({ .vertical = true }), + CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), 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(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_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(0) } })) {} + 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_Padding attributeConfigPadding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 8, 8}; + // Clay_LayoutConfig debug info + CLAY(CLAY_LAYOUT({ .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .boundingBox + CLAY_TEXT(CLAY_STRING("Bounding Box"), infoTitleConfig); + CLAY(CLAY_LAYOUT(CLAY__DEFAULT_STRUCT)) { + 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__DEFAULT_STRUCT)) { + CLAY_TEXT(CLAY_STRING("width: "), infoTextConfig); + Clay__RenderDebugLayoutSizing(layoutConfig->sizing.width, infoTextConfig); + } + CLAY(CLAY_LAYOUT(CLAY__DEFAULT_STRUCT)) { + 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("{ left: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->padding.left), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", right: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->padding.right), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", top: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->padding.top), infoTextConfig); + CLAY_TEXT(CLAY_STRING(", bottom: "), infoTextConfig); + CLAY_TEXT(Clay__IntToString(layoutConfig->padding.bottom), 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__DEFAULT_STRUCT)) { + 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"); + } + 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 (int32_t 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 = attributeConfigPadding, .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 = attributeConfigPadding, .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"); + } + CLAY_TEXT(wrapMode, infoTextConfig); + // .textColor + CLAY_TEXT(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 = attributeConfigPadding, .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); + } + // Image Preview + CLAY_TEXT(CLAY_STRING("Preview"), infoTitleConfig); + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0, imageConfig->sourceDimensions.width) }}), Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = imageConfig }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) {} + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER: { + Clay_ScrollElementConfig *scrollConfig = elementConfig->config.scrollElementConfig; + CLAY(CLAY_LAYOUT({ .padding = attributeConfigPadding, .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 = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { + // .offset + CLAY_TEXT(CLAY_STRING("Offset"), infoTitleConfig); + CLAY(CLAY_LAYOUT(CLAY__DEFAULT_STRUCT)) { + 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); + } + // .expand + CLAY_TEXT(CLAY_STRING("Expand"), infoTitleConfig); + CLAY(CLAY_LAYOUT(CLAY__DEFAULT_STRUCT)) { + 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); + } + // .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); + } + break; + } + case CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER: { + Clay_BorderElementConfig *borderConfig = elementConfig->config.borderElementConfig; + CLAY(CLAY_ID("Clay__DebugViewElementInfoBorderBody"), CLAY_LAYOUT({ .padding = attributeConfigPadding, .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: + default: break; + } + } + } + } else { + CLAY(CLAY_ID("Clay__DebugViewWarningsScrollPane"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), 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, 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(0), .height = CLAY_SIZING_FIXED(1)} }), CLAY_RECTANGLE({ .color = {200, 200, 200, 255} })) {} + int32_t previousWarningsLength = context->warnings.length; + for (int32_t i = 0; i < previousWarningsLength; i++) { + Clay__Warning warning = context->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, 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); + } + } + } + } + } + } +} +#pragma endregion + +uint32_t Clay__debugViewWidth = 400; +Clay_Color Clay__debugViewHighlightColor = { 168, 66, 28, 100 }; + +Clay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena) { + size_t totalSizeBytes = capacity * sizeof(Clay_String); + Clay__WarningArray array = {.capacity = capacity, .length = 0}; + uintptr_t nextAllocAddress = arena->nextAllocation + (uintptr_t)arena->memory; + uintptr_t arenaOffsetAligned = nextAllocAddress + (CLAY__ALIGNMENT(Clay_String) - (nextAllocAddress % CLAY__ALIGNMENT(Clay_String))); + arenaOffsetAligned -= (uintptr_t)arena->memory; + if (arenaOffsetAligned + totalSizeBytes <= arena->capacity) { + array.internalArray = (Clay__Warning*)((uintptr_t)arena->memory + (uintptr_t)arenaOffsetAligned); + arena->nextAllocation = arenaOffsetAligned + totalSizeBytes; + } + else { + Clay__currentContext->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED, + .errorText = CLAY_STRING("Clay attempted to allocate memory in its arena, but ran out of capacity. Try increasing the capacity of the arena passed to Clay_Initialize()"), + .userData = Clay__currentContext->errorHandler.userData }); + } + return array; +} + +Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item) +{ + if (array->length < array->capacity) { + array->internalArray[array->length++] = item; + return &array->internalArray[array->length - 1]; + } + return &CLAY__WARNING_DEFAULT; +} + +void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, uint32_t alignment, Clay_Arena *arena) +{ + size_t totalSizeBytes = capacity * itemSize; + uintptr_t nextAllocAddress = arena->nextAllocation + (uintptr_t)arena->memory; + uintptr_t arenaOffsetAligned = nextAllocAddress + (alignment - (nextAllocAddress % alignment)); + arenaOffsetAligned -= (uintptr_t)arena->memory; + if (arenaOffsetAligned + totalSizeBytes <= arena->capacity) { + arena->nextAllocation = arenaOffsetAligned + totalSizeBytes; + return (void*)((uintptr_t)arena->memory + (uintptr_t)arenaOffsetAligned); + } + else { + Clay__currentContext->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED, + .errorText = CLAY_STRING("Clay attempted to allocate memory in its arena, but ran out of capacity. Try increasing the capacity of the arena passed to Clay_Initialize()"), + .userData = Clay__currentContext->errorHandler.userData }); + } + return CLAY__NULL; +} + +bool Clay__Array_RangeCheck(int32_t index, int32_t length) +{ + if (index < length && index >= 0) { + return true; + } + Clay_Context* context = Clay_GetCurrentContext(); + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_INTERNAL_ERROR, + .errorText = CLAY_STRING("Clay attempted to make an out of bounds array access. This is an internal error and is likely a bug."), + .userData = context->errorHandler.userData }); + return false; +} + +bool Clay__Array_AddCapacityCheck(int32_t length, int32_t capacity) +{ + if (length < capacity) { + return true; + } + Clay_Context* context = Clay_GetCurrentContext(); + context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) { + .errorType = CLAY_ERROR_TYPE_INTERNAL_ERROR, + .errorText = CLAY_STRING("Clay attempted to make an out of bounds array access. This is an internal error and is likely a bug."), + .userData = context->errorHandler.userData }); + return false; +} + +// PUBLIC API FROM HERE --------------------------------------- + +CLAY_WASM_EXPORT("Clay_MinMemorySize") +uint32_t Clay_MinMemorySize(void) { + Clay_Context fakeContext = { + .maxElementCount = Clay__defaultMaxElementCount, + .maxMeasureTextCacheWordCount = Clay__defaultMaxMeasureTextWordCacheCount, + .internalArena = { + .capacity = SIZE_MAX, + .memory = NULL, + } + }; + Clay_Context* currentContext = Clay_GetCurrentContext(); + if (currentContext) { + fakeContext.maxElementCount = currentContext->maxElementCount; + fakeContext.maxMeasureTextCacheWordCount = currentContext->maxElementCount; + } + // Reserve space in the arena for the context, important for calculating min memory size correctly + Clay__Context_Allocate_Arena(&fakeContext.internalArena); + Clay__InitializePersistentMemory(&fakeContext); + Clay__InitializeEphemeralMemory(&fakeContext); + return fakeContext.internalArena.nextAllocation; +} + +CLAY_WASM_EXPORT("Clay_CreateArenaWithCapacityAndMemory") +Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset) { + Clay_Arena arena = { + .capacity = capacity, + .memory = (char *)offset + }; + return arena; +} + +#ifndef CLAY_WASM +void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_String *text, Clay_TextElementConfig *config)) { + Clay__MeasureText = measureTextFunction; +} +void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId)) { + Clay__QueryScrollOffset = queryScrollOffsetFunction; +} +#endif + +CLAY_WASM_EXPORT("Clay_SetLayoutDimensions") +void Clay_SetLayoutDimensions(Clay_Dimensions dimensions) { + Clay_GetCurrentContext()->layoutDimensions = dimensions; +} + +CLAY_WASM_EXPORT("Clay_SetPointerState") +void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + context->pointerInfo.position = position; + context->pointerOverIds.length = 0; + Clay__int32_tArray dfsBuffer = context->layoutElementChildrenBuffer; + for (int32_t rootIndex = context->layoutElementTreeRoots.length - 1; rootIndex >= 0; --rootIndex) { + dfsBuffer.length = 0; + Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex); + Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex); + context->treeNodeVisited.internalArray[0] = false; + bool found = false; + while (dfsBuffer.length > 0) { + if (context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) { + dfsBuffer.length--; + continue; + } + context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true; + Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1)); + 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 + Clay_BoundingBox elementBox = mapItem->boundingBox; + elementBox.x -= root->pointerOffset.x; + elementBox.y -= root->pointerOffset.y; + if (mapItem) { + if ((Clay__PointIsInsideRect(position, elementBox))) { + if (mapItem->onHoverFunction) { + mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData); + } + Clay__ElementIdArray_Add(&context->pointerOverIds, mapItem->elementId); + found = true; + } + if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) { + dfsBuffer.length--; + continue; + } + for (int32_t i = currentElement->childrenOrTextContent.children.length - 1; i >= 0; --i) { + Clay__int32_tArray_Add(&dfsBuffer, currentElement->childrenOrTextContent.children.elements[i]); + context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked + } + } else { + dfsBuffer.length--; + } + } + + Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, root->layoutElementIndex); + if (found && Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER) && + Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER).floatingElementConfig->pointerCaptureMode == CLAY_POINTER_CAPTURE_MODE_CAPTURE) { + break; + } + } + + if (isPointerDown) { + if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + context->pointerInfo.state = CLAY_POINTER_DATA_PRESSED; + } else if (context->pointerInfo.state != CLAY_POINTER_DATA_PRESSED) { + context->pointerInfo.state = CLAY_POINTER_DATA_PRESSED_THIS_FRAME; + } + } else { + if (context->pointerInfo.state == CLAY_POINTER_DATA_RELEASED_THIS_FRAME) { + context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED; + } else if (context->pointerInfo.state != CLAY_POINTER_DATA_RELEASED) { + context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED_THIS_FRAME; + } + } +} + +CLAY_WASM_EXPORT("Clay_Initialize") +Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler) { + Clay_Context *context = Clay__Context_Allocate_Arena(&arena); + if (context == NULL) return NULL; + // DEFAULTS + Clay_Context *oldContext = Clay_GetCurrentContext(); + *context = CLAY__INIT(Clay_Context) { + .maxElementCount = oldContext ? oldContext->maxElementCount : Clay__defaultMaxElementCount, + .maxMeasureTextCacheWordCount = oldContext ? oldContext->maxMeasureTextCacheWordCount : Clay__defaultMaxElementCount * 2, + .errorHandler = errorHandler.errorHandlerFunction ? errorHandler : CLAY__INIT(Clay_ErrorHandler) { Clay__ErrorHandlerFunctionDefault }, + .layoutDimensions = layoutDimensions, + .internalArena = arena, + }; + Clay_SetCurrentContext(context); + Clay__InitializePersistentMemory(context); + Clay__InitializeEphemeralMemory(context); + for (int32_t i = 0; i < context->layoutElementsHashMap.capacity; ++i) { + context->layoutElementsHashMap.internalArray[i] = -1; + } + for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) { + context->measureTextHashMap.internalArray[i] = 0; + } + context->measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element" + context->layoutDimensions = layoutDimensions; + return context; +} + +CLAY_WASM_EXPORT("Clay_GetCurrentContext") +Clay_Context* Clay_GetCurrentContext(void) { + return Clay__currentContext; +} + +CLAY_WASM_EXPORT("Clay_SetCurrentContext") +void Clay_SetCurrentContext(Clay_Context* context) { + Clay__currentContext = context; +} + +CLAY_WASM_EXPORT("Clay_UpdateScrollContainers") +void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime) { + Clay_Context* context = Clay_GetCurrentContext(); + bool isPointerActive = enableDragScrolling && (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED || context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME); + // Don't apply scroll events to ancestors of the inner element + int32_t highestPriorityElementIndex = -1; + Clay__ScrollContainerDataInternal *highestPriorityScrollData = CLAY__NULL; + for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *scrollData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (!scrollData->openThisFrame) { + Clay__ScrollContainerDataInternalArray_RemoveSwapback(&context->scrollContainerDatas, i); + continue; + } + scrollData->openThisFrame = false; + Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId); + // Element isn't rendered this frame but scroll offset has been retained + if (!hashMapItem) { + Clay__ScrollContainerDataInternalArray_RemoveSwapback(&context->scrollContainerDatas, i); + continue; + } + + // Touch / click is released + if (!isPointerActive && scrollData->pointerScrollActive) { + float xDiff = scrollData->scrollPosition.x - scrollData->scrollOrigin.x; + if (xDiff < -10 || xDiff > 10) { + scrollData->scrollMomentum.x = (scrollData->scrollPosition.x - scrollData->scrollOrigin.x) / (scrollData->momentumTime * 25); + } + float yDiff = scrollData->scrollPosition.y - scrollData->scrollOrigin.y; + if (yDiff < -10 || yDiff > 10) { + scrollData->scrollMomentum.y = (scrollData->scrollPosition.y - scrollData->scrollOrigin.y) / (scrollData->momentumTime * 25); + } + scrollData->pointerScrollActive = false; + + scrollData->pointerOrigin = CLAY__INIT(Clay_Vector2){0,0}; + scrollData->scrollOrigin = CLAY__INIT(Clay_Vector2){0,0}; + scrollData->momentumTime = 0; + } + + // Apply existing momentum + scrollData->scrollPosition.x += scrollData->scrollMomentum.x; + scrollData->scrollMomentum.x *= 0.95f; + bool scrollOccurred = scrollDelta.x != 0 || scrollDelta.y != 0; + if ((scrollData->scrollMomentum.x > -0.1f && scrollData->scrollMomentum.x < 0.1f) || scrollOccurred) { + scrollData->scrollMomentum.x = 0; + } + scrollData->scrollPosition.x = CLAY__MIN(CLAY__MAX(scrollData->scrollPosition.x, -(CLAY__MAX(scrollData->contentSize.width - scrollData->layoutElement->dimensions.width, 0))), 0); + + scrollData->scrollPosition.y += scrollData->scrollMomentum.y; + scrollData->scrollMomentum.y *= 0.95f; + if ((scrollData->scrollMomentum.y > -0.1f && scrollData->scrollMomentum.y < 0.1f) || scrollOccurred) { + scrollData->scrollMomentum.y = 0; + } + scrollData->scrollPosition.y = CLAY__MIN(CLAY__MAX(scrollData->scrollPosition.y, -(CLAY__MAX(scrollData->contentSize.height - scrollData->layoutElement->dimensions.height, 0))), 0); + + for (int32_t j = 0; j < context->pointerOverIds.length; ++j) { // TODO n & m are small here but this being n*m gives me the creeps + if (scrollData->layoutElement->id == Clay__ElementIdArray_Get(&context->pointerOverIds, j)->id) { + highestPriorityElementIndex = j; + highestPriorityScrollData = scrollData; + } + } + } + + if (highestPriorityElementIndex > -1 && highestPriorityScrollData) { + Clay_LayoutElement *scrollElement = highestPriorityScrollData->layoutElement; + 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; + } + if (canScrollHorizontally) { + highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollPosition.x + scrollDelta.x * 10; + } + // Handle click / touch scroll + if (isPointerActive) { + highestPriorityScrollData->scrollMomentum = CLAY__INIT(Clay_Vector2)CLAY__DEFAULT_STRUCT; + if (!highestPriorityScrollData->pointerScrollActive) { + highestPriorityScrollData->pointerOrigin = context->pointerInfo.position; + highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition; + highestPriorityScrollData->pointerScrollActive = true; + } else { + float scrollDeltaX = 0, scrollDeltaY = 0; + if (canScrollHorizontally) { + float oldXScrollPosition = highestPriorityScrollData->scrollPosition.x; + highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollOrigin.x + (context->pointerInfo.position.x - highestPriorityScrollData->pointerOrigin.x); + highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - highestPriorityScrollData->boundingBox.width)); + scrollDeltaX = highestPriorityScrollData->scrollPosition.x - oldXScrollPosition; + } + if (canScrollVertically) { + float oldYScrollPosition = highestPriorityScrollData->scrollPosition.y; + highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollOrigin.y + (context->pointerInfo.position.y - highestPriorityScrollData->pointerOrigin.y); + highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - highestPriorityScrollData->boundingBox.height)); + scrollDeltaY = highestPriorityScrollData->scrollPosition.y - oldYScrollPosition; + } + if (scrollDeltaX > -0.1f && scrollDeltaX < 0.1f && scrollDeltaY > -0.1f && scrollDeltaY < 0.1f && highestPriorityScrollData->momentumTime > 0.15f) { + highestPriorityScrollData->momentumTime = 0; + highestPriorityScrollData->pointerOrigin = context->pointerInfo.position; + highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition; + } else { + highestPriorityScrollData->momentumTime += deltaTime; + } + } + } + // Clamp any changes to scroll position to the maximum size of the contents + if (canScrollVertically) { + highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - scrollElement->dimensions.height)); + } + if (canScrollHorizontally) { + highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - scrollElement->dimensions.width)); + } + } +} + +CLAY_WASM_EXPORT("Clay_BeginLayout") +void Clay_BeginLayout(void) { + Clay_Context* context = Clay_GetCurrentContext(); + Clay__InitializeEphemeralMemory(context); + context->generation++; + context->dynamicElementIndex = 0; + // Set up the root container that covers the entire window + Clay_Dimensions rootDimensions = {context->layoutDimensions.width, context->layoutDimensions.height}; + if (context->debugModeEnabled) { + rootDimensions.width -= (float)Clay__debugViewWidth; + } + context->booleanWarnings.maxElementsExceeded = false; + context->booleanWarnings.maxTextMeasureCacheExceeded = false; + context->booleanWarnings.maxRenderCommandsExceeded = false; + 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(&context->openLayoutElementStack, 0); + Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { .layoutElementIndex = 0 }); +} + +Clay_TextElementConfig Clay__DebugView_ErrorTextConfig = {.textColor = {255, 0, 0, 255}, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }; + +CLAY_WASM_EXPORT("Clay_EndLayout") +Clay_RenderCommandArray Clay_EndLayout() { + Clay_Context* context = Clay_GetCurrentContext(); + Clay__CloseElement(); + if (context->debugModeEnabled) { + context->warningsEnabled = false; + Clay__RenderDebugView(); + context->warningsEnabled = true; + } + if (context->booleanWarnings.maxElementsExceeded) { + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand ) { .boundingBox = { context->layoutDimensions.width / 2 - 59 * 4, context->layoutDimensions.height / 2, 0, 0 }, .config = { .textElementConfig = &Clay__DebugView_ErrorTextConfig }, .text = CLAY_STRING("Clay Error: Layout elements exceeded Clay__maxElementCount"), .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT }); + } else { + Clay__CalculateFinalLayout(); + } + return context->renderCommands; +} + +CLAY_WASM_EXPORT("Clay_GetElementId") +Clay_ElementId Clay_GetElementId(Clay_String idString) { + return Clay__HashString(idString, 0, 0); +} + +CLAY_WASM_EXPORT("Clay_GetElementIdWithIndex") +Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index) { + return Clay__HashString(idString, index, 0); +} + +bool Clay_Hovered(void) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return false; + } + 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 (int32_t i = 0; i < context->pointerOverIds.length; ++i) { + if (Clay__ElementIdArray_Get(&context->pointerOverIds, i)->id == openLayoutElement->id) { + return true; + } + } + return false; +} + +void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerInfo, intptr_t userData), intptr_t userData) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context->booleanWarnings.maxElementsExceeded) { + return; + } + 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 + Clay_Context* context = Clay_GetCurrentContext(); + for (int32_t i = 0; i < context->pointerOverIds.length; ++i) { + if (Clay__ElementIdArray_Get(&context->pointerOverIds, i)->id == elementId.id) { + return true; + } + } + return false; +} + +CLAY_WASM_EXPORT("Clay_GetScrollContainerData") +Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id) { + Clay_Context* context = Clay_GetCurrentContext(); + for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) { + Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i); + if (scrollContainerData->elementId == id.id) { + return CLAY__INIT(Clay_ScrollContainerData) { + .scrollPosition = &scrollContainerData->scrollPosition, + .scrollContainerDimensions = { scrollContainerData->boundingBox.width, scrollContainerData->boundingBox.height }, + .contentDimensions = scrollContainerData->contentSize, + .config = *Clay__FindElementConfigWithType(scrollContainerData->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig, + .found = true + }; + } + } + return CLAY__INIT(Clay_ScrollContainerData) CLAY__DEFAULT_STRUCT; +} + +CLAY_WASM_EXPORT("Clay_GetElementData") +Clay_ElementData Clay_GetElementData(Clay_ElementId id){ + Clay_LayoutElementHashMapItem * item = Clay__GetHashMapItem(id.id); + if(item == &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT) { + return CLAY__INIT(Clay_ElementData) CLAY__DEFAULT_STRUCT; + } + + return CLAY__INIT(Clay_ElementData){ + .boundingBox = item->boundingBox, + .found = true + }; +} + +CLAY_WASM_EXPORT("Clay_SetDebugModeEnabled") +void Clay_SetDebugModeEnabled(bool enabled) { + Clay_Context* context = Clay_GetCurrentContext(); + context->debugModeEnabled = enabled; +} + +CLAY_WASM_EXPORT("Clay_IsDebugModeEnabled") +bool Clay_IsDebugModeEnabled(void) { + Clay_Context* context = Clay_GetCurrentContext(); + return context->debugModeEnabled; +} + +CLAY_WASM_EXPORT("Clay_SetCullingEnabled") +void Clay_SetCullingEnabled(bool enabled) { + Clay_Context* context = Clay_GetCurrentContext(); + context->disableCulling = !enabled; +} + +CLAY_WASM_EXPORT("Clay_SetExternalScrollHandlingEnabled") +void Clay_SetExternalScrollHandlingEnabled(bool enabled) { + Clay_Context* context = Clay_GetCurrentContext(); + context->externalScrollHandlingEnabled = enabled; +} + +CLAY_WASM_EXPORT("Clay_GetMaxElementCount") +int32_t Clay_GetMaxElementCount(void) { + Clay_Context* context = Clay_GetCurrentContext(); + return context->maxElementCount; +} + +CLAY_WASM_EXPORT("Clay_SetMaxElementCount") +void Clay_SetMaxElementCount(int32_t maxElementCount) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context) { + context->maxElementCount = maxElementCount; + } else { + Clay__defaultMaxElementCount = maxElementCount; // TODO: Fix this + } +} + +CLAY_WASM_EXPORT("Clay_GetMaxMeasureTextCacheWordCount") +int32_t Clay_GetMaxMeasureTextCacheWordCount(void) { + Clay_Context* context = Clay_GetCurrentContext(); + return context->maxMeasureTextCacheWordCount; +} + +CLAY_WASM_EXPORT("Clay_SetMaxMeasureTextCacheWordCount") +void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount) { + Clay_Context* context = Clay_GetCurrentContext(); + if (context) { + Clay__currentContext->maxMeasureTextCacheWordCount = maxMeasureTextCacheWordCount; + } else { + Clay__defaultMaxMeasureTextWordCacheCount = maxMeasureTextCacheWordCount; // TODO: Fix this + } +} + +CLAY_WASM_EXPORT("Clay_ResetMeasureTextCache") +void Clay_ResetMeasureTextCache(void) { + Clay_Context* context = Clay_GetCurrentContext(); + context->measureTextHashMapInternal.length = 0; + context->measureTextHashMapInternalFreeList.length = 0; + context->measureTextHashMap.length = 0; + context->measuredWords.length = 0; + context->measuredWordsFreeList.length = 0; + + for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) { + context->measureTextHashMap.internalArray[i] = 0; + } + context->measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element" +} + +#endif // CLAY_IMPLEMENTATION + +/* +LICENSE +zlib/libpng license + +Copyright (c) 2024 Nic Barker + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ diff --git a/bindings/c3/project.json b/bindings/c3/project.json new file mode 100644 index 0000000..c315027 --- /dev/null +++ b/bindings/c3/project.json @@ -0,0 +1,33 @@ +{ + "langrev": "1", + "authors": [ "Jefferey Schlueter " ], + "version": "0.1.0", + "targets": { + + // TODO: found out how to stop this from outputting a .lib and .pdb in addition to the .exe (they don't do anything) + "video-example": { + "output": "build/video-example/", + "c-sources": [ "c-lang/source/clay.c" ], + "cflags": "-DCLAY_IMPLEMENTATION", // makes the clay source actually define things + "type": "executable", + "dependency-search-paths": [ "lib" ], + "dependencies": [ "raylib55" ], + "sources": [ "source/clay.c3", "source/clay-raylib-renderer.c3", "examples/video-example.c3" ], + // "link-libc": false, // TODO; leads to duplicate definitions (eg math_nolibc) + // "use-stdlib": false, // TODO: leads to ZString being undefined -> then mising main @main_to_void_main + // "features": ["NO_STDLIB"] + }, + + // TODO: figure out why creating static/dynamic libraries with C sources doesn't work (emits no errors just crashes out and dumps an empty clay-win.h into the project dir) + // "clay-win": { + // "output": "build/clay.c3l/windows-x64", + // "c-sources": [ "c-lang/source/clay.c" ], + // "sources": [ "source/clay.c3" ], + // "type": "static-lib" + // }, + // build args with no C references + //../c3c.exe --link-libc=no --use-stdlib=no -o clay --template static-lib --output-dir build/clay.c3l/windows-x64 --print-linking --print-output -vvv static-lib source/clay-slim.c3 + }, + "cc": "gcc", + "cpu": "generic", +} \ No newline at end of file diff --git a/bindings/c3/resources/Lexend-Regular.ttf b/bindings/c3/resources/Lexend-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c5c1417b03434bbe990fddabe540e7a8ad421bab GIT binary patch literal 77836 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%0DZAasJBuHTl=%-(YHV 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 literal 0 HcmV?d00001 diff --git a/bindings/c3/source/clay-depracated.c3 b/bindings/c3/source/clay-depracated.c3 new file mode 100644 index 0000000..7dac453 --- /dev/null +++ b/bindings/c3/source/clay-depracated.c3 @@ -0,0 +1,700 @@ +// TODO: including additional structures required for Clay_Context led to bloat + +// module clay; + +// // import std::core::cinterop ; +// import clay::carray; + +// // ======================= +// // ===== USER MACROS ===== +// // ======================= +// macro @clay(...; @body()) @builtin +// { +// clay::openElement(); +// $for (var $i = 0; $i < $vacount; $i++) +// $vaexpr[$i]; // If you get an error here consisder the @body[...]() macros +// $endfor +// clay::elementPostConfiguration(); +// @body(); +// clay::closeElement(); +// } + +// macro text(String text, TextElementConfig *config) { clay::openTextElement({text.len, text}, config); } + +// <*Provides you conditional calls (eg #booleanCondition ? #doA : 0) within the condifuration of @clay(...)*> +// macro @bodyIf(#condition, #ifRes) { if (#condition) { #ifRes; } } + +// <*Provides you conditional calls (eg #booleanCondition ? #doA : #doB) within the condifuration of @clay(...)*> +// macro @bodyIfElse(#condition, #ifRes, #elseRes) { if (#condition) { #ifRes; } else { #elseRes; } } + +// // <*Facilitates non-method calls (eg { #doWhatever }, { #booleanExpression ? #doA : #doB}, etc.) within the parameters of @clay(...)*> +// // macro @inline(; @body()) { @body(); } + +// <*attaches a RectangleElementConfig to the clay element when called within @clay(...)*> +// macro rectangle(RectangleElementConfig config) { clay::attachElementConfig({ .rectangleElementConfig = clay::storeRectangleElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_RECTANGLE ); } + +// <*attaches a LayoutConfig to the clay element when called within @clay(...)*> +// macro layout(LayoutConfig config) { clay::attachLayoutConfig( clay::storeLayoutConfig(config) ); } + +// <*attaches a LayoutConfig to the clay element when called within @clay(...)*> +// macro scroll(ScrollElementConfig config) { clay::attachElementConfig({ .scrollElementConfig = clay::storeScrollElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER ); } + +// <*attaches a FloatingElementConfig to the clay element when called within @clay(...)*> +// macro floating(FloatingElementConfig config) { clay::attachElementConfig({ .floatingElementConfig = clay::storeFloatingElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER ); } + +// <*attaches a BorderElementConfig to the clay element when called within @clay(...)*> +// macro @borderRadiusUni(uint #width, ClayColor #color, float #cornerRadius = 0) { clay::attachElementConfig({ .borderElementConfig = clay::storeBorderElementConfig({ .left = { #width, #color }, .right = { #width, #color }, .top = { #width, #color }, .bottom = { #width, #color }, .#cornerRadius = {#cornerRadius, #cornerRadius, #cornerRadius, #cornerRadius}})}, clay::ELEMENT_CONFIG_TYPE_BORDER_CONTAINER); } + +// macro id(String idString) { clay::attachId(clay::hashString({idString.len, idString}, 0, 0)); } + +// macro TextElementConfig* textConfig(TextElementConfig config) { return clay::storeTextElementConfig(config); } + +// macro SizingAxis sizingFit(float min = 0, float max = float.max) { return { .size.minMax = {min, max}, .type = SizingType.FIT }; } + +// macro SizingAxis sizingGrow() { return { .size.minMax = {0, 0}, .type = SizingType.GROW }; } + +// macro SizingAxis sizingFixed(float pixels) { return { .size.minMax = {pixels, pixels}, .type = SizingType.FIXED }; } + +// macro SizingAxis sizingPercent(float percent) { return { .size.percent = percent, .type = SizingType.PERCENT }; } + +// macro Padding paddingUni(ushort uniform) { return {uniform, uniform, uniform, uniform}; } + +// macro Padding padding(ushort horizontal, ushort vertical) { return {horizontal, horizontal, vertical, vertical}; } + +// macro CornerRadius cornerRadiusUni(float uniform) { return {uniform, uniform, uniform, uniform}; } + +// macro SizingAxis sizingFitCT(float $min = 0, float $max = float.max) { return { .size.minMax = {$min, $max}, .type = SizingType.FIT }; } + +// macro SizingAxis sizingFixedCT(float $pixels) { return { .size.minMax = {$pixels, $pixels}, .type = SizingType.FIXED }; } + +// macro SizingAxis sizingPercentCT(float $percent) { return { .size.percent = $percent, .type = SizingType.PERCENT }; } + +// macro Padding paddingCT(ushort $a, ushort $b, ushort $c, ushort $d) { return { $a, $b, $c, $d }; } + +// macro CornerRadius @cornerRadiusUniCT(float #uniform) { return {#uniform, #uniform, #uniform, #uniform}; } + +// // =================== +// // ===== STRUCTS ===== +// // =================== +// struct ClayString +// { +// int length; +// char *chars; +// } +// def ClayStringArray = carray::Array() @private; + +// struct Arena +// { +// uint128 nextAllocation; +// uint128 capacity; +// char *memory; +// } + +// struct Dimensions +// { +// float width, height; +// } + +// struct ClayVector2 +// { +// float x, y; +// } + +// struct ClayColor +// { +// float r, g, b, a; +// } + +// struct ClayBoundingBox +// { +// float x, y, width, height; +// } + +// struct ElementId +// { +// uint id; +// uint offset; +// uint baseId; +// ClayString stringId; +// } + +// struct CornerRadius +// { +// float topLeft; +// float topRight; +// float bottomLeft; +// float bottomRight; +// } + +// // ===== Element Configs ===== +// distinct ElementConfigType @private = char; +// const ElementConfigType ELEMENT_CONFIG_TYPE_NONE @private = 0; +// const ElementConfigType ELEMENT_CONFIG_TYPE_RECTANGLE @private = 1; +// const ElementConfigType ELEMENT_CONFIG_TYPE_BORDER_CONTAINER @private = 2; +// const ElementConfigType ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER @private = 4; +// const ElementConfigType ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER @private = 8; +// const ElementConfigType ELEMENT_CONFIG_TYPE_IMAGE @private = 16; +// const ElementConfigType ELEMENT_CONFIG_TYPE_TEXT @private = 32; +// const ElementConfigType ELEMENT_CONFIG_TYPE_CUSTOM @private = 64; + +// enum LayoutDirection : char @export +// { +// LEFT_TO_RIGHT, +// TOP_TO_BOTTOM, +// } + +// enum AlignX : char @export +// { +// LEFT, +// RIGHT, +// CENTER, +// } + +// enum AlignY : char @export +// { +// TOP, +// BOTTOM, +// CENTER, +// } + +// enum SizingType : char @export +// { +// FIT, +// GROW, +// PERCENT, +// FIXED, +// } + +// struct ChildAlignment +// { +// AlignX x; +// AlignY y; +// } + +// struct SizingMinMax +// { +// float min; +// float max; +// } + +// struct SizingAxis +// { +// union size +// { +// SizingMinMax minMax; +// float percent; +// } +// SizingType type; +// } + +// struct Sizing +// { +// SizingAxis width; +// SizingAxis height; +// } + +// struct Padding +// { +// ushort left; +// ushort right; +// ushort top; +// ushort bottom; +// } + +// struct LayoutConfig +// { +// Sizing sizing; +// Padding padding; +// ushort childGap; +// ChildAlignment childAlignment; +// LayoutDirection layoutDirection; +// } + +// struct RectangleElementConfig +// { +// ClayColor color; +// CornerRadius cornerRadius; +// // #ifdef CLAY_EXTEND_CONFIG_RECTANGLE +// // CLAY_EXTEND_CONFIG_RECTANGLE +// // #endif +// } + +// enum WrapMode @export +// { +// WORDS, +// NEWLINES, +// NONE, +// } + +// struct TextElementConfig +// { +// ClayColor textColor; +// ushort fontId; +// ushort fontSize; +// ushort letterSpacing; +// ushort lineHeight; +// WrapMode wrapMode; +// // #ifdef CLAY_EXTEND_CONFIG_TEXT +// // CLAY_EXTEND_CONFIG_TEXT +// // #endif +// } + +// struct ImageElementConfig +// { +// void *imageData; +// Dimensions sourceDimensions; +// // #ifdef CLAY_EXTEND_CONFIG_IMAGE +// // CLAY_EXTEND_CONFIG_IMAGE +// // #endif +// } + +// enum AttachPoint : char @export +// { +// LEFT_TOP, +// LEFT_CENTER, +// LEFT_BOTTOM, +// CENTER_TOP, +// CENTER_CENTER, +// CENTER_BOTTOM, +// RIGHT_TOP, +// RIGHT_CENTER, +// RIGHT_BOTTOM, +// } + +// struct FloatingAttachPoints +// { +// AttachPoint element; +// AttachPoint parent; +// } + +// enum PointerCaptureMode @export +// { +// CAPTURE, +// // MODE_PASSTHROUGH, +// PARENT, +// } + +// struct FloatingElementConfig +// { +// ClayVector2 offset; +// Dimensions expand; +// ushort zIndex; +// uint parentId; +// FloatingAttachPoints attachment; +// PointerCaptureMode pointerCaptureMode; +// } + + +// struct CustomElementConfig +// { +// // #ifndef CLAY_EXTEND_CONFIG_CUSTOM +// void *customData; +// // #else +// // CLAY_EXTEND_CONFIG_CUSTOM +// // #endif +// } + +// struct ScrollElementConfig +// { +// bool horizontal; +// bool vertical; +// } + +// // Border +// struct Border +// { +// uint width; +// ClayColor color; +// } + +// struct BorderElementConfig +// { +// Border left; +// Border right; +// Border top; +// Border bottom; +// Border betweenChildren; +// CornerRadius cornerRadius; +// // #ifdef CLAY_EXTEND_CONFIG_BORDER +// // CLAY_EXTEND_CONFIG_BORDER +// // #endif +// } + +// union ElementConfigUnion +// { +// RectangleElementConfig *rectangleElementConfig; +// TextElementConfig *textElementConfig; +// ImageElementConfig *imageElementConfig; +// FloatingElementConfig *floatingElementConfig; +// CustomElementConfig *customElementConfig; +// ScrollElementConfig *scrollElementConfig; +// BorderElementConfig *borderElementConfig; +// } + +// struct ElementConfig +// { +// ElementConfigType type; +// ElementConfigUnion config; +// } + +// // Miscellaneous Structs & Enums --------------------------------- +// struct ScrollContainerData +// { +// // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. +// // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. +// ClayVector2 *scrollPosition; +// Dimensions scrollContainerDimensions; +// Dimensions contentDimensions; +// ScrollElementConfig config; +// // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. +// bool found; +// } + +// <* +// @ +// *> +// struct ElementData +// { +// ClayBoundingBox boundingBox; +// // Indicates whether an actual Element matched the provided ID or if the default struct was returned. +// bool found; +// } + +// enum RenderCommandType : char @export +// { +// NONE, +// RECTANGLE, +// BORDER, +// TEXT, +// IMAGE, +// SCISSOR_START, +// SCISSOR_END, +// CUSTOM, +// } + +// struct RenderCommand +// { +// ClayBoundingBox boundingBox; +// ElementConfigUnion config; +// ClayString text; +// uint id; +// RenderCommandType commandType; +// } +// def RenderCommandArray = carray::Array(); + +// enum PointerState @export +// { +// PRESSED_THIS_FRAME, +// PRESSED, +// RELEASED_THIS_FRAME, +// RELEASED, +// } + +// struct PointerData +// { +// ClayVector2 position; +// PointerState state; +// } + +// enum ErrorType @export +// { +// TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED, +// ARENA_CAPACITY_EXCEEDED, +// ELEMENTS_CAPACITY_EXCEEDED, +// TEXT_MEASUREMENT_CAPACITY_EXCEEDED, +// DUPLICATE_ID, +// FLOATING_CONTAINER_PARENT_NOT_FOUND, +// INTERNAL_ERROR, +// } + +// struct ErrorData +// { +// ErrorType errorType; +// ClayString errorText; +// uint128 userData; +// } + +// def ErrorHandleFunc = fn void(ErrorData errorText); + +// struct ErrorHandler +// { +// ErrorHandleFunc errorHandler; +// uint128 userData; +// } + +// struct BooleanWarnings +// { +// bool maxElementsExceeded; +// bool maxRenderCommandsExceeded; +// bool maxTextMeasureCacheExceeded; +// } + +// struct Warning +// { +// ClayString baseMessage; +// ClayString dynamicMessage; +// } + +// def WarningArray = carray::Array(); + +// struct LayoutElementChildren +// { +// int *elements; +// ushort length; +// } + +// struct LayoutElement +// { +// union childrenOrTextContent { +// LayoutElementChildren children; +// TextElementData *textElementData; +// } +// Dimensions dimensions; +// Dimensions minDimensions; +// LayoutConfig *layoutConfig; +// ElementConfigArraySlice elementConfigs; +// uint configsEnabled; +// uint id; +// } + +// struct WrappedTextLine +// { +// Dimensions dimensions; +// ClayString line; +// } + +// struct WrappedTextLineArraySlice +// { +// int length; +// WrappedTextLine *internalArray; +// } + +// struct TextElementData +// { +// ClayString text; +// Dimensions preferredDimensions; +// int elementIndex; +// WrappedTextLineArraySlice wrappedLines; +// } + +// struct ElementConfigArraySlice +// { +// int length; +// ElementConfig *internalArray; +// } + + +// def DebugElementData = bool[<2>]; + +// struct LayoutElementHashMapItem +// { +// ClayBoundingBox boundingBox; +// ElementId elementId; +// LayoutElement* layoutElement; +// OnHoverEvent onHoverFunction; +// int128 hoverFunctionUserData; +// int nextIndex; +// uint128 generation; +// DebugElementData *debugData; +// } + +// struct LayoutElementTreeRoot @private +// { +// int layoutElementIndex; +// uint parentId; // This can be zero in the case of the root layout tree +// uint clipElementId; // This can be zero if there is no clip element +// int zIndex; +// ClayVector2 pointerOffset; // Only used when scroll containers are managed externally +// } + +// struct LayoutElementTreeNode @private +// { +// LayoutElement *layoutElement; +// ClayVector2 position; +// ClayVector2 nextChildOffset; +// } + +// struct MeasuredWord @private +// { +// int startOffset; +// int length; +// float width; +// int next; +// } + +// struct MeasureTextCacheItem @private +// { +// Dimensions unwrappedDimensions; +// int measuredWordsStartIndex; +// bool containsNewlines; +// // Hash map data +// uint id; +// int nextIndex; +// uint generation; +// } + +// def LayoutElementArray = carray::Array(); +// def CIntArray = carray::Array(); +// def TextElementDataArray = carray::Array(); +// def RectangleElementConfigArray = carray::Array(); +// def TextElementConfigArray = carray::Array(); +// def ImageElementConfigArray = carray::Array(); +// def FloatingElementConfigArray = carray::Array(); +// def ScrollElementConfigArray = carray::Array(); +// def CustomElementConfigArray = carray::Array(); +// def BorderElementConfigArray = carray::Array(); +// def LayoutElementPointerArray = carray::Array(); +// def LayoutConfigArray = carray::Array(); +// def ElementConfigArray = carray::Array(); +// def WrappedTextLineArray = carray::Array(); +// def LayoutElementTreeNodeArray = carray::Array(); +// def LayoutElementTreeRootArray = carray::Array(); +// def LayoutElementHashMapItemArray = carray::Array(); +// def MeasureTextCacheItemArray = carray::Array(); +// def MeasuredWordArray = carray::Array(); +// def ElementIdArray = carray::Array(); +// def ScrollContainerDataInternalArray = carray::Array(); +// def BoolArray = carray::Array(); +// def CharArray = carray::Array(); +// def DebugElementDataArray = carray::Array(); + +// struct Context @extern ("Clay_Context") +// { +// int maxElementCount; +// int maxMeasureTextCacheWordCount; +// bool warningsEnabled; +// ErrorHandler errorHandler; +// BooleanWarnings booleanWarnings; +// WarningArray warnings; + +// PointerData pointerInfo; +// Dimensions layoutDimensions; +// ElementId dynamicElementIndexBaseHash; +// uint dynamicElementIndex; +// bool debugModeEnabled; +// bool disableCulling; +// bool externalScrollHandlingEnabled; +// uint debugSelectedElementId; +// uint generation; +// uint128 arenaResetOffset; +// Arena internalArena; + +// // Layout Elements / Render Commands +// LayoutElementArray layoutElements; +// RenderCommandArray renderCommands; +// CIntArray openLayoutElementStack; +// CIntArray layoutElementChildren; +// CIntArray layoutElementChildrenBuffer; +// TextElementDataArray textElementData; +// LayoutElementPointerArray imageElementPointers; +// CIntArray reusableElementIndexBuffer; +// CIntArray layoutElementClipElementIds; + +// // Configs +// LayoutConfigArray layoutConfigs; +// ElementConfigArray elementConfigBuffer; +// ElementConfigArray elementConfigs; +// RectangleElementConfigArray rectangleElementConfigs; +// TextElementConfigArray textElementConfigs; +// ImageElementConfigArray imageElementConfigs; +// FloatingElementConfigArray floatingElementConfigs; +// ScrollElementConfigArray scrollElementConfigs; +// CustomElementConfigArray customElementConfigs; +// BorderElementConfigArray borderElementConfigs; + +// // Misc Data Structures +// ClayStringArray layoutElementIdStrings; +// WrappedTextLineArray wrappedTextLines; +// LayoutElementTreeNodeArray layoutElementTreeNodeArray1; +// LayoutElementTreeRootArray layoutElementTreeRoots; +// LayoutElementHashMapItemArray layoutElementsHashMapInternal; +// CIntArray layoutElementsHashMap; +// MeasureTextCacheItemArray measureTextHashMapInternal; +// CIntArray measureTextHashMapInternalFreeList; +// CIntArray measureTextHashMap; +// MeasuredWordArray measuredWords; +// CIntArray measuredWordsFreeList; +// CIntArray openClipElementStack; +// ElementIdArray pointerOverIds; +// ScrollContainerDataInternalArray scrollContainerDatas; +// BoolArray treeNodeVisited; +// CharArray dynamicStringData; +// DebugElementDataArray debugElementData; +// } + +// def OnHoverEvent = fn void(ElementId elementId, PointerData pointerData, iptr userData); +// def MeasureTextFunc = fn Dimensions(ClayString *text, TextElementConfig *config); +// def QueryScrollOffsetFunc = fn ClayVector2(uint elementId); + +// // ===================== +// // ===== FUNCTIONS ===== +// // ===================== + +// // ===== Public Clay API C3 Functions (String replacement) ===== +// fn ElementId getElementIdWithIndex(String idString, uint index) @export @inline +// { return __getElementIdWithIndex({idString.len, idString}, (uint)index); } +// fn ElementId getElementId(String idString) @export @inline +// { return __getElementId({idString.len, idString}); } + + +// // ===== Public Clay API Functions ===== +// extern fn uint minMemorySize() @extern("Clay_MinMemorySize") @wasm @export; +// extern fn Arena createArena(uint capacity, void* offset) @extern("Clay_CreateArenaWithCapacityAndMemory") @wasm @export; +// extern fn void setPointerState(ClayVector2 position, bool pointerDown) @extern("Clay_SetPointerState") @export; +// extern fn Context* initialize(Arena arena, Dimensions layoutDimensions, ErrorHandler errorHandler) @extern("Clay_Initialize") @wasm @export; +// extern fn Context* getCurrentContext() @extern("Clay_GetCurrentContext") @export; +// extern fn void setCurrentContext(Context* context) @extern("Clay_SetCurrentContext") @export; +// extern fn void updateScrollContainer(bool enableDragScrolling, ClayVector2 scrollDelta, float deltaTime) @extern("Clay_UpdateScrollContainers") @export; +// extern fn void setLayoutDimensions (Dimensions dimensions) @extern("Clay_SetLayoutDimensions") @export; +// extern fn ElementData getElementData(ElementId id) @extern("Clay_GetElementData") @export; +// extern fn bool hovered() @extern("Clay_Hovered") @export; +// extern fn void onHover(OnHoverEvent onHover, iptr userData) @extern("Clay_OnHover") @export; +// extern fn bool pointerOver(ElementId elementId) @extern("Clay_PointerOver") @wasm @export; +// extern fn ScrollContainerData getScrollContainerData(ElementId id) @extern("Clay_GetScrollContainerData") @export; +// extern fn void setMeasureTextFunction(MeasureTextFunc measureText) @extern("Clay_SetMeasureTextFunction") @export; +// extern fn void setQueryScrollOffsetFunction(QueryScrollOffsetFunc queryScrollOffset) @extern("Clay_SetQueryScrollOffsetFunction") @export; +// extern fn RenderCommand * RenderCommandArray.get(RenderCommandArray* array, int index) @extern("Clay_RenderCommandArray_Get") @export; +// extern fn void setDebugModeEnabled(bool enabled) @extern("Clay_SetDebugModeEnabled") @export; +// extern fn bool isDebugModeEnabled() @extern("Clay_IsDebugModeEnabled") @export; +// extern fn void setCullingEnabled(bool enabled) @extern("Clay_SetCullingEnabled") @export; +// extern fn int getMaxMeasuredTextCachedWordCount() @extern("Clay_GetMaxElementCount") @export; +// extern fn void setMaxElementCount(int maxElementCount) @extern("Clay_SetMaxElementCount") @export; +// extern fn int getMaxElementCount() @extern("Clay_GetMaxMeasureTextCacheWordCount") @export; +// extern fn void setMaxMeasureTextCacheWordCount(int maxMeasureTextCacheWordCount) @extern("Clay_SetMaxMeasureTextCacheWordCount") @export; +// extern fn void resetMeasureTextCache() @extern("Clay_ResetMeasureTextCache") @export; +// extern fn void beginLayout() @extern("Clay_BeginLayout") @export; +// extern fn RenderCommandArray endLayout() @extern("Clay_EndLayout") @export; + +// // ===== (NEW) Internal Clay API Functions (String replacement) ===== +// extern fn ElementId __getElementIdWithIndex(ClayString idString, uint index) @extern("Clay_GetElementIdWithIndex") @export @private; +// extern fn ElementId __getElementId(ClayString idString) @extern("Clay_GetElementId") @export @private; + +// // ===== Internal Clay API Functions ===== +// extern fn void openElement() @extern ("Clay__OpenElement") @export @private; +// extern fn void closeElement() @extern("Clay__CloseElement") @export @private; +// extern fn void openTextElement(ClayString text, TextElementConfig *textConfig) @extern("Clay__OpenTextElement") @export @private; +// extern fn void elementPostConfiguration() @extern("Clay__ElementPostConfiguration") @export @private; +// extern fn LayoutConfig * storeLayoutConfig(LayoutConfig config) @extern("Clay__StoreLayoutConfig") @export @private; +// extern fn void attachId(ElementId id) @extern("Clay__AttachId") @export @private; +// extern fn void attachLayoutConfig(LayoutConfig *config) @extern("Clay__AttachLayoutConfig") @export @private; +// extern fn void attachElementConfig(ElementConfigUnion config, ElementConfigType type) @extern("Clay__AttachElementConfig") @export @private; +// extern fn RectangleElementConfig * storeRectangleElementConfig(RectangleElementConfig config) @extern("Clay__StoreRectangleElementConfig") @export @private; +// extern fn TextElementConfig * storeTextElementConfig(TextElementConfig config) @extern("Clay__StoreTextElementConfig") @export @private; +// extern fn ImageElementConfig * storeImageElementConfig(ImageElementConfig config) @extern("Clay__StoreImageElementConfig") @export @private; +// extern fn FloatingElementConfig * storeFloatingElementConfig(FloatingElementConfig config) @extern("Clay__StoreFloatingElementConfig") @export @private; +// extern fn CustomElementConfig * storeCustomElementConfig(CustomElementConfig config) @extern("Clay__StoreCustomElementConfig") @export @private; +// extern fn ScrollElementConfig * storeScrollElementConfig(ScrollElementConfig config) @extern("Clay__StoreScrollElementConfig") @export @private; +// extern fn BorderElementConfig * storeBorderElementConfig(BorderElementConfig config) @extern("Clay__StoreBorderElementConfig") @export @private; +// extern fn ElementId hashString(ClayString key, uint offset, uint seed) @extern("Clay__HashString") @export @private; +// extern fn uint getParentElementId() @extern("Clay__GetParentElementId") @export @private; + +// // ========================================================================== +// // ===== An internal module for wrapping Struct Array's defined in Clay ===== +// // ========================================================================== +// module clay::carray(); + +// struct Array { +// int capacity; +// int length; +// ElementType *data; +// } \ No newline at end of file diff --git a/bindings/c3/source/clay-raylib-renderer.c3 b/bindings/c3/source/clay-raylib-renderer.c3 new file mode 100644 index 0000000..1a0b618 --- /dev/null +++ b/bindings/c3/source/clay-raylib-renderer.c3 @@ -0,0 +1,448 @@ +module raylib @if($feature(NO_STDLIB)); + +distinct ZString = inline char*; + +module clay::renderer; + +import raylib; + +// TODO: this entire file was very rushed so it can probably be cleaned up a LOT +fn double calculate_sine(double x) { + double term = x; + double sum = term; + double n = 1; + + for (int i = 1; i <= 10; i++) { + term *= (-1) * x * x / ((2 * n) * (2 * n + 1)); + sum += term; + n++; + } + + return sum; +} + +fn double calculate_cosine(double x) { + double term = 1; + double sum = term; + double n = 1; + + for (int i = 1; i <= 10; i++) { + term *= (-1) * x * x / ((2 * n - 1) * (2 * n)); + sum += term; + n++; + } + + return sum; +} + +macro double calculate_tangent(double x) { + double sine = calculate_sine(x); + double cosine = calculate_cosine(x); + // Check for cases where cosine is zero (tangent is undefined) + if (cosine == 0.0) { + return 0.0; + } + return sine / cosine; +} + + +fn Color clayToRaylibColor(ClayColor color) @inline +{ + return { (char)$$round(color.r), (char)$$round(color.g), (char)$$round(color.b), (char)$$round(color.a)}; +} + +struct RaylibFont +{ + int fontId; + raylib::Font font; +} + +RaylibFont[10] raylibFonts; +raylib::Camera raylibCamera; + +const Matrix MATRIX_IDENTITY = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +}; + +enum CustomLayoutElementType +{ + MODEL_3D +} + +struct Model3DLayoutElement +{ + raylib::Model model; + float scale; + raylib::Vector3 position; + raylib::Matrix rotation; +} + +struct CustomLayoutElement +{ + CustomLayoutElementType type; + union element + { + Model3DLayoutElement model; + } +} + +fn Matrix matrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) +{ + Matrix result = { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 }; + + double top = nearPlane * calculate_tangent(fovY*0.5*raylib::RAD2DEG); + double bottom = -top; + double right = top*aspect; + double left = -right; + + // MatrixFrustum(-right, right, -top, top, near, far); + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float far_near = (float)(farPlane - nearPlane); + + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m5 = ((float)nearPlane*2.0f)/tb; + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)farPlane + (float)nearPlane)/far_near; + result.m11 = -1.0f; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/far_near; + + return result; +} + +fn Vector3 vector3Unproject(Vector3 source, Matrix projection, Matrix view) +{ + Vector3 result = { 0, 0, 0 }; + + // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it + Matrix matViewProj = { // MatrixMultiply(view, projection); + view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, + view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, + view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, + view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, + view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, + view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, + view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, + view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, + view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, + view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, + view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, + view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, + view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, + view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, + view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, + view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; + + // Calculate inverted matrix . MatrixInvert(matViewProj); + // Cache the matrix values (speed optimization) + float a00 = matViewProj.m0; + float a01 = matViewProj.m1; + float a02 = matViewProj.m2; + float a03 = matViewProj.m3; + float a10 = matViewProj.m4; + float a11 = matViewProj.m5; + float a12 = matViewProj.m6; + float a13 = matViewProj.m7; + float a20 = matViewProj.m8; + float a21 = matViewProj.m9; + float a22 = matViewProj.m10; + float a23 = matViewProj.m11; + float a30 = matViewProj.m12; + float a31 = matViewProj.m13; + float a32 = matViewProj.m14; + float a33 = matViewProj.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + Matrix matViewProjInv = { + (a11*b11 - a12*b10 + a13*b09)*invDet, + (-a01*b11 + a02*b10 - a03*b09)*invDet, + (a31*b05 - a32*b04 + a33*b03)*invDet, + (-a21*b05 + a22*b04 - a23*b03)*invDet, + (-a10*b11 + a12*b08 - a13*b07)*invDet, + (a00*b11 - a02*b08 + a03*b07)*invDet, + (-a30*b05 + a32*b02 - a33*b01)*invDet, + (a20*b05 - a22*b02 + a23*b01)*invDet, + (a10*b10 - a11*b08 + a13*b06)*invDet, + (-a00*b10 + a01*b08 - a03*b06)*invDet, + (a30*b04 - a31*b02 + a33*b00)*invDet, + (-a20*b04 + a21*b02 - a23*b00)*invDet, + (-a10*b09 + a11*b07 - a12*b06)*invDet, + (a00*b09 - a01*b07 + a02*b06)*invDet, + (-a30*b03 + a31*b01 - a32*b00)*invDet, + (a20*b03 - a21*b01 + a22*b00)*invDet }; + + // Create quaternion from source point + raylib::Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unprojecte matrix + raylib::Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) + matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, + matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, + matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, + matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; + + // Normalized world points in vectors + result.x = qtransformed.x/qtransformed.w; + result.y = qtransformed.y/qtransformed.w; + result.z = qtransformed.z/qtransformed.w; + + return result; +} + +fn Matrix matrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + Matrix result = { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float far_near = (float)(farPlane - nearPlane); + + result.m0 = 2.0f/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + result.m4 = 0.0f; + result.m5 = 2.0f/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + result.m8 = 0.0f; + result.m9 = 0.0f; + result.m10 = -2.0f/far_near; + result.m11 = 0.0f; + result.m12 = -((float)left + (float)right)/rl; + result.m13 = -((float)top + (float)bottom)/tb; + result.m14 = -((float)farPlane + (float)nearPlane)/far_near; + result.m15 = 1.0f; + + return result; +} + +fn Vector3 vector3Normalize(Vector3 v) +{ + Vector3 result = v; + + float length = $$sqrt(v.x*v.x + v.y*v.y + v.z*v.z); + if (length != 0.0f) + { + float ilength = 1.0f/length; + + result.x *= ilength; + result.y *= ilength; + result.z *= ilength; + } + + return result; +} + +fn Vector3 vector3Subtract(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; + + return result; +} + + +fn Ray getScreenToWorldPointWithZDistance(Vector2 position, Camera camera, int screenWidth, int screenHeight, float zDistance) +{ + Ray ray = { {0,0,0}, {0,0,0} }; + float x = (2.0f*position.x)/(float)screenWidth - 1.0f; + float y = 1.0f - (2.0f*position.y)/(float)screenHeight; + float z = 1.0f; + + // Store values in a vector + Vector3 deviceCoords = { x, y, z }; + + // Calculate view matrix from camera look at + Matrix matView = raylib::getCameraMatrix(camera); + + Matrix matProj = MATRIX_IDENTITY; + + if (camera.projection == CameraProjection.PERSPECTIVE) + { + // Calculate projection matrix from perspective + matProj = matrixPerspective((double)(camera.fovy*raylib::DEG2RAD), ((double)screenWidth/(double)screenHeight), 0.01f, zDistance); + } + else if (camera.projection == CameraProjection.ORTHOGRAPHIC) + { + double aspect = (double)screenWidth/(double)screenHeight; + double top = camera.fovy/2.0; + double right = top*aspect; + + // Calculate projection matrix from orthographic + matProj = matrixOrtho(-right, right, -top, top, 0.01, 1000.0); + } + + // Unproject far/near points + Vector3 nearPoint = vector3Unproject({ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView); + Vector3 farPoint = vector3Unproject({ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView); + + // Calculate normalized direction vector + Vector3 direction = vector3Normalize(vector3Subtract(farPoint, nearPoint)); + + ray.position = farPoint; + + // Apply calculated vectors to ray + ray.direction = direction; + + return ray; +} + +fn Dimensions raylibMeasureText(ClayString *text, TextElementConfig *config) +{ + // Measure string size for Font + Dimensions textSize = { 0, 0 }; + + float maxTextWidth = 0.0f; + float lineTextWidth = 0; + + float textHeight = config.fontSize; + Font fontToUse = raylibFonts[config.fontId].font; + float scaleFactor = config.fontSize/(float)fontToUse.baseSize; + + for (int i = 0; i < text.length; ++i) + { + if (text.chars[i] == '\n') { + maxTextWidth = $$max(maxTextWidth, lineTextWidth); + lineTextWidth = 0; + continue; + } + int index = text.chars[i] - 32; + if (fontToUse.glyphs[index].advanceX != 0) {lineTextWidth += fontToUse.glyphs[index].advanceX;} + else {lineTextWidth += (fontToUse.recs[index].width + fontToUse.glyphs[index].offsetX);} + } + + maxTextWidth = $$max(maxTextWidth, lineTextWidth); + + textSize.width = maxTextWidth * scaleFactor; + textSize.height = textHeight; + + return textSize; +} + +fn void raylibRender(RenderCommandArray renderCommands) +{ + for (int j = 0; j < renderCommands.length; j++) + { + RenderCommand *renderCommand = renderCommands.get(j); + ClayBoundingBox boundingBox = renderCommand.boundingBox; + switch (renderCommand.commandType) + { + case RenderCommandType.TEXT: { + // Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator + ClayString text = renderCommand.text; + ZString cloned = (ZString)malloc((ulong)text.length + 1); + $$memcpy(cloned, text.chars, (isz)text.length, false, (isz)0, (isz)0); + cloned[text.length] = '\0'; + Font fontToUse = raylibFonts[renderCommand.config.textElementConfig.fontId].font; + raylib::drawTextEx(fontToUse, cloned, {boundingBox.x, boundingBox.y}, (float)renderCommand.config.textElementConfig.fontSize, (float)renderCommand.config.textElementConfig.letterSpacing, clayToRaylibColor(renderCommand.config.textElementConfig.textColor)); + free(cloned); + break; + } + case RenderCommandType.IMAGE: { + Texture2D imageTexture = *(Texture2D *)renderCommand.config.imageElementConfig.imageData; + raylib::drawTextureEx( + imageTexture, + {boundingBox.x, boundingBox.y}, + 0, + boundingBox.width / (float)imageTexture.width, + raylib::WHITE); + break; + } + case RenderCommandType.SCISSOR_START: { + raylib::beginScissorMode((int)$$round(boundingBox.x), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width), (int)$$round(boundingBox.height)); + break; + } + case RenderCommandType.SCISSOR_END: { + raylib::endScissorMode(); + break; + } + case RenderCommandType.RECTANGLE: { + RectangleElementConfig *config = renderCommand.config.rectangleElementConfig; + if (config.cornerRadius.topLeft > 0) { + float radius = (config.cornerRadius.topLeft * 2) / (float)(boundingBox.width > boundingBox.height ? boundingBox.height : boundingBox.width); + raylib::drawRectangleRounded({ boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, clayToRaylibColor(config.color)); + } else { + raylib::drawRectangle((int)$$round(boundingBox.x), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width), (int)$$round(boundingBox.height), clayToRaylibColor(config.color)); + } + break; + } + case RenderCommandType.BORDER: { + BorderElementConfig *config = renderCommand.config.borderElementConfig; + // Left border + if (config.left.width > 0) { + raylib::drawRectangle((int)$$round(boundingBox.x), (int)$$round(boundingBox.y + config.cornerRadius.topLeft), (int)config.left.width, (int)$$round(boundingBox.height - config.cornerRadius.topLeft - config.cornerRadius.bottomLeft), clayToRaylibColor(config.left.color)); + } + // Right border + if (config.right.width > 0) { + raylib::drawRectangle((int)$$round(boundingBox.x + boundingBox.width - config.right.width), (int)$$round(boundingBox.y + config.cornerRadius.topRight), (int)config.right.width, (int)$$round(boundingBox.height - config.cornerRadius.topRight - config.cornerRadius.bottomRight), clayToRaylibColor(config.right.color)); + } + // Top border + if (config.top.width > 0) { + raylib::drawRectangle((int)$$round(boundingBox.x + config.cornerRadius.topLeft), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width - config.cornerRadius.topLeft - config.cornerRadius.topRight), (int)config.top.width, clayToRaylibColor(config.top.color)); + } + // Bottom border + if (config.bottom.width > 0) { + raylib::drawRectangle((int)$$round(boundingBox.x + config.cornerRadius.bottomLeft), (int)$$round(boundingBox.y + boundingBox.height - config.bottom.width), (int)$$round(boundingBox.width - config.cornerRadius.bottomLeft - config.cornerRadius.bottomRight), (int)config.bottom.width, clayToRaylibColor(config.bottom.color)); + } + if (config.cornerRadius.topLeft > 0) { + raylib::drawRing({ $$round(boundingBox.x + config.cornerRadius.topLeft), $$round(boundingBox.y + config.cornerRadius.topLeft) }, $$round(config.cornerRadius.topLeft - config.top.width), config.cornerRadius.topLeft, 180, 270, 10, clayToRaylibColor(config.top.color)); + } + if (config.cornerRadius.topRight > 0) { + raylib::drawRing({ $$round(boundingBox.x + boundingBox.width - config.cornerRadius.topRight), $$round(boundingBox.y + config.cornerRadius.topRight) }, $$round(config.cornerRadius.topRight - config.top.width), config.cornerRadius.topRight, 270, 360, 10, clayToRaylibColor(config.top.color)); + } + if (config.cornerRadius.bottomLeft > 0) { + raylib::drawRing({ $$round(boundingBox.x + config.cornerRadius.bottomLeft), $$round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomLeft) }, $$round(config.cornerRadius.bottomLeft - config.top.width), config.cornerRadius.bottomLeft, 90, 180, 10, clayToRaylibColor(config.bottom.color)); + } + if (config.cornerRadius.bottomRight > 0) { + raylib::drawRing({ $$round(boundingBox.x + boundingBox.width - config.cornerRadius.bottomRight), $$round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomRight) }, $$round(config.cornerRadius.bottomRight - config.bottom.width), config.cornerRadius.bottomRight, 0.1, 90, 10, clayToRaylibColor(config.bottom.color)); + } + break; + } + case RenderCommandType.CUSTOM: { + CustomLayoutElement *customElement = (CustomLayoutElement *)renderCommand.config.customElementConfig.customData; + if (!customElement) continue; + switch (customElement.type) { + case CustomLayoutElementType.MODEL_3D: { + ClayBoundingBox rootBox = renderCommands.data[0].boundingBox; + float scaleValue = $$min($$min(1f, 768f / rootBox.height) * $$max(1f, rootBox.width / 1024f), 1.5f); + Ray positionRay = getScreenToWorldPointWithZDistance({ renderCommand.boundingBox.x + renderCommand.boundingBox.width / 2, renderCommand.boundingBox.y + (renderCommand.boundingBox.height / 2) + 20 }, raylibCamera, (int)$$round(rootBox.width), (int)$$round(rootBox.height), 140); + raylib::beginMode3D(raylibCamera); + raylib::drawModel(customElement.element.model.model, positionRay.position, customElement.element.model.scale * scaleValue, raylib::WHITE); // Draw 3d model with texture + raylib::endMode3D(); + break; + } + default: break; + } + break; + } + default: { + // std::io::printfn("ERROR: unhandled render command."); TODO: maybe make a workout for this + } + } + } +} \ No newline at end of file diff --git a/bindings/c3/source/clay.c3 b/bindings/c3/source/clay.c3 new file mode 100644 index 0000000..0ec1686 --- /dev/null +++ b/bindings/c3/source/clay.c3 @@ -0,0 +1,478 @@ +module clay; + +import clay::carray; + +macro @clay(...; @body()) @builtin +{ + clay::openElement(); + $for (var $i = 0; $i < $vacount; $i++) + $vaexpr[$i]; // If you get an error here consisder the @body[...]() macros + $endfor + clay::elementPostConfiguration(); + @body(); + clay::closeElement(); +} + +macro text(String text, TextElementConfig *config) { clay::openTextElement({text.len, text}, config); } + +<*Provides you conditional calls (eg #booleanCondition ? #doA : 0) within the condifuration of @clay(...)*> +macro @bodyIf(#condition, #ifRes) { if (#condition) { #ifRes; } } + +<*Provides you conditional calls (eg #booleanCondition ? #doA : #doB) within the condifuration of @clay(...)*> +macro @bodyIfElse(#condition, #ifRes, #elseRes) { if (#condition) { #ifRes; } else { #elseRes; } } + +<*attaches a RectangleElementConfig to the clay element when called within @clay(...)*> +macro rectangle(RectangleElementConfig config) { clay::attachElementConfig({ .rectangleElementConfig = clay::storeRectangleElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_RECTANGLE ); } + +<*attaches a LayoutConfig to the clay element when called within @clay(...)*> +macro layout(LayoutConfig config) { clay::attachLayoutConfig( clay::storeLayoutConfig(config) ); } + +<*attaches a LayoutConfig to the clay element when called within @clay(...)*> +macro scroll(ScrollElementConfig config) { clay::attachElementConfig({ .scrollElementConfig = clay::storeScrollElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER ); } + +<*attaches a FloatingElementConfig to the clay element when called within @clay(...)*> +macro floating(FloatingElementConfig config) { clay::attachElementConfig({ .floatingElementConfig = clay::storeFloatingElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER ); } + +<*attaches a BorderElementConfig to the clay element when called within @clay(...)*> +macro @borderRadiusUni(uint #width, ClayColor #color, float #cornerRadius = 0) { clay::attachElementConfig({ .borderElementConfig = clay::storeBorderElementConfig({ .left = { #width, #color }, .right = { #width, #color }, .top = { #width, #color }, .bottom = { #width, #color }, .#cornerRadius = {#cornerRadius, #cornerRadius, #cornerRadius, #cornerRadius}})}, clay::ELEMENT_CONFIG_TYPE_BORDER_CONTAINER); } + +macro id(String idString) { clay::attachId(clay::hashString({idString.len, idString}, 0, 0)); } + +macro TextElementConfig* textConfig(TextElementConfig config) { return clay::storeTextElementConfig(config); } + +macro SizingAxis sizingFit(float min = 0, float max = float.max) { return { .size.minMax = {min, max}, .type = SizingType.FIT }; } + +macro SizingAxis sizingGrow() { return { .size.minMax = {0, 0}, .type = SizingType.GROW }; } + +macro SizingAxis sizingFixed(float pixels) { return { .size.minMax = {pixels, pixels}, .type = SizingType.FIXED }; } + +macro SizingAxis sizingPercent(float percent) { return { .size.percent = percent, .type = SizingType.PERCENT }; } + +macro Padding paddingUni(ushort uniform) { return {uniform, uniform, uniform, uniform}; } + +macro Padding padding(ushort horizontal, ushort vertical) { return {horizontal, horizontal, vertical, vertical}; } + +macro CornerRadius cornerRadiusUni(float uniform) { return {uniform, uniform, uniform, uniform}; } + +macro SizingAxis sizingFitCT(float $min = 0, float $max = float.max) { return { .size.minMax = {$min, $max}, .type = SizingType.FIT }; } + +macro SizingAxis sizingFixedCT(float $pixels) { return { .size.minMax = {$pixels, $pixels}, .type = SizingType.FIXED }; } + +macro SizingAxis sizingPercentCT(float $percent) { return { .size.percent = $percent, .type = SizingType.PERCENT }; } + +macro Padding paddingCT(ushort $a, ushort $b, ushort $c, ushort $d) { return { $a, $b, $c, $d }; } + +macro CornerRadius @cornerRadiusUniCT(float #uniform) { return {#uniform, #uniform, #uniform, #uniform}; } + +struct ClayString +{ + int length; + char *chars; +} +def ClayStringArray = carray::Array() @private; + +struct Arena +{ + uint128 nextAllocation; + uint128 capacity; + char *memory; +} + +struct Dimensions +{ + float width, height; +} + +struct ClayVector2 +{ + float x, y; +} + +struct ClayColor +{ + float r, g, b, a; +} + + +struct ElementId +{ + uint id; + uint offset; + uint baseId; + ClayString stringId; +} + +struct CornerRadius +{ + float topLeft; + float topRight; + float bottomLeft; + float bottomRight; +} +distinct ElementConfigType @private = char; +const ElementConfigType ELEMENT_CONFIG_TYPE_NONE @private = 0; +const ElementConfigType ELEMENT_CONFIG_TYPE_RECTANGLE @private = 1; +const ElementConfigType ELEMENT_CONFIG_TYPE_BORDER_CONTAINER @private = 2; +const ElementConfigType ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER @private = 4; +const ElementConfigType ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER @private = 8; +const ElementConfigType ELEMENT_CONFIG_TYPE_IMAGE @private = 16; +const ElementConfigType ELEMENT_CONFIG_TYPE_TEXT @private = 32; +const ElementConfigType ELEMENT_CONFIG_TYPE_CUSTOM @private = 64; + +enum LayoutDirection : char @export +{ + LEFT_TO_RIGHT, + TOP_TO_BOTTOM, +} + +enum AlignX : char @export +{ + LEFT, + RIGHT, + CENTER, +} + +enum AlignY : char @export +{ + TOP, + BOTTOM, + CENTER, +} + +enum SizingType : char @export +{ + FIT, + GROW, + PERCENT, + FIXED, +} + +struct ChildAlignment +{ + AlignX x; + AlignY y; +} + +struct SizingMinMax +{ + float min; + float max; +} + +struct SizingAxis +{ + union size + { + SizingMinMax minMax; + float percent; + } + SizingType type; +} + +struct Sizing +{ + SizingAxis width; + SizingAxis height; +} + +struct Padding +{ + ushort left; + ushort right; + ushort top; + ushort bottom; +} + +struct LayoutConfig +{ + Sizing sizing; + Padding padding; + ushort childGap; + ChildAlignment childAlignment; + LayoutDirection layoutDirection; +} + +struct RectangleElementConfig +{ + ClayColor color; + CornerRadius cornerRadius; + // #ifdef CLAY_EXTEND_CONFIG_RECTANGLE + // CLAY_EXTEND_CONFIG_RECTANGLE + // #endif +} + +enum WrapMode @export +{ + WORDS, + NEWLINES, + NONE, +} + +struct TextElementConfig +{ + ClayColor textColor; + ushort fontId; + ushort fontSize; + ushort letterSpacing; + ushort lineHeight; + WrapMode wrapMode; + // #ifdef CLAY_EXTEND_CONFIG_TEXT + // CLAY_EXTEND_CONFIG_TEXT + // #endif +} + +struct ImageElementConfig +{ + void *imageData; + Dimensions sourceDimensions; + // #ifdef CLAY_EXTEND_CONFIG_IMAGE + // CLAY_EXTEND_CONFIG_IMAGE + // #endif +} + +enum AttachPoint : char @export +{ + LEFT_TOP, + LEFT_CENTER, + LEFT_BOTTOM, + CENTER_TOP, + CENTER_CENTER, + CENTER_BOTTOM, + RIGHT_TOP, + RIGHT_CENTER, + RIGHT_BOTTOM, +} + +struct FloatingAttachPoints +{ + AttachPoint element; + AttachPoint parent; +} + +enum PointerCaptureMode @export +{ + CAPTURE, + // MODE_PASSTHROUGH, + PARENT, +} + +struct FloatingElementConfig +{ + ClayVector2 offset; + Dimensions expand; + ushort zIndex; + uint parentId; + FloatingAttachPoints attachment; + PointerCaptureMode pointerCaptureMode; +} + + +struct CustomElementConfig +{ + void *customData; +} + +struct ScrollElementConfig +{ + bool horizontal; + bool vertical; +} + +// Border +struct Border +{ + uint width; + ClayColor color; +} + +struct BorderElementConfig +{ + Border left; + Border right; + Border top; + Border bottom; + Border betweenChildren; + CornerRadius cornerRadius; +} + +union ElementConfigUnion +{ + RectangleElementConfig *rectangleElementConfig; + TextElementConfig *textElementConfig; + ImageElementConfig *imageElementConfig; + FloatingElementConfig *floatingElementConfig; + CustomElementConfig *customElementConfig; + ScrollElementConfig *scrollElementConfig; + BorderElementConfig *borderElementConfig; +} + +struct ElementConfig +{ + ElementConfigType type; + ElementConfigUnion config; +} + +struct ClayBoundingBox +{ + float x, y, width, height; +} + +enum RenderCommandType : char @export +{ + NONE, + RECTANGLE, + BORDER, + TEXT, + IMAGE, + SCISSOR_START, + SCISSOR_END, + CUSTOM, +} + +struct RenderCommand +{ + ClayBoundingBox boundingBox; + ElementConfigUnion config; + ClayString text; + uint id; + RenderCommandType commandType; +} +def RenderCommandArray = carray::Array(); + +struct ScrollContainerData +{ + // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. + // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. + ClayVector2 *scrollPosition; + Dimensions scrollContainerDimensions; + Dimensions contentDimensions; + ScrollElementConfig config; + // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. + bool found; +} + +struct ElementData +{ + ClayBoundingBox boundingBox; + // Indicates whether an actual Element matched the provided ID or if the default struct was returned. + bool found; +} + +enum PointerState @export +{ + PRESSED_THIS_FRAME, + PRESSED, + RELEASED_THIS_FRAME, + RELEASED, +} + +struct PointerData +{ + ClayVector2 position; + PointerState state; +} + +def OnHoverEvent = fn void(ElementId elementId, PointerData pointerData, iptr userData); +def MeasureTextFunc = fn Dimensions(ClayString *text, TextElementConfig *config); +def QueryScrollOffsetFunc = fn ClayVector2(uint elementId); + +enum ErrorType @export +{ + TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED, + ARENA_CAPACITY_EXCEEDED, + ELEMENTS_CAPACITY_EXCEEDED, + TEXT_MEASUREMENT_CAPACITY_EXCEEDED, + DUPLICATE_ID, + FLOATING_CONTAINER_PARENT_NOT_FOUND, + INTERNAL_ERROR, +} + +struct ErrorData +{ + ErrorType errorType; + ClayString errorText; + uint128 userData; +} + +def ErrorHandleFunc = fn void(ErrorData errorText); + +struct ErrorHandler +{ + ErrorHandleFunc errorHandler; + uint128 userData; +} + +distinct Context = void; // I'm not doing all that again + +// ===================== +// ===== FUNCTIONS ===== +// ===================== + +// ===== Public Clay API C3 Functions (String replacement) ===== // TODO @export and @wasm can be ignored rn + // I haven't been able to get c3c and c compilations to work together for wasm32 or a c3 library +fn ElementId getElementIdWithIndex(String idString, uint index) @export @inline +{ return __getElementIdWithIndex({idString.len, idString}, (uint)index); } +fn ElementId getElementId(String idString) @export @inline +{ return __getElementId({idString.len, idString}); } + +// ===== Public Clay API Functions ===== +extern fn uint minMemorySize() @extern("Clay_MinMemorySize") @wasm @export; +extern fn Arena createArena(uint capacity, void* offset) @extern("Clay_CreateArenaWithCapacityAndMemory") @wasm @export; +extern fn void setPointerState(ClayVector2 position, bool pointerDown) @extern("Clay_SetPointerState") @export; +extern fn Context* initialize(Arena arena, Dimensions layoutDimensions, ErrorHandler errorHandler) @extern("Clay_Initialize") @wasm @export; +extern fn Context* getCurrentContext() @extern("Clay_GetCurrentContext") @export; +extern fn void setCurrentContext(Context* context) @extern("Clay_SetCurrentContext") @export; +extern fn void updateScrollContainer(bool enableDragScrolling, ClayVector2 scrollDelta, float deltaTime) @extern("Clay_UpdateScrollContainers") @export; +extern fn void setLayoutDimensions (Dimensions dimensions) @extern("Clay_SetLayoutDimensions") @export; +extern fn ElementData getElementData(ElementId id) @extern("Clay_GetElementData") @export; +extern fn bool hovered() @extern("Clay_Hovered") @export; +extern fn void onHover(OnHoverEvent onHover, iptr userData) @extern("Clay_OnHover") @export; +extern fn bool pointerOver(ElementId elementId) @extern("Clay_PointerOver") @wasm @export; +extern fn ScrollContainerData getScrollContainerData(ElementId id) @extern("Clay_GetScrollContainerData") @export; +extern fn void setMeasureTextFunction(MeasureTextFunc measureText) @extern("Clay_SetMeasureTextFunction") @export; +extern fn void setQueryScrollOffsetFunction(QueryScrollOffsetFunc queryScrollOffset) @extern("Clay_SetQueryScrollOffsetFunction") @export; +extern fn RenderCommand * RenderCommandArray.get(RenderCommandArray* array, int index) @extern("Clay_RenderCommandArray_Get") @export; +extern fn void setDebugModeEnabled(bool enabled) @extern("Clay_SetDebugModeEnabled") @export; +extern fn bool isDebugModeEnabled() @extern("Clay_IsDebugModeEnabled") @export; +extern fn void setCullingEnabled(bool enabled) @extern("Clay_SetCullingEnabled") @export; +extern fn int getMaxMeasuredTextCachedWordCount() @extern("Clay_GetMaxElementCount") @export; +extern fn void setMaxElementCount(int maxElementCount) @extern("Clay_SetMaxElementCount") @export; +extern fn int getMaxElementCount() @extern("Clay_GetMaxMeasureTextCacheWordCount") @export; +extern fn void setMaxMeasureTextCacheWordCount(int maxMeasureTextCacheWordCount) @extern("Clay_SetMaxMeasureTextCacheWordCount") @export; +extern fn void resetMeasureTextCache() @extern("Clay_ResetMeasureTextCache") @export; +extern fn void beginLayout() @extern("Clay_BeginLayout") @export; +extern fn RenderCommandArray endLayout() @extern("Clay_EndLayout") @export; + +// ===== (NEW) Internal Clay API Functions (String replacement) ===== +extern fn ElementId __getElementIdWithIndex(ClayString idString, uint index) @extern("Clay_GetElementIdWithIndex") @export @private; +extern fn ElementId __getElementId(ClayString idString) @extern("Clay_GetElementId") @export @private; + +// ===== Internal Clay API Functions ===== +extern fn void openElement() @extern ("Clay__OpenElement") @export @private; +extern fn void closeElement() @extern("Clay__CloseElement") @export @private; +extern fn void openTextElement(ClayString text, TextElementConfig *textConfig) @extern("Clay__OpenTextElement") @export @private; +extern fn void elementPostConfiguration() @extern("Clay__ElementPostConfiguration") @export @private; +extern fn LayoutConfig * storeLayoutConfig(LayoutConfig config) @extern("Clay__StoreLayoutConfig") @export @private; +extern fn void attachId(ElementId id) @extern("Clay__AttachId") @export @private; +extern fn void attachLayoutConfig(LayoutConfig *config) @extern("Clay__AttachLayoutConfig") @export @private; +extern fn void attachElementConfig(ElementConfigUnion config, ElementConfigType type) @extern("Clay__AttachElementConfig") @export @private; +extern fn RectangleElementConfig * storeRectangleElementConfig(RectangleElementConfig config) @extern("Clay__StoreRectangleElementConfig") @export @private; +extern fn TextElementConfig * storeTextElementConfig(TextElementConfig config) @extern("Clay__StoreTextElementConfig") @export @private; +extern fn ImageElementConfig * storeImageElementConfig(ImageElementConfig config) @extern("Clay__StoreImageElementConfig") @export @private; +extern fn FloatingElementConfig * storeFloatingElementConfig(FloatingElementConfig config) @extern("Clay__StoreFloatingElementConfig") @export @private; +extern fn CustomElementConfig * storeCustomElementConfig(CustomElementConfig config) @extern("Clay__StoreCustomElementConfig") @export @private; +extern fn ScrollElementConfig * storeScrollElementConfig(ScrollElementConfig config) @extern("Clay__StoreScrollElementConfig") @export @private; +extern fn BorderElementConfig * storeBorderElementConfig(BorderElementConfig config) @extern("Clay__StoreBorderElementConfig") @export @private; +extern fn ElementId hashString(ClayString key, uint offset, uint seed) @extern("Clay__HashString") @export @private; +extern fn uint getParentElementId() @extern("Clay__GetParentElementId") @export @private; + +// ========================================================================== +// ===== An internal module for wrapping Struct Array's defined in Clay ===== +// ========================================================================== +module clay::carray(); + +struct Array { + int capacity; + int length; + ElementType *data; +} \ No newline at end of file