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 1/6] 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 zcmb@v34EkQ(Let5^CX#MGMQvDSFV}K+>>MOOeT~2J~!89&+J9^U>8_mmj!oOSh-X{ z!~;A)5Ks{C#sg7b#1lkc6|YxO@kU=25jhqRHZ%Wkbw6`tH_Q5det+1R$x}~vb#--h zbyame{YXrbq$+$BNz&Nx$mrU0CVnJIKmN8PvDUG<*@egEW-pecpZrUbW?vdxSU>nb zEjKPn%=NS+>0X^(2*3)=v&RSSEudZ(rZer zs6U5m&%X07KJw&0KHVfqw>3yoMQs0tyOzH3`kyb6r1y?XQpxS-FC96cEJ;7XbpYiX zFIYN%&z7IO{fHzz@RB4czdCT?p^J4F-Exs6ec?xv)bPxKi}oDYSMrXx;{HESULz?I z>dB4r!&13qk(`or$dxd(#ho#;!Q}QjW9@$CE5YAhOFQ6hr>n#|<#w~rx!vg*_8~=< z(@Sy@`=VS_oZg8mxBMozd-?5ddyy<}R9MmS$BW(gkv9|<$&xJ9qOH5pmO(P3B^GCH zC8YS1dPSoolTK?btGs|!9J)&}SgI9kiNRQ1a;G-HdPDNV%fswmP5WT7lo{GOVl= zusv6{Ta?Xo$Y0F0rcp-OY`!1L(Ol_d9&=HL3iT;~N*bj}IxO9lqw!#_P6zHXsTlL* z&5%hWNu<-9`#<4K#tl|h%>K&Wll~H`Pyd;Xrx((%^?j!{eVzR9wjXT&#kO0wvQ+8v zuYj9l;c{3{^Ga!Aci>l%c48bZ<=E{s(+(M&F^k1)D$ykDve~4U-Trv2y*<(9^SRv# z^-s5ZC-V$#4fl0~NBxeT4asjl+>~-SyPF>RoIPCM-Ept{a9}o-nyWNv<7=X^>9+cP zHDRZt)w-{yxjq;{pCXx;-#+J{>|;q{tj!qb#_VHw)B?SsjGIjJCg1xo8ROe+|r)f zHt4Q)SvS?U)}r^kJ>Gk}=)H6&A0+`N=@;;*^bQqXPV}kp5@qDgBrTB)pgAl)g7VQT z#6`0};A)j_C}@MERKR`I26PQ^{$X%lCt*a0?^%X}3k zTsfw~{Q?f^qe>;01896sje@nnAxR4fr&&1-O;M)%^=Zgr(Z$8|6RQ#@Xpg zQKFX<#cInXB_w}T3iB6y0l!2kJukI0GqVC}x=ihD(MR&VWiQCjWP1zv^V!}CxTKMn zo>iO7*IqgknsqF(w znnFNh(|IUKm4HSC6w;F~3+YL5Jzh|QWJlC!U6aMLy@>QJK}p}fJ;$MXC0iQ$mSf7- zY2bQQU<#g-#WYd?jU3BD>x)R&in3we>Ll+e=~@9DsLhq-x>i8l1$2{a3#d=+X-o!p zb5PKyj(#Br14DTzX<&h&KiBg-ub_boS|bfC%68|=7KK!(PXUzA%Cn^RXInkxZa^9m zccH!WltOz;AIr)Q#)>qtXsH7@g{}-hYLu+p#0?5$hRe5T%q3M9<^qLOSJz;exoI(Sz+t$2gcQU!_Eo%~c*4B;M`v#ruTSk0=v2AVs(RO>~ z&sn=Hv&i~`+T_k_)=XW!)NXHgPuP4W-|&|9wr!&UYje_J(w|puHkOSXTpI)zI2Bw@ z$G9ZLq+5AE1)L;Rz@sbRBw+&HD&3-%ub|%K;KR~e3-FK}i1NM_cxc1~yqoZ-ocutZ zlS_uU!HExXQ*PP6{NMX~*!PH&oSRL=$s!3tt_VA@5^EtW(1k?YiZ#&e_B%DK07L5H z5PQ4l1MhaE&kC_Gso2=h_Gz{Fmu_8wm+Kpg3|c_r4Cjqex+W(BAK7y*qP&qHbC1TF>oliPz2OdfS1NsKz#z5iBXSpP<=sJvUo+=z%zMzX`TycKT&o{-v$dXkkp8> z-J)!!V?}GD3SsC+FFwR)swmq5w}MGA%(^*@MV**+{-j;uwu?F{uxps)>P<9P4x8PL zjn$rZ`;~0derL~uw%QeL7_)m!8fT}MU5X()7MbsMQA->rmj@W&&@ipiXYebu!k6>1 zeAo}SfuxZo*~2C5!gnrzWUv^k7<$^vD~z^k9C-7A`#mhxB}os8(^I1zuWy zUAkVXP+LYDD!fD)j;&Vu95%<=dFdEYlq;A zX5viVy}ztn|L4KqmY0{w52u$Hy92J6WAWW%-t--8d(+U3OX)wTJpeZJ0KO(hPcMBD zzk=5fp%v&8w8Au@q6AG|mb4e4g(ddMs-lu&Mc%(Fy`{WZQ!F#k_pP$puA!;5t$)E_ z2J}7aySz1J>ECevWnL~mhmQ>glEy}<&FPYd_}FkbEdF~7;53T`JSyER;E)SYm4gpw z;b7!Le9nsUzAPL%sj$4B;1bLHHq#|TRwSDwZS=?BAyW^Pf*zVmN{aoRozzeCPT}2D zO5hBK);&7g-6j7QI_p@oxH~EO>KyD%wpOMeZJTM~Jnd5c&c~lq!_LX}7UN$?jY+zx zptrQD3hFxuPGbNnX$;`qkNC;4(B+gfauWf%!g;{F`fL~NzK)jqWb z(O_SNJdLZoD$z$8%@omQ@v%m56y>55! z`gnYOpUc&^UfaHO&CJX-OYQADubG~{W@r03zL9Oog)Sk-nOzi(8ALop53Me03psi88P`-)mYiL}~YmUc98j{R)SJ zs%~H-drfd9f8VE4oxhJ>$&cmm@?PcY(W)cb3#hBOz(?zjfcLB3mw<;Px0EcD1dG*T z;VVTPYlJ3wCc&unS5;Vb_JDC~r9I-*gh#w2E~gWd;vz+FK21V{+>jWGa>&tyxf0AA~3F;2Ci#91iq8p zd@w&A+)5JTv|_vq#(~3CdCQMer8>TTC3~M{ZSKC3pRL*Ze72Ilg@N=XXm%6n_N6R0 zav>};qd*UqTWDN%cq|6Mu!Eof+&GJ3Fz=nf({UUd<7BE zfhtipwxX<_mldtiswv72s(5Eslr=4pT|pLm3Ro6VPVfX8|RPKtQ8wb7hNYZW7d|0E(IGlh*Q?8AG%Qeb3by$=BgFfxy%{ zFECZCC_7wGmR4|4)~7;aE6VEgP;QqBS^!0#P=i}{FU#MOe=c>c7zK(b`$gn8X+?0^ zhp4aOHu5;I6=7fKd5e`4J4WO0{>fg`Q0-`ATTg9gERw3TwNC|Ojk{VRYp_D^XKc;e zxAk5eFYenIvb4GqWmX2LlPfcCDs{?Jjp~-BRsT$HfB7*S0-)66UPhPig-|{EPg3XptD9#|Fw4&Sx zneJ;;l*JK({eh?eiQ?t?Jf!_S#9X;e>V|Zn9gMAjj;PRz6;NTny5U z2-Y%bQ|2}K9r9PnXTzH9gTGle`Gcsn~&_BxUyvI*erV>y*WM~ ziOk2@ed)Q`V`HG2NHj6%G=(Y{tH4F8y&GGPc^G) z>-DpbEe{I}%J+f61+AcP3{XJ8V8nu@=`F%v2#HvgSAKWs+7fN4MzK{Iv_)@TI;ySI zY3hoLi;Buh-+J-yroLSEIiR1cYH9W9SfbNW&brgzt_g?hH1!UX(O$P(I!pZ(Q0kX}y60Ad`UDi364d0%>I=#e2Ly(|XdVO2Isxr3XpLr!fOZ!^ z$y*c9j#=t^H~PL+^!-qRS)H^K;dh$5*}E#!m$TQ?hpO29v$DmoZ)W*#1|B)&HF&@O zQP6JzCG9DoQBJjhQojXM^spN}M4LIN=pm11E4LsHNHJI+#jO!v;Zbdx2Qsn*K<{pR z{|NM!aJxDdiyL;g>+35JrWppGcV}K!YIx-A1(ee;g|%_{=|t5=PBXzVR-Xs=GLS9S z&HYgUjon31NFwXuYn^~bIrQIvc5<6XKwIymvha^sm}@eEYLtP@erhiR>13nak`bld z90!N;sJMW72r8{d&EFN2^$BPuCjE}{NR-ucXan#T74#$Ue!|cJ46k$DE6VnB+D`7> zAgAFaVCYjwHbmKODl3&`I;214TEh-j=5mSp@eE3jb4?{mcVLs6Tg2>Xi~(PtLo(vx ze4m}K)A^XVt);0JA@O`X+>fXl!r{M3*HSPXt&&ALOFU%?=U%Jy1!@((z{^El`Pej5 zt-YO0E32Pxp&;`HVM~#I0K zTqrabZ<`MhrMzWDj~>9*l1k)P`EI13=fxZ}lZPrF%R&FHLSvvkTNb;pLxgAb8qXBq z`BDzgKMJ7t=b+E#q4M)N=<=%26FKN+pM4d>t3Ox+4dDelS}$n&+SbO_}3sQLgL@DuqNzO%_VL%9YKJ!7JfB)SN3@ zmTyg7&Oyub7~~)1pv5Y*LmtkxrctV;-xSb>yeQE-MapAVUQwYwf+ADkRqT)bElZ>P z_go!@yIgLSEs)!O(g7RE(j{losYy`uSjZ`iUP@naXK%t?&^UQySs280)5xLSrYMuQ|9hg*HF@1Qnz<+Rk174+cd>*oi9A$MvUud&cqfv=a1)waazWVU0u z;aE*LP_sF*kVq^<#5XLoK<3}r#d{jQODG#b$%Rb(p(3muMdY2I+_d8FgH@_wj*$a{ zzQOgO*leOaCYyb!*|z4c-LoCGz60aU!-2qXGymRP@t)*O)d!n2<4333&zfv*oliPK zQ$1l{)r>y1_S`Eb)?YnwR%j*~o(=`4Bhi@#TsEO+#jB^pRwb~aya+Z$rJ}t`^;g?Ij?3Qa`S*cPlf)E;w6ZsFPDjwD|e{3u; zwyQI~X~4}`dcn%h<2t2%(id8ngidLz8JX6O9GU5bHt-K`OSZ1%x}s^eGdbT{=bEVT zn6o@~b2*MtALLiF^8?h->L;L?0w}Fo0=le1%L~e4CP0T`rChbfi5zr}uTBEbKQ9%O z#DFJ@J-43aYPbt(JjX3(nmgTmJrj8Tbz#0ow4Mp*^E^L^R=I+HD%w}_WobPVWnU<0 zjb^HV9#^46Uu_ne8|@W%w90V|N*SktL-{Hvpym12_$nu$#d)Z5dk#bC zs!$CKXwo4aL>cV99@VRE=lU_bn*t4zU8i7tX-+3n*R^-GZD_OfbXyF1Ls>_eGq~qJ z5wgd-0>S>6#qH{DG+MEvQe`td@*m0_UwK8ZSxU@;mt>c0m3-(2!iPx8b|%fL6-#mc z>|#m_5G+~?Etc$4*7W--i@cpKlKq7alKtA@Y46(0F+q0f_(}-bZ%o)p_UD2^_J=R! zGXva%8fI@p=AenZ)J5GjBajBY3=+raUT3tc`_ zd$%n)=_@Zzu5FpRWO(S3342GxSEE?=dPjG6Mb|X9OhqG8EiF@jaW~9WS}Q!GZF@A1 zlt*Z;%KAo6Rq17EW8cz{n{|ce+F%)n=dlWM`#6vx8fkt{@mZgl60?DzGy?^6c~$6% zJXG;y%PP78D9s#!;dma(C7eT*V*K!oD!CF`9KI9f!60nH4GT&G@%c1lOA+tK_f~5= z=bF3c0()y>UVF@Ij#t#lPp7}Cb9Z^eXHBHP&QigydfDg-)}_Bmvq6V1!mCJ)*%<0+ zvFn)bN2@k4k44}!r)>the>`KMR?yS6#6iUvAr6jH+sfTJXlppY%f?oe)nnsKmPS$U zM~VVED+LD>+6N`!!@|3HZ^G&EHsT{!EWapU<#O0O^a0b{!vU0ne!LrvcaT(H8J!75*#0Ed}rsD*Rl)SsuTl%^>Cv z{7(SxR7i&kJTIu_e+0No`Bo18V&)2Nr^i9vSGgS|${!cyrBQg+;0MaXtEAQRJ%s%P z{{DWxRr!?+=s<5Rfd7(fSdK+0%N#4{EqM|GUY^6=#r~JFSvYnLJBI-S@c$iDmT=h= z<%?D7{{qkZ>p3d@GN%%egzC)ZTsyT~e~EgG+Lz?aAHkI$V^qs&x014QC@qIVz{Y~n zE%W>1b$f&J)9M&MI*Y^sS-1S__)M!Znjh=TAAs#)jCJ`+T&28zXuhx>`hwCdz()=| z!Ww+y-4u%>nGEsy!1JLG`;$C_E$&aUdy8McA?@7z`ePG}-E-jI5VWV48~%4F{d3~* zct%h4z)8eTFdkKK5P%K4c_uD>DmH}V(7`x18t*E8{nB)5>0jR%dF6v=GZI^TSXDUv zbx7?baD0|i!KHR`FExc$5IaZYUE$W`=1CN}H~s(Od>@ zN#z}~+K`=wzB*jL^L%M9^GxrWOuvb`L4`0Ym5s86^4)1)StHyet3RpwOG2&j^-SI9*aJW@lr78EvI_kTo{rjvTPeasW+E){DHbgw&_W@m9#A~s42a62WrpD^X z&WZG+ED=mKK=0M3A7ja;l-=lr3j8)Wf|*3LfTu^OkH`)BDSopWw?@^tpz;5paVz$( zs&LVo(0SzNlSK;)k|N9*c#(1k#f0R$?go?{ZQ?QoXeF0WQFfU}qe;>#xrfD}N>za_ zC4C^u9#_kfE+ubFlqFrtXx3rBs)l0dm~+_6!w~pa=^o}`8Ba6YTcJ-)`T436_0g&l z#AKo*Gg)_qxiQfg*}+$qP^Y@Gd>k{A`ih)L50@Tl9nvEsCd%P6E2uK@SO@0}*AaqN zHqA!?w4w*xzlDBC7A$w5I!iPtWn28IPNuGUd-ocWgDwzEv%b5taM6=1*48!G(pq>w zsN@#N%W@xDs65Gs=9vzplKkz$y0MlZ;?_2O>FDUC(`)E^O?a}IvF6Eec#3|fR98~i z6mZljzQ(Nx-aoQu`?zj?n;7|=joQc88v)IG0)2es1vKkB5T#^s2@ygM3-| z2c2BU<=f=`fvA!95Bj)&AfS2wpid$HKtS{UK|x;%{DVHewu!QN|DaEKn~K3F1BzKz zEOkm>hAarK6nkutTxsbufD9ZB;Z?XjW=o9x=%aqC5o(cbp|J+Wp$p5ClFIuAp$Gt{=f;^;Ar<4 zYE@TC%)QlSAgA#+pwWrHjFbyNS!u8$)fm$1GH~7?dvs{|4Tx|@WK%R8n_Y_5vNkNbl2{z{Ae^$ji0umc2mGE?leuYaj~Y(# zwr?A^Hd;qF##UF8ja`AJ&PHShBiIsb@pd?BLIX|lg$O)OtJm!446s0F`2c#WOtDqh;)mk&Pw{1mWqEw>iE~Tb>y48XRTyI-9;e z>b1xf9}OT!tfc5bvBvC;+S0#;>|<4D*K^tbl#u;bRm+B5&sQ=5UFOhOMh_@ydk$5q zu147md0qt=6zzkAA&KX03d`!LEN|_-T)PPj<%Bo$9IVyCvZkxZvcSs29^~_bb}d9g z9OX6nAw>LNF2b9A5*Jxm*?%De5u z3p-@wyw$cXrl+sfI$g;c({RGy8+GXhboDkzmA84OKdogij-K0v&6oPNW_^(^)VHRk z=d2EP$F^;tkDQis<(si`V270AFBIgue(%rNjC}L*`Rrr6cheeJSPDMqVU(e52`byk zbYG?7zgk@URkjMokZL@r7%>9h;3NC`!QBMSlv8V2sA71Oc-?^6Q3$PijG$JcOxcnx zE5GpAMqW0yqO6{mZNNMz#9(@q=D|34Z-$RrtF98DnCGc+zW{qGSO}QZu@KyAOZsgQ zw`uR*YF%nZ+qQjx4X2+B4S9^NNCWF$e(?gGx7$rLag1DEzboW5k%MweS3olbP_k(S zba_?i2@Z7(IQu5@hbW}-aH2R6=xm;#h*C_r3~p7f-gJe zE67qxGEf3|oQ*fa?&oOQp z3TSa2s@$M*%%=c~7}^W4{I2ErJLEu)_q68>>CUxFd(HxvRM0N%Dhp^KjbsN2sKUnt zQ7G9DS*2Ns>@N+nyGAJSFN+bE?gtLUGB+kE;|r^k2Adh+isr6! zS^Bjj_Y~`k>q;v%*B^ay^IwjymFLoL);5_+%*|$Y+475jK|eVT-p{`Z$>Wg+fth+K zpeOQB-bVp_p#Vye2LU~vhjJMpC`DDobG;|G_llV$LqKT;avVxoAP42LC!oazm`Pp) zlm&199@H7{`4Jt z3f4yzc9fB?#}_z_U-gX)8gAmj#*Ayk6L|SN33f$agmN3g=l6AQio8ZS5?z}+Zz_9# zK@vobxp$mxXiXw}As!1%C)4j084%5uwJWn6h|9?FW>0ZWyec@scau4kYij|`>sR? zBZ`jCx(|7-hLZP2GL z)yL|Lu6m8x9t+pS7ox?bC1vaC9DZ9>e0|JVS#8?9COp-$XmnXzO-=YRvYYU%3Sxz| z&2d0G&>9i zhuLh$H@mKBEEt+-ZkfQnrZJ1Zwmx9B2I_157UF;&>iw%2w-WgeoFlNSkqP}6u7A(3 z=Q6wGBCH!Fcs`O=F7RFMRc#dmoqU3M0#C<^?~4puWXnR?7VV!Z*i-R6dn(eC?E7wf z(+U!Tj*9ZW;LN0g$Y!=Ls?%h$u<+cW-rhrVa~JpaUOYE3H#dQgcGHJ1yzs-D#P`nY zj~%<7K7@B8i24%niXAJ1RReRxpaEIG^uFEpUu{pZnW)^+FtL62@_A8lu1g%%icrIa zwU@SVvP=}u2J!3zMWaDJ_BQ9w4PKYIHte)@gen#cBTK2St!-M1D{Q~h#5S#czB(L> zS{j2juHL4$&V|0hAr-1zdvcy zRA^`a>DV1e*A;1;U9-XA^LlBNu=6GliVSmP*DL6ZlTKk^XVw0thA}> zevhZuJGj_hKQy;F@|L&<#es4jz!j z&J**k>;QA=a2~~H_36LKM;dpJEMJe6B#~K`^(aTtqCq5?K(2{=;qu;1>@@UHpXqX zcq9K-l3P0*$&jzothM=CYZ4pU+BPI=T0Qo1gU%Oex23kW?{0`VoN>E7?zBg2tf!`_ zzMhtd`ue6Cu${|E5qMuu&pjXV8}XR-ig+FML345r%hN?aQmR|x?G353I{PmiiESG6 zX|2A7g;9U6+tuUubU2+ITK1I%d&p_%-rY5JaL_ZbK2n<++tU4Mwh^fvejEbV)8y{< zLjS0G0qX`Lq*%dehu2G#VfM_hz0iG#I*2@7)!ba6W3ezl)H)LIj|79mKL1Dy6rZ=T z(eKkD8M1v{IJ~YMiIJ1_F=wo&Cl;gO#go=)>AP%^o)6im>I>weytyRi-r3>RR?Doq zx^b{GeGipw##aFQ$Tp!XU?0#J7ES_<2Im+Jl);60n+F%bDhKfEo zwyU(xX{a*R+5&Zcb6wEZ5O1j(C>tW&DfF<3ZRfFltRy0&M+x)1g(P$RwWeB4acWb^@ZegFQk06g zlMcvhFq3BM*lzq)1`A2rLq{+9&d^Znw*L1$^_7mQ zV52?S;2SQpRCZSA?X?zrX;t9v5%V#F)oO2R^mjFqOu93#vSxNR#$9Mmbwa6GTV~A) zF%T3$3hwV4^$fH$CaRq}r_s^eT-E1u#p)q!W=Dgi)^0D|KHj|;vs86xb&2-QScffa z^L07<9W|E5MsrOgdWfI^#(I-fim^@@Is27$*Xch0Wc~GJtl2RfOaBV|Yt0;I2~cN; zY_#VjGpWf2xy8ne#-H1u$x)cZQ-dNhXtI3P+wJjt6`RYMbh|p7l?^8540yUdR;SZi z)9AFC8X8RI`i63Q)YcHLD%UpE+d>TuA)CQgQ(CT%HrS%}KD)(KUvIM5KWsG^YOK`; zYXR?UQj@yssLyaFMdV-QCCL+XQjVMts(dp$jakNZyNRJraLx)2mU70`ggwN$NCP;x zeY|T+>>I>7Vj)=)cAWAwBYmtyBO^~DGYCjH(=L5ix(SdNART}-qf9{hFd$fkIV1%L z&Q$>fAroFJm}zJ0q}u_(i10G8Oc=SA?*RlH=MZsbQUmj+vA0<#-u}1%xF39zbI34i-}+K#1o10CBUsSTnqBLG%59B-qD+ zSq6k?{x~45>^7EAbbt`egxL)|%^X5B-;23r!o;4$da2O58fPZ<(_P{?Ht^J^ z^<#o!a|e@_XHQ}=-sE#5m&l}Z)z>yzO`gc-L#8&P)@U%+Ro1{Pa8ep6(S#ie=^@#Q zRrMTTrBzj8@8Ixh=}Fm&%$;*zCmPR@s#W-LIU?NvI7%W8bPj*I9QWC!xTYNs^a10? zGv5xwM8hrybchC#A3+_iHfqXC_Lr1voNC9O@4NObU8AGD*S)p#M81!NYXflE1g-^G zzGgS6rDT#PN5cHT)r}|NV$Ri&$&v*l@(cM}ZdH7Qc3!kE2 z_UOU_QC6L~Ts8=8u@Ty$n%%xDz2_}T|B0{D-ALwrh(&WNIGp)5WgBAk zX~}$9g~+TOG$f>FpsD9cpV7Wqf;B#K_SwL#!r?8yna}-%^ep=Uo6DEG<=I`kc>QOU za6x_fwP$ye{#VPR{*|0ITvIt*V-t8a{F$@lW0_AA{*6S3Jbu^A43%RR(04hfgU)+P zuo}>D8OJNG>w)(QjyD8)0+~^^C_h5abB}O^LxK#M#yLnwue|DL^6c}@JNqkZo_@y- ze_qo&_UJ_yJvv4sVM47@Q7fyh=y*R<$rP{9fvR6oi}&)%sMCsd`WlR$7cIeAN=?HyQ*8FCOHvf@dWZ%S{~OrLjBD< zItSPq*59>b?V@=^-xzP&(YdBlF|0}LXo@vL1o0d{+PqM-xe}+$!S#jw`W^gw4%hR@fGv@41g;F)Js0iL^*i~sy6)imTrOw0 zW{*gh&^iG**5F%2hXU-^?340yfZr;;4*0Fo6PeeE9>Cwn;UDDXKPY{l%YT6VUiyvv z60K(fL)EWt0$tN`G@FYm5rM=01T+BY%J`gM& z)6XojgN|zbaH+=Ns$sXztT9ygmui|?)BD#T^t-?o&{`m~UHK5=Z*z3|SN>#W3W4Ra zg;qR&%a4~&#uX&AinDx(9B2wUfeaBfDoY`XWIk90&BgB6c*9bB;pT5$ICtgNsDE@@ zJI=h)$ok6oaG+(#=jz{-a6}zSP2HZjQf{fRxA`pnm##ni-b?!YlNXGJhkJeAu65%M zS6{GLDKdF${rx`gM-ofdt-a*Gt{DlgJ2X0c*^XpwtE19YZ3+*%LTlT@XU)az?VhfW zr@wEk)dj}3#;49prOv;7*T{Km{1x@4A-hAzLM=m{>N;JW*Ob~EZh-+WV}8CYKMH;{ z(m6{@VL2Z{QW+0LY?mEMu~^*I`NLU}jO&Z!zAaUsstw_cvu!=WnN-D$G1=s3w$%Cp zl{eVll|Fy$;*pK(`ulqWQ*FViZhzU<(x#r-vzDsA(a_bItidxw&Mto})NJ(BTN3Rd z!)=YXYkT%|&tB}9)(<3``*B?VVzOu7x-Km3SbIV6*^o*j?06`vthwP-Bfu}$xZ2l8 zJN68-_4})S*2+5F?c2Hs&Pw34)%W&yEMJV?hBHSYYu|zQ3?q9FX=AJar=ICW_LLR* zONap~D}o~^th(B?SU_)QkD40N;Xo#b`>L|0*2cQ{RMb0|w5AMAq1xsSgQsEJ^n{{B z7S8d_Pt?aeTbg1ilgs+1(cz|#DsC zgVnXVW|zTUQ(AACNks>p=~t~yb+hgkuiH>((s<0%sg{xEQN?UG)p&LF+CX0@K1*8@ zk_S)X{!{)I_!Xn42u_xEf0lvtP})fkpu@VRyp6(BO|QqB5|YWCS4~Y`wF{?=U)9mQ zlu9jiy9RAuV`D>wx6_HU9;}gCuYEYtwRgI;b$V}?`m3R8+3NKBzV_|isqGzB%aqPor2osCB08nb1JDs`Vd)6z0?b~jbVIWIPx>G4-s+0}^t_|TIcJeSKU&!!cg`WQ2^ z0w{taOnP_HoS>IcP`tsNWJ>?OUQgdbB)rgv=06lpmw!nAn9ZdhVD`yLnx_}Acd>iH1)g~?`k!?&a=SjHCwYXIXv%@|@U_h+A8p?mc7%GlD<()Vy=&H>rM-jzN`NVcu{-v^iV^}x2w}Ty=L4|@2-oEwmOCeQr5P$A%j`d?QqhlhNN$@v*n-h zzQCKN-r$TkGx-;{FFnCJ(vP!_Z_3rn|NP9t!n@EfS!#iAzD^#&ZXF^cg}Y8xONkqg zpNb_UN&Yp=Rt*+7dLo<8IgBIgn?E)>dTf5?((z(ep-pcst_yZH4xZ)FXv-=TC4BC4 zWo!dZJLvWyUA27rZRz=ilI^(-x=65CKHqYL( ze9K=Co;~))FNz^aN3@vHspAgX%h+>T&B{eqQ$X+=o-}bpqgm2uW=?nB#LRv#`$)_7 z9nSQ;>paM1L*^3sL7a$2vkeQt2%RL$^Y+EA)@gZCHq9QGSpEWif8dOaxxu5HV?XgZ z{{8%!_)-GNU&~&?uDsBNesBl-Yz6$`kpkiSC#}|kElR!y^OykAwX9J(=r78@zcaRV zY+~o=&K_0ZNbEj^>n)>O$BstU#$szD(X~;02S#^}ePQe9)?f2|C&(G?I~@Vwd!swC z94fGVJ}Re>Z52Zl5V1nc(5&r=NrrNjp4Tc;ctNV?&A#{6r!~wC&{MKIhqE&h!@cQ75|_ z^Ohv+Cy+1;-cHa=nNWrC7At5${wW;DlGUcK5mWkWfsy9s(H3XItFbRFncS%_vbX-+ z>a$vWH8|bH)6?eeypx3O*D z3&KgrpcH-$kNOnm5;Jt%!*oB)zGmgh#l9_3Ik^VaGD7`U)_2c}2BJ7bogOhh21IPNMVaoqMtopJf>8i7p zvjbuNQjpb(Ng-hB4jbTDdTHgQm5)A;PFWl}lTKIMv1epxmvyqz(`5Cg8qHd*$>92~ zbqFT|dGzK;tTy1^g;f6*bHvinVQX`2Os`|Nr1!B~5@y|aOG96`H{z-q+LH_hI;za& zt}dTr!e$?_+f&KC)tVx0X{la2R#U63)KnU(2O8|ZU%&py`t>xA8z74jaEso)S1Wu- zFo~Ylgm zuCEM#ZKx3+((G6-<@YO2fjfJO170J+PZQu4mm!Jfc1~P5pFcM8>LvC7&f3Hwk?A>h zpE#y8y*Vwrmw!VzwIG{fjODcdTKGO1jP6P6taiIgmYTQiY+6p+i&k4!JGW^x_?Ld{ zvZj}XF36vW#8F`B(oJKUdxAK^ z`28ikAw(Sctxx~ulEuXjZrip|?`X5*zIaS^R$XR#j#j$s+Yz61Bu_;PioNqov2CjI#6RAr4`DXGw&VG1`+c?15l zqPED&8JsYe`r2&z26wCd3=`N;SvY^$CYrr6osGgzDJ`cptx$4|Z*XR5=&)1X;yHL- zbonol;WM9K$_BUT_4t>5eypll}}J;u6?GE7opOvzM8iCj3KAyV3b)BmIF(BmPl6EA|`=h+|Tm9eWN| zcK_P+rt_2?Cq7L43rfv6*@jN-peP~6vl!Ya$d0Ffbj~x2i{D^>O51qbugKN;y?_|| z{VSU{qqbWbW8YwZ5w-VVT`4B3@v8Jk`q}pMhr8I;?iqa2cMlG*?fobflPYC5dl`T6 z=R)~fo40oPl0hL8bv@zNG4_1A+Fs5784T(lxHMGTf3djuc(slGCb+Gd?Wwi_Ol1wW zYU*7~dMDn)`cXk2X{};>V(cgB5c>(6T3q}pdO=h*$VE7(hBUi-! zTa?@5O<+Aq6tmm#+O~5d>08;}q9^8`pu$A&Aim;qiAsmxt09~;%dWPtw-l$3+Hh=j z6?^{e^Y7|=&*X;&(ocdAS?WivXttJU!Cy>01?%S61Gevd3##D?=5b?LAQEuvTJaoDn7_Wfz2ljJzkaqgWhY*yR-L%C@ra6)ACmWdUu54|o z)Vm#})yB!bE}g?+FV{P5r3RA>!v3JEDlXBL*&Bu@HRa{S#k#V3v(lVG+9J)-OK>v$ zd+D^8Xrt>gwJYiEQld+7!`yoe$Jn#oapVgLao5lH|&~hiT-(pD55 zD16v?XFq$sC;jvrJo;TR$o@E(cAoyxZ-#x4t1t)thDbEexXZd?N#hD13{S_%7gl=x zrm)UyFxpMU^X1{Ou&>i>Glz{vySaD{XS!ZdZmOKumYV|&?018kS`F1Z^p)mjN7{)C zj38`8$}*;S;sdaesLIMMFautqc3dqQ-k( ztMk1Ja@gRmg?LUkD-|V$6$RkP_EpCv|c8hX(dk?H{Y;A1ufF zjTVNjBeHrsMQEcg5DX z=Wb1Mpsk?|w`|yLrM?thyrM4)(ysr1`cr&{{=S{}x8?uZUv^0$BP^Ul|BsBAR9Up4 z1KgV==Cn1aETSCI|AD6Z97Y=!*GQRMob+p0GpuwC)1?3X;SYbfg~6PR*E@c% z=ceBOD~_H^H!R50(?dFXI!8A&eb1$RH|21A33MPjcoH2V?}9HnwB{f%fTc-9rD>mQ zC5;<3rR7zI3PVMe&roHGnh+V$R~zt-!-6*N(;CalEal}oO;uv$v3t zr(tIx5}|GGGvX;Gqfy%sft<^dP2RzNq1-7fHJrdj`Ob0LIU4dE+HlBst^F1ni5<>^ z2b~iGGzh3OhB}{KtTh@^*&j%|X310J>QJrw868Z{NQ581NU|qx0Ur;Ck19yghzwEy>87 ze2Cqncu)^3Gu4Eq%=MOq+qO=ppU={9$a7=rK(;*Y^7EwsvJ#z@9aZ|N)Y&NY&Ri+%fm$_BoZ~xYKju3}-ho*);XDZXc`4;B zzy)WQI78*IH=XN$woXfy18Uv7b4);6T*qP$5jkWlwaH4IAx|%i>HMLPUpGcObT`Ud z**+xw z+Qts2m=$|5EAHg8BF3@j_*6K@C~UjB4n1Y|9X>r3ctk)w9W?v<#ns3nJ%j!#51_wr zqE6pqnyYWr>j?<@sy)92GEaF@o6yc}%G-*33d=}xe(vsv-nkC8Gv*!n!24N)jCW&E z4Kdqs`U0Qlbnb#AU5k<5FCW1Sz8y8xSwq_&vfAoy8Y?X>Gb&mZH^`L6m5RWOxF-U?}3i_TUO7+g|68o zZ-o3U^Vy&-q8cs^AAyC@pR+gQjhF^vLQo$bfF>D+7BcfWM4MkcClbp)pAhN#ctcZj zMNLI*RYj?`+9z-K2Jpm~si;{tSjx2}vQlBLsRf?9*n`M`*hhZMLIQyVI{o9WSY)kx zmOVI-O2u&)KT17_C(ZjZU!Zu+#)8?N)we&)iX7?3d6d1Im&Lk*eY#a;{uIg1p~#^^ zA8|6waOP*!N1ly&1`YVaY6_4^x8T&7ZHQdr$pUc_E4k`e+BL%MxKmTp=5aOaE5^H8 zVlj8e&`XJ`TAjYCRD02=HsY=e)I&`%=|*IR?v2^gsTP}H076F(B8*sr&TZ4&TFR-iSp}F_Accm=uRI_o~1V@OB~1B>GX8jJxoW{~5V{&H4C{OfQ0h+my=+EsZO@mrq=d=d0xx-Fx=92Z$D&IL>QU!EvZt?5G4<6n8LDfv!cz&D-PINKT74xv6_ZD8;ILj(N=`~skG`~|oSc+! z1Ns$2dyn&e`3w4WhV^AeZS_b=Sy^>urSQ_Y?@+5VR>4aXeF~OV0+#3x%*~It3CO-O~*~E@mgDr+v zAlu9~yUAp;$@jq~wnrKoB6eXDTWjjf!X{p$NbFK2;Gx?L6Lj16WE9~jCT)d zD-6@4t>%DHUtgxLVry!GP^qPwQlqiG3p1qDRAp;xB<(&0toIjHLaTLfi;(mqy$MyJ0%~PEDH`bH4lbL#pIQrqxq`NUDUjb-NV+OY~2N0x3IB1Eq#{-!P z*jYdQPwlK1SGTi106S~z>FumL@^)5pH0F9^J1b5xiPh|^k`l7B%uOjzYtGKn_Hi>S z=(B6%IWw!)OA@C(8-ZNJySP1}K4pUITOoT)YDBi%J8@o#su8g$!g&u5PhC5ra&=Fw(NxpRP?N{8 zC`}+-kY{k3T zXV~Ad7ahXsH6zG;Jxks%e?Yz)Z!!Hdo^gFuQ53yWr!*@=%FO>)-kZQzQJw$)=bX8@ zSqULzPY9Q=2n52-&Z=Z3Ac}&xP^$(A5S9c(K%}VPf{6Q4t4OU{#f@5O)mlYGiq=}4 zTB|m-iV)FD(Yl~@LFE45=gizCAzIqs_xJt(U$6hYug}agXJ4M@oM+CQdB!@!nrpRL z7g$$VKe6tzeqlXs{m!zjzgc^&kFA5A7*C3)Ke9E}Q|CF`bC>5K&l8^KJ+FFpdG^P6 zV*D}VV;W*kiD`~m9llN!d2fdGZcX(g+{>{7Bdnk5b?5NmNV`s;<#IA~67kfkO?XfS%{w?;s*iYkz z#GMnjIBsR!rE%BAZHn6x_jugSxL4x-9v6uFGCnrGZ+!pw!uVD3>*8;Szdine_{ZXR z#M|+2#_x|mm~cwM%?WoUJe2Tsf}OA{u}@-U;#rAzCccvR_r&)TKTSN8q}NvZB`r!? zk+e4Hs-)YJHYfcmXL+^|M|N^m??{_FjMN^+vCQ$=>8X$$81g zB$p&lNNz};k$hJ2HOU*3?@8X8{ABX;$*(5AoxCsk%iiO9*Y`fT_c^`S_I|SW?|c8H z_dC5mNJ&l^mQt2-T*{P`nJMR_EJ|6CvNq+alzURPraYPQe9E6w_NIK666}-EC%uoa zPazwas{5SK=kz{v`n2>pzt5$8uI+PMpUr(R(ekQ~#OzRbQ)ba^LK}gZujX9@lqD-|c;0N{de$msX#4 za@v_`3)7aTtx4OKo}8YOepY&OdVBh%>D$x)nEpokp7f8?4`#$<^v%f67?x3%aa_if zjF}ndWGu>9k+C-8s*Imx+?8=CGd8m-vpMt0%;z)zocUH}AoD=xw^_ro&di#h)s}TZ z)~#9hWc@1ZsjNR_y`Hrv>z`R)XUAlxWaniM%`VHHm_0T7%3IY5it@(f)#gpho0Zp`w=(aNyle7q&3h+rf8M|PiGGRwGWreZH=3Z!<;Uh{UET0sRLIA24IUSp%8}v=6vwz=i=o z9&pEi2M0Vh;KKo57N~+=1z80H3yKQH6x0^XC^)-dVZri(H3c^nJX-K9MWq@zahm#s)n3ArwX4h{8M2^;hw_%g`X838WA(1?}&jT zN=MX;m@#6`h-D)#IpTloh^I#UVZ`er-W!=XvUuc@kq?ag^~gVs{JJQwsIX{kQBBeD zMKg+iP&Bt_NzwU5Ym2Tfy0z%eqWg<}RrFNRb44!~yYwdj?qBb}-M_{ExPPbrCI4&1iN!Y-|Dz>9~={)g3qgxGRqP)5MgC z!zb2Gym;ah6L(i7Rb^EbRgJA`s5-T(y=p_%LsidKy;k*Qb!zpo)r+bhtKMJzMU7R{ zyJlcbWleL;t*^VO?ykD6b#~pmbzjt{)DLI(^&R!UuK#oWXOq$=ojPgL zq!%WAHYwPU*pS&UsKMVbzM-LERl~InTN<8e=$srgdBo(h$>S%VKDmAJ+R4|L*-6Sj zKKsVnA!qj<|1V)#`}FMfX2rsD^!e>!Z_Eyk3MR%ha~ID0mFU*yry7LbBb@!e0qYMU zMnl~%W~+OJ|2y%am>o0Sf%;GsgV|B%*`7ZM12qr#qsNs0Ana^4i?k0@#_z({VzzaL z7^66cAa{ud&pDz&pSvP>fa@9;-Tb5}pM$^%-&T+3V6hk#_ zkWIVY2F>{h#0PhI3c9%`3qQ|D{&&H%8B3#Ez8;JAUPYaN_EW!s?ux1|*Gp-uqs2ke zOd`#RV&HdTFL&PmH}OZ2W$}I2?ohuFS?VT{a8$TajK_v}7Z{!^6LYyYor~e)v9b(X zO=VbyD#LbMS@5UeBL`w8{u+5;G;TWF=g{$-KT!3wC-D3f9y*)0KOd_p{|m4#!BQx| z@W|`Lee;m&vouc?`QyaSwhfz72kB-74xeLS<5`gD-^XP+1#; zoz|I=avT5o)b;2vK1I1tU(@*L}tiM{g8Z*#DE^$dJ6aN-6n_(%XcKOywsbwBx8;SaxbC!p1B!2i}P5V@Bp5B zX^5AB{_Ukc+Tw*idug{HvKsoK10MHgXc)g)tifDDn`{>SX{S`+H|~pwF7kf#=rhGj3Tiis@b>?BYvn{aRH!ChPP_z-Nz|0$6s zK4ty*Q^LJ366FWD_2Jx?^Cs-gyiA$9xvN;=v7V!!-(@~}0sb9~u8=AQ$!|nID~J6_ zX}tBV;|ADI_%ZwR9~VU`S7ft0xkxP+g`QiP>vepsW9s?jUUtC`adZWpzgmY}%@Tu* zZuceaH5)V%<}2dmiUj&dpJ%g|dA72$J4OBC6t8kIT zGnXXjWuh8PG;``G!W|~x+i|a<4s(b*Ulgce^tu*Tj;Nm3CJX{uA!I=O~Gtg;X4MOqtS-a`D zIz4^X&?|U4zV_2;+yQhRI*h))1?Y6Ne+y_wc25S6f*V|n;(R8UCvOm^bG{m!A@385 zIp3nMiFaZYbow*FI&eAAbtncrWaIq=A$&lWcO*Cs=)Ai_r#TfI9|fJJ&gT>tx^6mM z9rq5P>+=(EwdPI3yKj@%+w4`>{8obxD}&a)pjU{P+k&GG4~x;(+`9a%slubC%Iic7 zJ-k8bl#h4LSQagj6%;m z0_bxZN1cC~@Wg4iS6H3&(ptUfu`%L8c_9`OZje8fcgn5uH5rf}@FnzIHIlb6D%BaP zO>I-ps29{r>TSzoC0V_#R4bP+E)KPdtYWL&y2-lV+GcIfkI(O&pPAn;e?b1={KEWE z`IY(O@+ard$p1lpbAD_7kMb|ezc&BI{Ga6Cp8vHk(U;;&_vQHVeFeTDKEJQjH`Z6{ zTk1RCcZKgx-`&1@efRr*;d|KksP75iQv>=A$QV#CV90=?0ld*y;4Q#*OF?=;PCK=4((csg4%St7%J=1d`KdgphV!nYQ==!<)9QI@)WN5I zz0{}=H5zCQ=~kn-{G|M}{JeZ${-FHf`9=8^`D62|^QY#|%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 From 91295a2a9ac0b5a8727ca8f08591138781df99bc Mon Sep 17 00:00:00 2001 From: Jefferey S Date: Tue, 28 Jan 2025 23:54:37 -0500 Subject: [PATCH 2/6] Update README.md --- bindings/c3/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bindings/c3/README.md b/bindings/c3/README.md index 4a70a27..8d18017 100644 --- a/bindings/c3/README.md +++ b/bindings/c3/README.md @@ -1,6 +1,6 @@ # 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. +This directory contains the clay.c3 bindings file as well as a recreation of the clay-raylib-renderer and the video-example raylib/clay project. Special thanks to: - [Christoffer L](https://github.com/lerno) C3's core developer (as I understand it) @@ -35,7 +35,7 @@ CLAY( Clay C3 Macro System ```cpp /* FILTER BUTTON */ -clay::clay( +@clay( clay::id("FilterButton"), clay::@bodyIf(clay::hovered(), clay::rectangle({ .color = clay::hovered() ? FIRE_ORANGE : {80, 25, 200, 255}, @@ -61,9 +61,9 @@ clay::clay( - - 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 +- - - (*note: for the current configuration you'll need to modify the default raylib module name in the raylib.c3i file in [build](build) from `raylib55::li` to `module raylib`*) +- - then use the command `c3c run video-example` to compile and run the 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 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: From 9dc9e4992ce3246d656e1513bc06fbe134f31d05 Mon Sep 17 00:00:00 2001 From: Jefferey Schlueter <47044769+JeffereyAEL@users.noreply.github.com> Date: Wed, 29 Jan 2025 12:04:40 -0500 Subject: [PATCH 3/6] Somehow I deleted the example .c3 file. Fixed project settings, I forgot I can't drag and drop when it comes to relative project files. --- bindings/c3/examples/video-example.c3 | 292 +++++++++++ bindings/c3/project.json | 2 +- bindings/c3/source/clay-depracated.c3 | 700 -------------------------- 3 files changed, 293 insertions(+), 701 deletions(-) create mode 100644 bindings/c3/examples/video-example.c3 delete mode 100644 bindings/c3/source/clay-depracated.c3 diff --git a/bindings/c3/examples/video-example.c3 b/bindings/c3/examples/video-example.c3 new file mode 100644 index 0000000..be38f24 --- /dev/null +++ b/bindings/c3/examples/video-example.c3 @@ -0,0 +1,292 @@ +module videoexample; + +import clay; +import raylib; + +const uint FONT_ID_BODY_16 = 0; + +fn void errorHandler(ErrorData errorData) +{ + // std::io::printfn("ERROR: \"%s\"", errorData.errorText.chars); +} + +fn void renderDropdownMenuItem(String text) { + @clay( + clay::layout({ .padding = clay::paddingUni(16)}) + ) { + clay::text( + text, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + })); + }; +} + +fn void renderHeaderButton(String text) { + @clay( + clay::layout({ .padding = { 16, 16, 8, 8 }}), + clay::rectangle({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = clay::cornerRadiusUni(5) + }) + ) { + clay::text(text, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + })); + }; +} + +fn void handleSidebarInteraction( + ElementId elementId, + PointerData pointerData, + iptr userData +) { + // If this button was clicked + if (pointerData.state == PointerState.RELEASED_THIS_FRAME) { + if (userData >= 0 && userData < ALL_DOCUMENTS.len) { + // Select the corresponding document + selectedDocumentIndex = (uint)userData; + } + } +} + +struct Document { + String title; + String contents; +} + +const Document[*] ALL_DOCUMENTS = { + { "Squirrels", "The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n" }, + { "Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }, + { "Vacuum Instructions", "Chapter 3: Getting Started - Unpacking and Setup\n""\n""Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \"Package Contents\" section on page 2.\n""\n""1. Unboxing Your Vacuum\n""Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\n""\n"" The main vacuum unit\n"" A telescoping extension wand\n"" A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\n"" A reusable dust bag (if applicable)\n"" A power cord with a 3-prong plug\n"" A set of quick-start instructions\n""\n""2. Assembling Your Vacuum\n""Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\n""\n""For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\n""\n""3. Powering On\n""To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \"On\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\n""\n""Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \"Section 5: Maintenance\" for filter installation instructions." }, + { "Article 4", "Article 4" }, + { "Article 5", "Article 5" } +}; + +uint selectedDocumentIndex = 0; + +RectangleElementConfig contentBackgroundConfig = { + { 90, 90, 90, 255 }, + clay::@cornerRadiusUniCT(8) +}; + +Sizing layoutExpand = Sizing{ clay::sizingGrow(), clay::sizingGrow() }; + +LayoutConfig sidebarButtonLayout = LayoutConfig{ + .sizing = { .width = clay::sizingGrow() }, + .padding = clay::paddingCT(16, 16, 16, 16) +}; + +bool isDebugModeEnabled = false; + +fn void main() @public +{ + raylib::setConfigFlags(raylib::FLAG_VSYNC_HINT | raylib::FLAG_WINDOW_RESIZABLE | raylib::FLAG_WINDOW_HIGHDPI | raylib::FLAG_MSAA_4X_HINT); + raylib::initWindow(1024, 768, "Introducing Clay C3-Demo"); + defer raylib::closeWindow(); + + uint clayRequiredMemory = clay::minMemorySize(); + Arena clayMemory = clay::createArena(clayRequiredMemory, malloc(clayRequiredMemory)); + clay::initialize(clayMemory, { raylib::getScreenWidth(), raylib::getScreenHeight() }, { &errorHandler, 0 }); + + + clay::setMeasureTextFunction(&clay::renderer::raylibMeasureText); + clay::renderer::raylibFonts[FONT_ID_BODY_16] = { + FONT_ID_BODY_16, + raylib::loadFontEx("resources/Lexend-Regular.ttf", 48, null, 400) + }; + + raylib::setTextureFilter( + clay::renderer::raylibFonts[FONT_ID_BODY_16].font.texture, + TextureFilter.BILINEAR + ); + + while (!raylib::windowShouldClose()) { + clay::setLayoutDimensions({raylib::getScreenWidth(), raylib::getScreenHeight()}); + Vector2 mouse_position = raylib::getMousePosition(); + Vector2 scroll_delta = raylib::getMouseWheelMoveV(); + clay::setPointerState({mouse_position.x, mouse_position.y}, raylib::isMouseButtonDown(MouseButton.LEFT)); + clay::updateScrollContainer(true, {scroll_delta.x, scroll_delta.y}, raylib::getFrameTime()); + + if (raylib::isKeyReleased(raylib::KEY_D)) { isDebugModeEnabled = !isDebugModeEnabled; clay::setDebugModeEnabled(isDebugModeEnabled); } + + clay::beginLayout(); + + @clay( + clay::id("OuterContainer"), + clay::rectangle({ .color = { 43, 41, 51, 255 } }), + clay::layout({ + .sizing = layoutExpand, + .padding = clay::paddingUni(16), + .childGap = 16, + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + }) + ) { + @clay( + clay::id("HeaderBar"), + clay::rectangle(contentBackgroundConfig), + clay::layout({ + .sizing = { + .height = clay::sizingFixed(60), + .width = clay::sizingGrow() + }, + .padding = { 16, 16, 0, 0 }, + .childGap = 16, + .childAlignment = { + .y = AlignY.CENTER + } + }) + ) { + // Header buttons go here + @clay( + clay::id("FileButton"), + clay::layout({ .padding = { 16, 16, 8, 8 }}), + clay::rectangle({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = clay::cornerRadiusUni(5) + }) + ) { + clay::text("File", clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + })); + + bool fileMenuVisible = + clay::pointerOver(clay::getElementId("FileButton")) + || + clay::pointerOver(clay::getElementId("FileMenu")); + + if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap + @clay( + clay::id("FileMenu"), + clay::floating({ + .attachment = { + .parent = AttachPoint.LEFT_BOTTOM + }, + }), + clay::layout({ + .padding = {0, 0, 8, 8 } + }) + ) { + @clay( + clay::layout({ + .sizing = { + .width = clay::sizingFixed(200) + }, + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + }), + clay::rectangle({ + .color = { 40, 40, 40, 255 }, + .cornerRadius = clay::cornerRadiusUni(8) + }) + ) { + // Render dropdown items here + renderDropdownMenuItem("New"); + renderDropdownMenuItem("Open"); + renderDropdownMenuItem("Close"); + }; + }; + } + }; + renderHeaderButton("Edit"); + @clay(clay::layout({ .sizing = { .width = clay::sizingGrow() }})) {}; + renderHeaderButton("Upload"); + renderHeaderButton("Media"); + renderHeaderButton("Support"); + }; + + @clay( + clay::id("LowerContent"), + clay::layout({ .sizing = layoutExpand, .childGap = 16 }) + ) { + @clay( + clay::id("Sidebar"), + contentBackgroundConfig, + clay::layout({ + .sizing = { + .width = clay::sizingFixed(250), + .height = clay::sizingGrow() + }, + .padding = clay::paddingUni(16), + .childGap = 8, + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + }) + ) { + for (int i = 0; i < ALL_DOCUMENTS.len; i++) { + Document document = ALL_DOCUMENTS[i]; + + if (i == selectedDocumentIndex) { + @clay( + clay::layout(sidebarButtonLayout), + clay::rectangle({ + .color = { 120, 120, 120, 255 }, + .cornerRadius = clay::cornerRadiusUni(8), + }) + ) { + clay::text(document.title, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + })); + }; + } else { + @clay( + clay::layout(sidebarButtonLayout), + clay::onHover(&handleSidebarInteraction, i), + clay::@bodyIf( + clay::hovered(), + clay::rectangle({ + .color = { 120, 120, 120, 120 }, + .cornerRadius = clay::cornerRadiusUni(8) + }) + ), + ) { + clay::text(document.title, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + })); + }; + } + } + }; + + @clay( + clay::id("MainContent"), + clay::rectangle(contentBackgroundConfig), + clay::scroll({ .vertical = true }), + clay::layout({ + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + .childGap = 14, + .padding = clay::paddingUni(16), + .sizing = layoutExpand, + }), + ) { + Document document = ALL_DOCUMENTS[selectedDocumentIndex]; + clay::text(document.title, clay::textConfig({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = {255, 255, 255, 255}, + })); + clay::text(document.contents, clay::textConfig({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = {255, 255, 255, 255}, + })); + }; + }; + }; + + RenderCommandArray renderCommands = clay::endLayout(); + + + raylib::beginDrawing(); + raylib::clearBackground(raylib::WHITE); + clay::renderer::raylibRender(renderCommands); + raylib::endDrawing(); + } +} \ No newline at end of file diff --git a/bindings/c3/project.json b/bindings/c3/project.json index c315027..77574df 100644 --- a/bindings/c3/project.json +++ b/bindings/c3/project.json @@ -2,6 +2,7 @@ "langrev": "1", "authors": [ "Jefferey Schlueter " ], "version": "0.1.0", + "dependency-search-paths": [ "lib" ], "targets": { // TODO: found out how to stop this from outputting a .lib and .pdb in addition to the .exe (they don't do anything) @@ -10,7 +11,6 @@ "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) diff --git a/bindings/c3/source/clay-depracated.c3 b/bindings/c3/source/clay-depracated.c3 deleted file mode 100644 index 7dac453..0000000 --- a/bindings/c3/source/clay-depracated.c3 +++ /dev/null @@ -1,700 +0,0 @@ -// 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 From 1b676968439dd8be19326d9dec06a3d1cc2f43b6 Mon Sep 17 00:00:00 2001 From: Jefferey Schlueter <47044769+JeffereyAEL@users.noreply.github.com> Date: Wed, 29 Jan 2025 12:17:46 -0500 Subject: [PATCH 4/6] added .gitkeeps to the lib and build folder --- bindings/c3/build/.gitkeep | 0 bindings/c3/c-lang/source/clay.c | 4126 ++++++++++++++++++++ bindings/c3/examples/video-example.c3 | 292 ++ bindings/c3/lib/.gitkeep | 0 bindings/c3/project.json | 33 + bindings/c3/resources/Lexend-Regular.ttf | Bin 0 -> 77836 bytes bindings/c3/source/clay-raylib-renderer.c3 | 447 +++ bindings/c3/source/clay.c3 | 478 +++ 8 files changed, 5376 insertions(+) create mode 100644 bindings/c3/build/.gitkeep create mode 100644 bindings/c3/c-lang/source/clay.c create mode 100644 bindings/c3/examples/video-example.c3 create mode 100644 bindings/c3/lib/.gitkeep create mode 100644 bindings/c3/project.json create mode 100644 bindings/c3/resources/Lexend-Regular.ttf create mode 100644 bindings/c3/source/clay-raylib-renderer.c3 create mode 100644 bindings/c3/source/clay.c3 diff --git a/bindings/c3/build/.gitkeep b/bindings/c3/build/.gitkeep new file mode 100644 index 0000000..e69de29 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/examples/video-example.c3 b/bindings/c3/examples/video-example.c3 new file mode 100644 index 0000000..cc54f9c --- /dev/null +++ b/bindings/c3/examples/video-example.c3 @@ -0,0 +1,292 @@ +module videoexample; + +import clay; +import raylib5::rl; + +const uint FONT_ID_BODY_16 = 0; + +fn void errorHandler(ErrorData errorData) +{ + // std::io::printfn("ERROR: \"%s\"", errorData.errorText.chars); +} + +fn void renderDropdownMenuItem(String text) { + @clay( + clay::layout({ .padding = clay::paddingUni(16)}) + ) { + clay::text( + text, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + })); + }; +} + +fn void renderHeaderButton(String text) { + @clay( + clay::layout({ .padding = { 16, 16, 8, 8 }}), + clay::rectangle({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = clay::cornerRadiusUni(5) + }) + ) { + clay::text(text, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + })); + }; +} + +fn void handleSidebarInteraction( + ElementId elementId, + PointerData pointerData, + iptr userData +) { + // If this button was clicked + if (pointerData.state == PointerState.RELEASED_THIS_FRAME) { + if (userData >= 0 && userData < ALL_DOCUMENTS.len) { + // Select the corresponding document + selectedDocumentIndex = (uint)userData; + } + } +} + +struct Document { + String title; + String contents; +} + +const Document[*] ALL_DOCUMENTS = { + { "Squirrels", "The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n" }, + { "Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }, + { "Vacuum Instructions", "Chapter 3: Getting Started - Unpacking and Setup\n""\n""Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \"Package Contents\" section on page 2.\n""\n""1. Unboxing Your Vacuum\n""Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\n""\n"" The main vacuum unit\n"" A telescoping extension wand\n"" A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\n"" A reusable dust bag (if applicable)\n"" A power cord with a 3-prong plug\n"" A set of quick-start instructions\n""\n""2. Assembling Your Vacuum\n""Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\n""\n""For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\n""\n""3. Powering On\n""To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \"On\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\n""\n""Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \"Section 5: Maintenance\" for filter installation instructions." }, + { "Article 4", "Article 4" }, + { "Article 5", "Article 5" } +}; + +uint selectedDocumentIndex = 0; + +RectangleElementConfig contentBackgroundConfig = { + { 90, 90, 90, 255 }, + clay::@cornerRadiusUniCT(8) +}; + +Sizing layoutExpand = Sizing{ clay::sizingGrow(), clay::sizingGrow() }; + +LayoutConfig sidebarButtonLayout = LayoutConfig{ + .sizing = { .width = clay::sizingGrow() }, + .padding = clay::paddingCT(16, 16, 16, 16) +}; + +bool isDebugModeEnabled = false; + +fn void main() @public +{ + rl::setConfigFlags(rl::FLAG_VSYNC_HINT | rl::FLAG_WINDOW_RESIZABLE | rl::FLAG_WINDOW_HIGHDPI | rl::FLAG_MSAA_4X_HINT); + rl::initWindow(1024, 768, "Introducing Clay C3-Demo"); + defer rl::closeWindow(); + + uint clayRequiredMemory = clay::minMemorySize(); + Arena clayMemory = clay::createArena(clayRequiredMemory, malloc(clayRequiredMemory)); + clay::initialize(clayMemory, { rl::getScreenWidth(), rl::getScreenHeight() }, { &errorHandler, 0 }); + + + clay::setMeasureTextFunction(&clay::renderer::raylibMeasureText); + clay::renderer::raylibFonts[FONT_ID_BODY_16] = { + FONT_ID_BODY_16, + rl::loadFontEx("resources/Lexend-Regular.ttf", 48, null, 400) + }; + + rl::setTextureFilter( + clay::renderer::raylibFonts[FONT_ID_BODY_16].font.texture, + TextureFilter.BILINEAR + ); + + while (!rl::windowShouldClose()) { + clay::setLayoutDimensions({rl::getScreenWidth(), rl::getScreenHeight()}); + Vector2 mouse_position = rl::getMousePosition(); + Vector2 scroll_delta = rl::getMouseWheelMoveV(); + clay::setPointerState({mouse_position.x, mouse_position.y}, rl::isMouseButtonDown(MouseButton.LEFT)); + clay::updateScrollContainer(true, {scroll_delta.x, scroll_delta.y}, rl::getFrameTime()); + + if (rl::isKeyReleased(rl::KEY_D)) { isDebugModeEnabled = !isDebugModeEnabled; clay::setDebugModeEnabled(isDebugModeEnabled); } + + clay::beginLayout(); + + @clay( + clay::id("OuterContainer"), + clay::rectangle({ .color = { 43, 41, 51, 255 } }), + clay::layout({ + .sizing = layoutExpand, + .padding = clay::paddingUni(16), + .childGap = 16, + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + }) + ) { + @clay( + clay::id("HeaderBar"), + clay::rectangle(contentBackgroundConfig), + clay::layout({ + .sizing = { + .height = clay::sizingFixed(60), + .width = clay::sizingGrow() + }, + .padding = { 16, 16, 0, 0 }, + .childGap = 16, + .childAlignment = { + .y = AlignY.CENTER + } + }) + ) { + // Header buttons go here + @clay( + clay::id("FileButton"), + clay::layout({ .padding = { 16, 16, 8, 8 }}), + clay::rectangle({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = clay::cornerRadiusUni(5) + }) + ) { + clay::text("File", clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + })); + + bool fileMenuVisible = + clay::pointerOver(clay::getElementId("FileButton")) + || + clay::pointerOver(clay::getElementId("FileMenu")); + + if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap + @clay( + clay::id("FileMenu"), + clay::floating({ + .attachment = { + .parent = AttachPoint.LEFT_BOTTOM + }, + }), + clay::layout({ + .padding = {0, 0, 8, 8 } + }) + ) { + @clay( + clay::layout({ + .sizing = { + .width = clay::sizingFixed(200) + }, + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + }), + clay::rectangle({ + .color = { 40, 40, 40, 255 }, + .cornerRadius = clay::cornerRadiusUni(8) + }) + ) { + // Render dropdown items here + renderDropdownMenuItem("New"); + renderDropdownMenuItem("Open"); + renderDropdownMenuItem("Close"); + }; + }; + } + }; + renderHeaderButton("Edit"); + @clay(clay::layout({ .sizing = { .width = clay::sizingGrow() }})) {}; + renderHeaderButton("Upload"); + renderHeaderButton("Media"); + renderHeaderButton("Support"); + }; + + @clay( + clay::id("LowerContent"), + clay::layout({ .sizing = layoutExpand, .childGap = 16 }) + ) { + @clay( + clay::id("Sidebar"), + contentBackgroundConfig, + clay::layout({ + .sizing = { + .width = clay::sizingFixed(250), + .height = clay::sizingGrow() + }, + .padding = clay::paddingUni(16), + .childGap = 8, + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + }) + ) { + for (int i = 0; i < ALL_DOCUMENTS.len; i++) { + Document document = ALL_DOCUMENTS[i]; + + if (i == selectedDocumentIndex) { + @clay( + clay::layout(sidebarButtonLayout), + clay::rectangle({ + .color = { 120, 120, 120, 255 }, + .cornerRadius = clay::cornerRadiusUni(8), + }) + ) { + clay::text(document.title, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + })); + }; + } else { + @clay( + clay::layout(sidebarButtonLayout), + clay::onHover(&handleSidebarInteraction, i), + clay::@bodyIf( + clay::hovered(), + clay::rectangle({ + .color = { 120, 120, 120, 120 }, + .cornerRadius = clay::cornerRadiusUni(8) + }) + ), + ) { + clay::text(document.title, clay::textConfig({ + .textColor = { 255, 255, 255, 255 }, + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + })); + }; + } + } + }; + + @clay( + clay::id("MainContent"), + clay::rectangle(contentBackgroundConfig), + clay::scroll({ .vertical = true }), + clay::layout({ + .layoutDirection = LayoutDirection.TOP_TO_BOTTOM, + .childGap = 14, + .padding = clay::paddingUni(16), + .sizing = layoutExpand, + }), + ) { + Document document = ALL_DOCUMENTS[selectedDocumentIndex]; + clay::text(document.title, clay::textConfig({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = {255, 255, 255, 255}, + })); + clay::text(document.contents, clay::textConfig({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = {255, 255, 255, 255}, + })); + }; + }; + }; + + RenderCommandArray renderCommands = clay::endLayout(); + + + rl::beginDrawing(); + rl::clearBackground(rl::WHITE); + clay::renderer::raylibRender(renderCommands); + rl::endDrawing(); + } +} \ No newline at end of file diff --git a/bindings/c3/lib/.gitkeep b/bindings/c3/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bindings/c3/project.json b/bindings/c3/project.json new file mode 100644 index 0000000..f43045b --- /dev/null +++ b/bindings/c3/project.json @@ -0,0 +1,33 @@ +{ + "langrev": "1", + "authors": [ "Jefferey Schlueter " ], + "version": "0.1.0", + "dependency-search-paths": [ "lib" ], + "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", + "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 missing 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-raylib-renderer.c3 b/bindings/c3/source/clay-raylib-renderer.c3 new file mode 100644 index 0000000..45553ca --- /dev/null +++ b/bindings/c3/source/clay-raylib-renderer.c3 @@ -0,0 +1,447 @@ +module raylib5::rl @if($feature(NO_STDLIB)); + +distinct ZString = inline char*; + +module clay::renderer; +import raylib5::rl; + +// 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; + rl::Font font; +} + +RaylibFont[10] raylibFonts; +rl::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 +{ + rl::Model model; + float scale; + rl::Vector3 position; + rl::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*rl::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 + rl::Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unprojecte matrix + rl::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 = rl::getCameraMatrix(camera); + + Matrix matProj = MATRIX_IDENTITY; + + if (camera.projection == CameraProjection.PERSPECTIVE) + { + // Calculate projection matrix from perspective + matProj = matrixPerspective((double)(camera.fovy*rl::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; + rl::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; + rl::drawTextureEx( + imageTexture, + {boundingBox.x, boundingBox.y}, + 0, + boundingBox.width / (float)imageTexture.width, + rl::WHITE); + break; + } + case RenderCommandType.SCISSOR_START: { + rl::beginScissorMode((int)$$round(boundingBox.x), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width), (int)$$round(boundingBox.height)); + break; + } + case RenderCommandType.SCISSOR_END: { + rl::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); + rl::drawRectangleRounded({ boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, clayToRaylibColor(config.color)); + } else { + rl::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) { + rl::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) { + rl::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) { + rl::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) { + rl::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) { + rl::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) { + rl::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) { + rl::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) { + rl::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); + rl::beginMode3D(raylibCamera); + rl::drawModel(customElement.element.model.model, positionRay.position, customElement.element.model.scale * scaleValue, rl::WHITE); // Draw 3d model with texture + rl::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 From afe52f946bf4a377fe39c261fdb085e4ff586327 Mon Sep 17 00:00:00 2001 From: Jefferey S Date: Wed, 29 Jan 2025 12:34:07 -0500 Subject: [PATCH 5/6] Update README.md --- bindings/c3/README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bindings/c3/README.md b/bindings/c3/README.md index 8d18017..a4491b8 100644 --- a/bindings/c3/README.md +++ b/bindings/c3/README.md @@ -57,14 +57,12 @@ Clay C3 Macro System ## 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 +- - set your `cd` to [c3](./) +- - 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 the raylib.c3i file in [build](build) from `raylib55::li` to `module raylib`*) - - then use the command `c3c run video-example` to compile and run the 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 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)) +- - - (*note: to use the `c3c build ` command with video-example, you'll need to copy the resource folder into the [build](build) directory 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) From c133e070961c728100e3545e624f20eea3482f6c Mon Sep 17 00:00:00 2001 From: Jefferey Schlueter <47044769+JeffereyAEL@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:49:57 -0500 Subject: [PATCH 6/6] Added in some missing Clay macros and moved re-used config declarations out of global scope into main for video-example.c3 --- bindings/c3/examples/video-example.c3 | 27 +- bindings/c3/source/clay.c3 | 397 ++++++++++++++++++++------ 2 files changed, 321 insertions(+), 103 deletions(-) diff --git a/bindings/c3/examples/video-example.c3 b/bindings/c3/examples/video-example.c3 index cc54f9c..750952c 100644 --- a/bindings/c3/examples/video-example.c3 +++ b/bindings/c3/examples/video-example.c3 @@ -68,18 +68,6 @@ const Document[*] ALL_DOCUMENTS = { uint selectedDocumentIndex = 0; -RectangleElementConfig contentBackgroundConfig = { - { 90, 90, 90, 255 }, - clay::@cornerRadiusUniCT(8) -}; - -Sizing layoutExpand = Sizing{ clay::sizingGrow(), clay::sizingGrow() }; - -LayoutConfig sidebarButtonLayout = LayoutConfig{ - .sizing = { .width = clay::sizingGrow() }, - .padding = clay::paddingCT(16, 16, 16, 16) -}; - bool isDebugModeEnabled = false; fn void main() @public @@ -104,6 +92,21 @@ fn void main() @public TextureFilter.BILINEAR ); + // ============================================== + // ===== predeclarations of re-used configs ===== + // ============================================== + RectangleElementConfig contentBackgroundConfig = { + { 90, 90, 90, 255 }, + clay::@cornerRadiusUniCT(8) + }; + + Sizing layoutExpand = Sizing{ clay::sizingGrow(), clay::sizingGrow() }; + + LayoutConfig sidebarButtonLayout = LayoutConfig{ + .sizing = { .width = clay::sizingGrow() }, + .padding = clay::paddingCT(16, 16, 16, 16) + }; + while (!rl::windowShouldClose()) { clay::setLayoutDimensions({rl::getScreenWidth(), rl::getScreenHeight()}); Vector2 mouse_position = rl::getMousePosition(); diff --git a/bindings/c3/source/clay.c3 b/bindings/c3/source/clay.c3 index 0ec1686..76a806a 100644 --- a/bindings/c3/source/clay.c3 +++ b/bindings/c3/source/clay.c3 @@ -2,11 +2,14 @@ module clay; 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 + $vaexpr[$i]; // If you get an error here consider the @body[...]() macros $endfor clay::elementPostConfiguration(); @body(); @@ -15,34 +18,33 @@ macro @clay(...; @body()) @builtin 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 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 idi(String idString, uint seed) { clay::attachId(clay::hashString({idString.len, idString}, 0, seed)); } + +macro idLocal(String idString, uint offset) { clay::attachId(clay::hashString({idString.len, idString}, offset, 0)); } + +macro idiLocal(String idString, uint offset, uint seed) { clay::attachId(clay::hashString({idString.len, idString}, offset, seed)); } + 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 sizingFit(float min = 0) { return { .size.minMax = {min, float.max}, .type = SizingType.FIT }; } -macro SizingAxis sizingGrow() { return { .size.minMax = {0, 0}, .type = SizingType.GROW }; } +macro SizingAxis sizingGrow(float max = 0) { return { .size.minMax = {0, max}, .type = SizingType.GROW }; } macro SizingAxis sizingFixed(float pixels) { return { .size.minMax = {pixels, pixels}, .type = SizingType.FIXED }; } @@ -64,6 +66,9 @@ macro Padding paddingCT(ushort $a, ushort $b, ushort $c, ushort $d) { return { $ macro CornerRadius @cornerRadiusUniCT(float #uniform) { return {#uniform, #uniform, #uniform, #uniform}; } +// TODO: figure out why this isn't working +// macro ClayColor @colorHex(#hex) { return {(float)(((#hex >> 16) & 0xFF) / 255.0), (float)(((#hex >> 8) & 0xFF) / 255.0), (float)(((#hex) & 0xFF) / 255.0), 255}; } + struct ClayString { int length; @@ -93,7 +98,6 @@ struct ClayColor float r, g, b, a; } - struct ElementId { uint id; @@ -109,15 +113,16 @@ struct CornerRadius 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; + +distinct ElementConfigType = char; +const ElementConfigType ELEMENT_CONFIG_TYPE_NONE = 0; +const ElementConfigType ELEMENT_CONFIG_TYPE_RECTANGLE = 1; +const ElementConfigType ELEMENT_CONFIG_TYPE_BORDER_CONTAINER = 2; +const ElementConfigType ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER = 4; +const ElementConfigType ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER = 8; +const ElementConfigType ELEMENT_CONFIG_TYPE_IMAGE = 16; +const ElementConfigType ELEMENT_CONFIG_TYPE_TEXT = 32; +const ElementConfigType ELEMENT_CONFIG_TYPE_CUSTOM = 64; enum LayoutDirection : char @export { @@ -196,12 +201,11 @@ struct RectangleElementConfig { ClayColor color; CornerRadius cornerRadius; - // #ifdef CLAY_EXTEND_CONFIG_RECTANGLE - // CLAY_EXTEND_CONFIG_RECTANGLE - // #endif + // No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_RECTANGLE) + // TODO: C3 doesn't support conditional compilation outside of functions/methods/macros } -enum WrapMode @export +enum WrapMode { WORDS, NEWLINES, @@ -216,21 +220,19 @@ struct TextElementConfig ushort letterSpacing; ushort lineHeight; WrapMode wrapMode; - // #ifdef CLAY_EXTEND_CONFIG_TEXT - // CLAY_EXTEND_CONFIG_TEXT - // #endif + // No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_TEXT) + // TODO: C3 doesn't support conditional compilation outside of functions/methods/macros } struct ImageElementConfig { void *imageData; Dimensions sourceDimensions; - // #ifdef CLAY_EXTEND_CONFIG_IMAGE - // CLAY_EXTEND_CONFIG_IMAGE - // #endif + // No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_IMAGE) + // TODO: C3 doesn't support conditional compilation outside of functions/methods/macros } -enum AttachPoint : char @export +enum AttachPoint : char { LEFT_TOP, LEFT_CENTER, @@ -249,10 +251,10 @@ struct FloatingAttachPoints AttachPoint parent; } -enum PointerCaptureMode @export +enum PointerCaptureMode { CAPTURE, - // MODE_PASSTHROUGH, + // MODE_PASSTHROUGH, // not fully implemented in C as of bindings dev-time PARENT, } @@ -270,6 +272,8 @@ struct FloatingElementConfig struct CustomElementConfig { void *customData; + // No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_FLOATING) + // TODO: C3 doesn't support conditional compilation outside of functions/methods/macros } struct ScrollElementConfig @@ -293,6 +297,8 @@ struct BorderElementConfig Border bottom; Border betweenChildren; CornerRadius cornerRadius; + // No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_BORDER) + // TODO: C3 doesn't support conditional compilation outside of functions/methods/macros } union ElementConfigUnion @@ -341,24 +347,20 @@ 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 +enum PointerState { PRESSED_THIS_FRAME, PRESSED, @@ -376,7 +378,7 @@ def OnHoverEvent = fn void(ElementId elementId, PointerData pointerData, iptr us def MeasureTextFunc = fn Dimensions(ClayString *text, TextElementConfig *config); def QueryScrollOffsetFunc = fn ClayVector2(uint elementId); -enum ErrorType @export +enum ErrorType { TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED, ARENA_CAPACITY_EXCEEDED, @@ -402,69 +404,282 @@ struct ErrorHandler uint128 userData; } -distinct Context = void; // I'm not doing all that again +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 +{ + 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 +{ + LayoutElement *layoutElement; + ClayVector2 position; + ClayVector2 nextChildOffset; +} + +struct MeasuredWord +{ + int startOffset; + int length; + float width; + int next; +} + +struct MeasureTextCacheItem +{ + Dimensions unwrappedDimensions; + int measuredWordsStartIndex; + bool containsNewlines; + // Hash map data + uint id; + int nextIndex; + uint generation; +} + +struct ScrollContainerDataInternal +{ + LayoutElement *layoutElement; + ClayBoundingBox boundingBox; + Dimensions contentSize; + ClayVector2 scrollOrigin; + ClayVector2 pointerOrigin; + ClayVector2 scrollMomentum; + ClayVector2 scrollPosition; + ClayVector2 previousDelta; + float momentumTime; + uint elementId; + bool openThisFrame; + bool pointerScrollActive; +} + +def LayoutElementArray = carray::Array() ; +def IntArray = 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() ; + +// TODO: find out why alignment is off, accessing from Context doesn't really work +struct 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; + IntArray openLayoutElementStack; + IntArray layoutElementChildren; + IntArray layoutElementChildrenBuffer; + TextElementDataArray textElementData; + LayoutElementPointerArray imageElementPointers; + IntArray reusableElementIndexBuffer; + IntArray 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; + IntArray layoutElementsHashMap; + MeasureTextCacheItemArray measureTextHashMapInternal; + IntArray measureTextHashMapInternalFreeList; + IntArray measureTextHashMap; + MeasuredWordArray measuredWords; + IntArray measuredWordsFreeList; + IntArray openClipElementStack; + ElementIdArray pointerOverIds; + ScrollContainerDataInternalArray scrollContainerDatas; + BoolArray treeNodeVisited; + CharArray dynamicStringData; + DebugElementDataArray debugElementData; +} // ===================== // ===== 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 +// ===== Public Clay API C3 Functions (String replacement) ===== +fn ElementId getElementIdWithIndex(String idString, uint index) @inline { return __getElementIdWithIndex({idString.len, idString}, (uint)index); } -fn ElementId getElementId(String idString) @export @inline +fn ElementId getElementId(String idString) @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; +// ===== Public Clay API Functions ===== // TODO: worry about exports/ static library creation later +extern fn uint minMemorySize() @extern("Clay_MinMemorySize") ; // @export; +extern fn Arena createArena(uint capacity, void* offset) @extern("Clay_CreateArenaWithCapacityAndMemory") ; // @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") ; // @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") ; // @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; +extern fn ElementId __getElementIdWithIndex(ClayString idString, uint index) @extern("Clay_GetElementIdWithIndex") ; // @export; +extern fn ElementId __getElementId(ClayString idString) @extern("Clay_GetElementId") ; // @export; // ===== 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; +extern fn void openElement() @extern ("Clay__OpenElement") ; // @export; +extern fn void closeElement() @extern("Clay__CloseElement") ; // @export; +extern fn void openTextElement(ClayString text, TextElementConfig *textConfig) @extern("Clay__OpenTextElement") ; // @export; +extern fn void elementPostConfiguration() @extern("Clay__ElementPostConfiguration") ; // @export; +extern fn LayoutConfig * storeLayoutConfig(LayoutConfig config) @extern("Clay__StoreLayoutConfig") ; // @export; +extern fn void attachId(ElementId id) @extern("Clay__AttachId") ; // @export; +extern fn void attachLayoutConfig(LayoutConfig *config) @extern("Clay__AttachLayoutConfig") ; // @export; +extern fn void attachElementConfig(ElementConfigUnion config, ElementConfigType type) @extern("Clay__AttachElementConfig") ; // @export; +extern fn RectangleElementConfig * storeRectangleElementConfig(RectangleElementConfig config) @extern("Clay__StoreRectangleElementConfig") ; // @export; +extern fn TextElementConfig * storeTextElementConfig(TextElementConfig config) @extern("Clay__StoreTextElementConfig") ; // @export; +extern fn ImageElementConfig * storeImageElementConfig(ImageElementConfig config) @extern("Clay__StoreImageElementConfig") ; // @export; +extern fn FloatingElementConfig * storeFloatingElementConfig(FloatingElementConfig config) @extern("Clay__StoreFloatingElementConfig") ; // @export; +extern fn CustomElementConfig * storeCustomElementConfig(CustomElementConfig config) @extern("Clay__StoreCustomElementConfig") ; // @export; +extern fn ScrollElementConfig * storeScrollElementConfig(ScrollElementConfig config) @extern("Clay__StoreScrollElementConfig") ; // @export; +extern fn BorderElementConfig * storeBorderElementConfig(BorderElementConfig config) @extern("Clay__StoreBorderElementConfig") ; // @export; +extern fn ElementId hashString(ClayString key, uint offset, uint seed) @extern("Clay__HashString") ; // @export; +extern fn uint getParentElementId() @extern("Clay__GetParentElementId") ; // @export; // ========================================================================== // ===== An internal module for wrapping Struct Array's defined in Clay =====