diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0d72e..27397fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,4 +8,6 @@ if(NOT MSVC) add_subdirectory("examples/raylib-sidebar-scrolling-container") add_subdirectory("examples/cairo-pdf-rendering") add_subdirectory("examples/clay-official-website") + add_subdirectory("examples/introducing-clay-video-demo") + add_subdirectory("examples/SDL2-video-demo") endif() diff --git a/README.md b/README.md index eedf305..f4c34a0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Take a look at the [clay website](https://nicbarker.com/clay) for an example of clay compiled to wasm and running in the browser, or others in the [examples directory](https://github.com/nicbarker/clay/tree/main/examples). +You can also watch the [introduction video](https://youtu.be/DYWTw19_8r4) for an overview of the motivation behind Clay's development and a short demo of its usage. + A screenshot of a code IDE with lots of visual and textual elements _An example GUI application built with clay_ @@ -410,15 +412,12 @@ Clay supports C preprocessor directives to modulate functionality at compile tim The supported directives are: -- `CLAY_MAX_ELEMENT_COUNT` - Controls the maximum number of clay elements that memory is pre-allocated for. Defaults to **8192**, which should be more than enough for the majority of use cases. Napkin math is ~450 bytes of memory overhead per element (8192 elements is ~3.5mb of memory) -- `CLAY_DISABLE_CULLING` - Disables [Visibility Culling](#visibility-culling) of render commands. - `CLAY_WASM` - Required when targeting Web Assembly. -- `CLAY_OVERFLOW_TRAP` - By default, clay will continue to allow function calls without crashing even when it exhausts all its available pre-allocated memory. This can produce erroneous layout results that are difficult to interpret. If `CLAY_OVERFLOW_TRAP` is defined, clay will raise a `SIGTRAP` signal that will be caught by your debugger. Relies on `signal.h` being available in your environment. - `CLAY_DEBUG` - Used for debugging clay's internal implementation. Useful if you want to modify or debug clay, or learn how things work. It enables a number of debug features such as preserving source strings for hash IDs to make debugging easier. - `CLAY_EXTEND_CONFIG_RECTANGLE` - Provide additional struct members to `CLAY_RECTANGLE` that will be passed through with output render commands. - `CLAY_EXTEND_CONFIG_TEXT` - Provide additional struct members to `CLAY_TEXT_CONFIG` that will be passed through with output render commands. - `CLAY_EXTEND_CONFIG_IMAGE` - Provide additional struct members to `CLAY_IMAGE_CONFIG` that will be passed through with output render commands. -- `CLAY_EXTEND_CONFIG_CUSTOM` - Provide additional struct members to `CLAY_IMAGE_CONFIG` that will be passed through with output render commands. +- `CLAY_EXTEND_CONFIG_CUSTOM` - Provide additional struct members to `CLAY_CUSTOM_CONFIG` that will be passed through with output render commands. ### Bindings for non C @@ -483,11 +482,29 @@ Takes a pointer to a function that can be used to measure the `width, height` di **Note 2: It is essential that this function is as fast as possible.** For text heavy use-cases this function is called many times, and despite the fact that clay caches text measurements internally, it can easily become the dominant overall layout cost if the provided function is slow. **This is on the hot path!** +### Clay_SetMaxElementCount + +`void Clay_SetMaxElementCount(uint32_t maxElementCount)` + +Updates the internal maximum element count, allowing clay to allocate larger UI hierarchies. + +**Note: You will need to reinitialize clay, after calling [Clay_MinMemorySize()](#clay_minmemorysize) to calculate updated memory requirements.** + +### Clay_SetMaxMeasureTextCacheWordCount + +`void Clay_SetMaxMeasureTextCacheWordCount(uint32_t maxMeasureTextCacheWordCount)` + +Updates the internal text measurement cache size, allowing clay to allocate more text. The value represents how many seperate words can be stored in the text measurement cache. + +**Note: You will need to reinitialize clay, after calling [Clay_MinMemorySize()](#clay_minmemorysize) to calculate updated memory requirements.** + ### Clay_Initialize -`void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions)` +`void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler)` -Initializes the internal memory mapping, and sets the internal dimensions for layout. +Initializes the internal memory mapping, sets the internal dimensions for layout, and binds an error handler for clay to use when something goes wrong. + +Reference: [Clay_Arena](#clay_createarenawithcapacityandmemory), [Clay_ErrorHandler](#clay_errorhandler) ### Clay_SetLayoutDimensions @@ -1545,6 +1562,29 @@ Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_ ## Data Structures & Definitions +### Clay_String + +```C +typedef struct { + int length; + const char *chars; +} Clay_String; +``` + +`Clay_String` is a string container that clay uses internally to represent all strings. + +**Fields** + +**`.length`** - `int` + +The number of characters in the string, _not including an optional null terminator._ + +--- + +**`.chars`** - `const char *` + +A pointer to the contents of the string. This data is not guaranteed to be null terminated, so if you are passing it to code that expects standard null terminated C strings, you will need to copy the data and append a null terminator. + ### Clay_ElementId ```C @@ -1770,4 +1810,77 @@ An enum value representing the current "state" of the pointer interaction. As an --- +### Clay_ErrorHandler +```C +typedef struct +{ + void (*errorHandlerFunction)(Clay_ErrorData errorText); + uintptr_t userData; +} Clay_ErrorHandler; +``` + +**Fields** + +**`.errorHandlerFunction`** - `void (Clay_ErrorData errorText) {}` + +A function pointer to an error handler function, which takes `Clay_ErrorData` as an argument. This function will be called whenever Clay encounters an internal error. + +--- + +**`.userData`** - `uintptr_t` + +A generic pointer to extra userdata that is transparently passed through from `Clay_Initialize` to Clay's error handler callback. Defaults to NULL. + +--- + +### Clay_ErrorData + +```C +typedef struct +{ + Clay_ErrorType errorType; + Clay_String errorText; + uintptr_t userData; +} Clay_ErrorData; +``` + +**Fields** + +**`.errorType`** - `Clay_ErrorType` + +```C +typedef 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_ErrorType; +``` + +An enum representing the type of error Clay encountered. It's up to the user to handle on a case by case basis, but as some general guidance: + +- `CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED` - The user is attempting to use `CLAY_TEXT` and either forgot to call [Clay_SetMeasureTextFunction](#clay_setmeasuretextfunction) or accidentally passed a null function pointer. +- `CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED` - Clay was initialized with an Arena that was too small for the configured [Clay_SetMaxElementCount](#clay_setmaxelementcount). Try using [Clay_MinMemorySize()](#clay_minmemorysize) to get the exact number of bytes required by the current configuration. +- `CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED` - The declared UI hierarchy has too many elements for the configured max element count. Use [Clay_SetMaxElementCount](#clay_setmaxelementcount) to increase the max, then call [Clay_MinMemorySize()](#clay_minmemorysize) again and reinitialize clay's memory with the required size. +- `CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED` - The declared UI hierarchy has too much text for the configured text measure cache size. Use [Clay_SetMaxMeasureTextCacheWordCount](#clay_setmeasuretextcachesize) to increase the max, then call [Clay_MinMemorySize()](#clay_minmemorysize) again and reinitialize clay's memory with the required size. +- `CLAY_ERROR_TYPE_DUPLICATE_ID` - Two elements in Clays UI Hierarchy have been declared with exactly the same ID. Set a breakpoint in your error handler function for a stack trace back to exactly where this occured. +- `CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND` - A `CLAY_FLOATING` element was declared with the `.parentId` property, but no element with that ID was found. Set a breakpoint in your error handler function for a stack trace back to exactly where this occured. +- `CLAY_ERROR_TYPE_INTERNAL_ERROR` - Clay has encountered an internal logic or memory error. Please report this as a bug with a stack trace to help us fix these! + +--- + +**`.errorText`** - `Clay_String` + +A [Clay_String](#clay_string) that provides a human readable description of the error. May change in future and should not be relied on to detect error types. + +--- + +**`.userData`** - `uintptr_t` + +A generic pointer to extra userdata that is transparently passed through from `Clay_Initialize` to Clay's error handler callback. Defaults to NULL. + +--- \ No newline at end of file diff --git a/bindings/rust/README b/bindings/rust/README new file mode 100644 index 0000000..24fee06 --- /dev/null +++ b/bindings/rust/README @@ -0,0 +1,2 @@ +https://github.com/clay-ui-rs/clay +https://crates.io/crates/clay-layout \ No newline at end of file diff --git a/clay.h b/clay.h index af27b46..ea2d42d 100644 --- a/clay.h +++ b/clay.h @@ -69,15 +69,15 @@ #define CLAY__STRUCT_0_ARGS() {} #define CLAY__STRUCT_OVERRIDE(_0, _1, NAME, ...) NAME -#define CLAY__SIZING_FIT_INTERNAL(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) __VA_ARGS__, .type = CLAY__SIZING_TYPE_FIT } +#define CLAY__SIZING_FIT_INTERNAL(...) CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = CLAY__INIT(Clay_SizingMinMax) __VA_ARGS__ }, .type = CLAY__SIZING_TYPE_FIT } #define CLAY_SIZING_FIT(...) CLAY__SIZING_FIT_INTERNAL(CLAY__STRUCT_OVERRIDE("empty", ##__VA_ARGS__, CLAY__STRUCT_1_ARGS, CLAY__STRUCT_0_ARGS)(__VA_ARGS__)) -#define CLAY__SIZING_GROW_INTERNAL(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) __VA_ARGS__, .type = CLAY__SIZING_TYPE_GROW } +#define CLAY__SIZING_GROW_INTERNAL(...) CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = CLAY__INIT(Clay_SizingMinMax) __VA_ARGS__ }, .type = CLAY__SIZING_TYPE_GROW } #define CLAY_SIZING_GROW(...) CLAY__SIZING_GROW_INTERNAL(CLAY__STRUCT_OVERRIDE("empty", ##__VA_ARGS__, CLAY__STRUCT_1_ARGS, CLAY__STRUCT_0_ARGS)(__VA_ARGS__)) -#define CLAY_SIZING_FIXED(fixedSize) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = { fixedSize, fixedSize }, .type = CLAY__SIZING_TYPE_FIXED } +#define CLAY_SIZING_FIXED(fixedSize) CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { fixedSize, fixedSize } }, .type = CLAY__SIZING_TYPE_FIXED } -#define CLAY_SIZING_PERCENT(percentOfParent) CLAY__INIT(Clay_SizingAxis) { .sizePercent = (percentOfParent), .type = CLAY__SIZING_TYPE_PERCENT } +#define CLAY_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)) @@ -144,9 +144,8 @@ typedef struct } Clay__StringArray; typedef struct { - Clay_String label; - uint64_t nextAllocation; - uint64_t capacity; + uintptr_t nextAllocation; + size_t capacity; char *memory; } Clay_Arena; @@ -229,9 +228,9 @@ typedef struct { typedef struct { union { - Clay_SizingMinMax sizeMinMax; - float sizePercent; - }; + Clay_SizingMinMax minMax; + float percent; + } size; Clay__SizingType type; } Clay_SizingAxis; @@ -412,9 +411,6 @@ typedef struct Clay_String text; // TODO I wish there was a way to avoid having to have this on every render command uint32_t id; Clay_RenderCommandType commandType; - #ifdef CLAY_DEBUG - Clay_String name; - #endif } Clay_RenderCommand; typedef struct @@ -438,32 +434,58 @@ typedef struct Clay_PointerDataInteractionState state; } Clay_PointerData; +typedef 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_ErrorType; + +typedef struct +{ + Clay_ErrorType errorType; + Clay_String errorText; + uintptr_t userData; +} Clay_ErrorData; + +typedef struct +{ + void (*errorHandlerFunction)(Clay_ErrorData errorText); + uintptr_t userData; +} Clay_ErrorHandler; + // Function Forward Declarations --------------------------------- // Public API functions --- -uint32_t Clay_MinMemorySize(); +uint32_t Clay_MinMemorySize(void); Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset); void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown); -void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions); +void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler); void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime); void Clay_SetLayoutDimensions(Clay_Dimensions dimensions); -void Clay_BeginLayout(); -Clay_RenderCommandArray Clay_EndLayout(); +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); -bool Clay_Hovered(); +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); void Clay_SetCullingEnabled(bool enabled); +void Clay_SetMaxElementCount(uint32_t maxElementCount); +void Clay_SetMaxMeasureTextCacheWordCount(uint32_t maxMeasureTextCacheWordCount); // Internal API functions required by macros -void Clay__OpenElement(); -void Clay__CloseElement(); +void Clay__OpenElement(void); +void Clay__CloseElement(void); Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config); -void Clay__ElementPostConfiguration(); +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); @@ -475,12 +497,11 @@ Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConf Clay_ScrollElementConfig * Clay__StoreScrollElementConfig(Clay_ScrollElementConfig config); Clay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config); Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed); -void Clay__Noop(); +void Clay__Noop(void); void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig); extern Clay_Color Clay__debugViewHighlightColor; extern uint32_t Clay__debugViewWidth; -extern bool Clay__debugMaxElementsLatch; #ifdef __cplusplus } @@ -494,22 +515,6 @@ extern bool Clay__debugMaxElementsLatch; #ifdef CLAY_IMPLEMENTATION #undef CLAY_IMPLEMENTATION -#ifdef CLAY_OVERFLOW_TRAP - #include "signal.h" -#endif - -#ifndef CLAY_MAX_ELEMENT_COUNT -#define CLAY_MAX_ELEMENT_COUNT 8192 -#endif - -#ifndef CLAY__TEXT_MEASURE_HASH_BUCKET_COUNT -#define CLAY__TEXT_MEASURE_HASH_BUCKET_COUNT 128 -#endif - -#ifndef CLAY_MEASURE_TEXT_CACHE_SIZE -#define CLAY_MEASURE_TEXT_CACHE_SIZE CLAY_MAX_ELEMENT_COUNT * 2 -#endif - #ifndef CLAY__NULL #define CLAY__NULL 0 #endif @@ -519,8 +524,12 @@ extern bool Clay__debugMaxElementsLatch; #endif bool Clay__warningsEnabled = true; +uint32_t Clay__maxElementCount = 8192; +uint32_t Clay__maxMeasureTextCacheWordCount = 16384; +void Clay__ErrorHandlerFunctionDefault(Clay_ErrorData errorText) {} +Clay_ErrorHandler Clay__errorHandler = CLAY__INIT(Clay_ErrorHandler) { .errorHandlerFunction = Clay__ErrorHandlerFunctionDefault }; -void Clay__Noop() {}; +void Clay__Noop(void) {} Clay_String CLAY__SPACECHAR = CLAY__INIT(Clay_String) { .length = 1, .chars = " " }; Clay_String CLAY__STRING_DEFAULT = CLAY__INIT(Clay_String) { .length = 0, .chars = NULL }; @@ -529,7 +538,7 @@ typedef struct { bool maxElementsExceeded; bool maxRenderCommandsExceeded; - bool maxStringMeasureCacheExceeded; + bool maxTextMeasureCacheExceeded; } Clay_BooleanWarnings; Clay_BooleanWarnings Clay__booleanWarnings; @@ -553,7 +562,7 @@ typedef struct Clay__WarningArray Clay__WarningArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { size_t totalSizeBytes = capacity * sizeof(Clay_String); Clay__WarningArray array = CLAY__INIT(Clay__WarningArray){.capacity = capacity, .length = 0}; - uintptr_t nextAllocAddress = (uintptr_t)arena->nextAllocation + (uintptr_t)arena->memory; + 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) { @@ -561,9 +570,10 @@ Clay__WarningArray Clay__WarningArray_Allocate_Arena(uint32_t capacity, Clay_Are arena->nextAllocation = arenaOffsetAligned + totalSizeBytes; } else { - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif + Clay__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__errorHandler.userData }); } return array; } @@ -576,18 +586,13 @@ Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning i array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } - else { - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif - } return &CLAY__WARNING_DEFAULT; } void* Clay__Array_Allocate_Arena(uint32_t capacity, uint32_t itemSize, uint32_t alignment, Clay_Arena *arena) { size_t totalSizeBytes = capacity * itemSize; - uintptr_t nextAllocAddress = (uintptr_t)arena->nextAllocation + (uintptr_t)arena->memory; + 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) { @@ -595,12 +600,10 @@ void* Clay__Array_Allocate_Arena(uint32_t capacity, uint32_t itemSize, uint32_t return (void*)((uintptr_t)arena->memory + (uintptr_t)arenaOffsetAligned); } else { - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif + Clay__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__errorHandler.userData }); } return CLAY__NULL; } @@ -610,26 +613,22 @@ bool Clay__Array_RangeCheck(int index, uint32_t length) if (index < length && index >= 0) { return true; } - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Array access out of bounds.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif + Clay__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 = Clay__errorHandler.userData }); return false; } -bool Clay__Array_IncrementCapacityCheck(uint32_t length, uint32_t capacity) +bool Clay__Array_AddCapacityCheck(uint32_t length, uint32_t capacity) { if (length < capacity) { return true; } - if (Clay__warningsEnabled && !Clay__debugMaxElementsLatch) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Attempting to add to array that is already at capacity.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif + Clay__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 = Clay__errorHandler.userData }); return false; } @@ -666,7 +665,7 @@ Clay_ElementId *Clay__ElementIdArray_Get(Clay__ElementIdArray *array, int index) return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__ELEMENT_ID_DEFAULT; } Clay_ElementId *Clay__ElementIdArray_Add(Clay__ElementIdArray *array, Clay_ElementId item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -697,7 +696,7 @@ Clay_ElementConfig *Clay__ElementConfigArray_Get(Clay__ElementConfigArray *array return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__ELEMENT_CONFIG_DEFAULT; } Clay_ElementConfig *Clay__ElementConfigArray_Add(Clay__ElementConfigArray *array, Clay_ElementConfig item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -709,7 +708,7 @@ Clay_ElementConfig *Clay__ElementConfigArraySlice_Get(Clay__ElementConfigArraySl #pragma endregion // __GENERATED__ template -Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = CLAY__INIT(Clay_LayoutConfig){ .sizing = { .width = { .sizeMinMax = {0, CLAY__MAXFLOAT }, .type = CLAY__SIZING_TYPE_FIT }, .height = { .sizeMinMax = {0, CLAY__MAXFLOAT }, .type = CLAY__SIZING_TYPE_FIT } } }; +Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = CLAY__INIT(Clay_LayoutConfig){ .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 @@ -723,7 +722,7 @@ Clay__LayoutConfigArray Clay__LayoutConfigArray_Allocate_Arena(uint32_t capacity 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -746,7 +745,7 @@ Clay__RectangleElementConfigArray Clay__RectangleElementConfigArray_Allocate_Are 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -769,7 +768,7 @@ Clay__TextElementConfigArray Clay__TextElementConfigArray_Allocate_Arena(uint32_ 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -792,7 +791,7 @@ Clay__ImageElementConfigArray Clay__ImageElementConfigArray_Allocate_Arena(uint3 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -815,7 +814,7 @@ Clay__FloatingElementConfigArray Clay__FloatingElementConfigArray_Allocate_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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -838,7 +837,7 @@ Clay__CustomElementConfigArray Clay__CustomElementConfigArray_Allocate_Arena(uin 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -861,7 +860,7 @@ Clay__ScrollElementConfigArray Clay__ScrollElementConfigArray_Allocate_Arena(uin 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -881,7 +880,7 @@ Clay__StringArray Clay__StringArray_Allocate_Arena(uint32_t capacity, Clay_Arena return CLAY__INIT(Clay__StringArray){.capacity = capacity, .length = 0, .internalArray = (Clay_String *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_String), CLAY__ALIGNMENT(Clay_String), arena)}; } Clay_String *Clay__StringArray_Add(Clay__StringArray *array, Clay_String item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -890,12 +889,48 @@ Clay_String *Clay__StringArray_Add(Clay__StringArray *array, Clay_String item) { #pragma endregion // __GENERATED__ template +typedef struct { + Clay_Dimensions dimensions; + Clay_String line; +} Clay__WrappedTextLine; + +Clay__WrappedTextLine CLAY__WRAPPED_TEXT_LINE_DEFAULT = CLAY__INIT(Clay__WrappedTextLine) {}; + +// __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 +typedef struct +{ + uint32_t capacity; + uint32_t length; + Clay__WrappedTextLine *internalArray; +} Clay__WrappedTextLineArray; +typedef struct +{ + uint32_t length; + Clay__WrappedTextLine *internalArray; +} Clay__WrappedTextLineArraySlice; +Clay__WrappedTextLineArray Clay__WrappedTextLineArray_Allocate_Arena(uint32_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, int index) { + return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__WRAPPED_TEXT_LINE_DEFAULT; +} +#pragma endregion +// __GENERATED__ template + typedef struct { Clay_String text; Clay_Dimensions preferredDimensions; uint32_t elementIndex; - Clay__StringArraySlice wrappedLines; + Clay__WrappedTextLineArraySlice wrappedLines; } Clay__TextElementData; Clay__TextElementData CLAY__TEXT_ELEMENT_DATA_DEFAULT = CLAY__INIT(Clay__TextElementData) {}; @@ -915,7 +950,7 @@ Clay__TextElementData *Clay__TextElementDataArray_Get(Clay__TextElementDataArray return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__TEXT_ELEMENT_DATA_DEFAULT; } Clay__TextElementData *Clay__TextElementDataArray_Add(Clay__TextElementDataArray *array, Clay__TextElementData item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -938,7 +973,7 @@ Clay__BorderElementConfigArray Clay__BorderElementConfigArray_Allocate_Arena(uin 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -955,9 +990,6 @@ typedef struct typedef struct { - #ifdef CLAY_DEBUG - Clay_String name; - #endif union { Clay__LayoutElementChildren children; Clay__TextElementData *textElementData; @@ -984,7 +1016,7 @@ Clay_LayoutElementArray Clay_LayoutElementArray_Allocate_Arena(uint32_t capacity 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1008,7 +1040,7 @@ Clay__LayoutElementPointerArray Clay__LayoutElementPointerArray_Allocate_Arena(u return CLAY__INIT(Clay__LayoutElementPointerArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElement* *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElement*), CLAY__ALIGNMENT(Clay_LayoutElement*), arena)}; } Clay_LayoutElement* *Clay__LayoutElementPointerArray_Add(Clay__LayoutElementPointerArray *array, Clay_LayoutElement* item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1037,13 +1069,13 @@ Clay_RenderCommandArray Clay_RenderCommandArray_Allocate_Arena(uint32_t capacity 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_IncrementCapacityCheck(array->length, array->capacity)) { + 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) { +Clay_RenderCommand *Clay_RenderCommandArray_Get(Clay_RenderCommandArray *array, int index) { return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__RENDER_COMMAND_DEFAULT; } #pragma endregion @@ -1079,7 +1111,7 @@ Clay__ScrollContainerDataInternalArray Clay__ScrollContainerDataInternalArray_Al 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1125,7 +1157,7 @@ Clay__DebugElementDataArray Clay__DebugElementDataArray_Allocate_Arena(uint32_t 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1166,7 +1198,7 @@ Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Get(Clay__Lay 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1200,20 +1232,13 @@ Clay__MeasuredWord *Clay__MeasuredWordArray_Get(Clay__MeasuredWordArray *array, return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__MEASURED_WORD_DEFAULT; } void Clay__MeasuredWordArray_Set(Clay__MeasuredWordArray *array, int index, Clay__MeasuredWord value) { - if (index < array->capacity && index >= 0) { + if (Clay__Array_RangeCheck(index, array->capacity)) { array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; - } else { - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif } } Clay__MeasuredWord *Clay__MeasuredWordArray_Add(Clay__MeasuredWordArray *array, Clay__MeasuredWord item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1249,23 +1274,16 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCacheItemArray_Get(Clay__MeasureTex 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_IncrementCapacityCheck(array->length, array->capacity)) { + 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, int index, Clay__MeasureTextCacheItem value) { - if (index < array->capacity && index >= 0) { + if (Clay__Array_RangeCheck(index, array->capacity)) { array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; - } else { - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif } } #pragma endregion @@ -1286,21 +1304,14 @@ int32_t Clay__int32_tArray_Get(Clay__int32_tArray *array, int 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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; } } void Clay__int32_tArray_Set(Clay__int32_tArray *array, int index, int32_t value) { - if (index < array->capacity && index >= 0) { + if (Clay__Array_RangeCheck(index, array->capacity)) { array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; - } else { - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif } } int32_t Clay__int32_tArray_RemoveSwapback(Clay__int32_tArray *array, int index) { @@ -1336,7 +1347,7 @@ Clay__LayoutElementTreeNodeArray Clay__LayoutElementTreeNodeArray_Allocate_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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1371,7 +1382,7 @@ Clay__LayoutElementTreeRootArray Clay__LayoutElementTreeRootArray_Allocate_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_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } @@ -1416,7 +1427,6 @@ bool Clay__externalScrollHandlingEnabled = false; uint32_t Clay__debugSelectedElementId = 0; uint32_t Clay__debugViewWidth = 400; Clay_Color Clay__debugViewHighlightColor = CLAY__INIT(Clay_Color) { 168, 66, 28, 100 }; -bool Clay__debugMaxElementsLatch = false; uint32_t Clay__generation = 0; uint64_t Clay__arenaResetOffset = 0; Clay_Arena Clay__internalArena; @@ -1443,7 +1453,7 @@ Clay__CustomElementConfigArray Clay__customElementConfigs; Clay__BorderElementConfigArray Clay__borderElementConfigs; // Misc Data Structures Clay__StringArray Clay__layoutElementIdStrings; -Clay__StringArray Clay__wrappedTextLines; +Clay__WrappedTextLineArray Clay__wrappedTextLines; Clay__LayoutElementTreeNodeArray Clay__layoutElementTreeNodeArray1; Clay__LayoutElementTreeRootArray Clay__layoutElementTreeRoots; Clay__LayoutElementHashMapItemArray Clay__layoutElementsHashMapInternal; @@ -1596,8 +1606,17 @@ Clay__MeasuredWord *Clay__AddMeasuredWord(Clay__MeasuredWord word, Clay__Measure } Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) { + #ifndef CLAY_WASM + if (!Clay__MeasureText) { + Clay__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 = Clay__errorHandler.userData }); + return NULL; + } + #endif uint32_t id = Clay__HashTextWithConfig(text, config); - uint32_t hashBucket = id % CLAY__TEXT_MEASURE_HASH_BUCKET_COUNT; + uint32_t hashBucket = id % (Clay__maxMeasureTextCacheWordCount / 32); int32_t elementIndexPrevious = 0; int32_t elementIndex = Clay__measureTextHashMap.internalArray[hashBucket]; while (elementIndex != 0) { @@ -1642,6 +1661,13 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text measured = Clay__MeasureTextCacheItemArray_Get(&Clay__measureTextHashMapInternal, newItemIndex); } else { if (Clay__measureTextHashMapInternal.length == Clay__measureTextHashMapInternal.capacity - 1) { + if (Clay__booleanWarnings.maxTextMeasureCacheExceeded) { + Clay__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 = Clay__errorHandler.userData }); + Clay__booleanWarnings.maxTextMeasureCacheExceeded = true; + } return &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT; } measured = Clay__MeasureTextCacheItemArray_Add(&Clay__measureTextHashMapInternal, newCacheItem); @@ -1657,6 +1683,13 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text Clay__MeasuredWord *previousWord = &tempWord; while (end < text->length) { if (Clay__measuredWords.length == Clay__measuredWords.capacity - 1) { + if (!Clay__booleanWarnings.maxTextMeasureCacheExceeded) { + Clay__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 = Clay__errorHandler.userData }); + Clay__booleanWarnings.maxTextMeasureCacheExceeded = true; + } return &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT; } char current = text->chars[end]; @@ -1720,9 +1753,10 @@ Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Cl hashItem->layoutElement = layoutElement; hashItem->debugData->collision = false; } else { // Multiple collisions this frame - two elements have the same ID - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Duplicate ID detected for element: "), Clay__WriteStringToCharBuffer(&Clay__dynamicStringData, elementId.stringId) }); - } + Clay__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 = Clay__errorHandler.userData }); if (Clay__debugModeEnabled) { hashItem->debugData->collision = true; } @@ -1761,13 +1795,10 @@ void Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) openLayoutElement->id = elementId.id; Clay__AddHashMapItem(elementId, openLayoutElement); Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); - #ifdef CLAY_DEBUG - openLayoutElement->name = elementId.stringId; - #endif } -void Clay__ElementPostConfiguration() { - if (Clay__debugMaxElementsLatch) { +void Clay__ElementPostConfiguration(void) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); @@ -1809,7 +1840,10 @@ void Clay__ElementPostConfiguration() { Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig->parentId); clipElementId = Clay__int32_tArray_Get(&Clay__layoutElementClipElementIds, parentItem->layoutElement - Clay__layoutElements.internalArray); if (!parentItem) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Clay Warning: Couldn't find parent container to attach floating container to.") }); + Clay__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 = Clay__errorHandler.userData }); } } Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { @@ -1851,8 +1885,8 @@ void Clay__ElementPostConfiguration() { Clay__elementConfigBuffer.length -= openLayoutElement->elementConfigs.length; } -void Clay__CloseElement() { - if (Clay__debugMaxElementsLatch) { +void Clay__CloseElement(void) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); @@ -1913,22 +1947,22 @@ void Clay__CloseElement() { // Clamp element min and max width to the values configured in the layout if (layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) { - if (layoutConfig->sizing.width.sizeMinMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier - layoutConfig->sizing.width.sizeMinMax.max = CLAY__MAXFLOAT; + 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.sizeMinMax.min), layoutConfig->sizing.width.sizeMinMax.max); - openLayoutElement->minDimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.width, layoutConfig->sizing.width.sizeMinMax.min), layoutConfig->sizing.width.sizeMinMax.max); + 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.sizeMinMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier - layoutConfig->sizing.height.sizeMinMax.max = CLAY__MAXFLOAT; + 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.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); - openLayoutElement->minDimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.height, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); + 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; } @@ -1945,9 +1979,9 @@ void Clay__CloseElement() { } } -void Clay__OpenElement() { - if (Clay__layoutElements.length == Clay__layoutElements.capacity - 1 || Clay__debugMaxElementsLatch) { - Clay__debugMaxElementsLatch = true; +void Clay__OpenElement(void) { + if (Clay__layoutElements.length == Clay__layoutElements.capacity - 1 || Clay__booleanWarnings.maxElementsExceeded) { + Clay__booleanWarnings.maxElementsExceeded = true; return; } Clay_LayoutElement layoutElement = CLAY__INIT(Clay_LayoutElement) {}; @@ -1961,8 +1995,8 @@ void Clay__OpenElement() { } void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig) { - if (Clay__layoutElements.length == Clay__layoutElements.capacity - 1 || Clay__debugMaxElementsLatch) { - Clay__debugMaxElementsLatch = true; + if (Clay__layoutElements.length == Clay__layoutElements.capacity - 1 || Clay__booleanWarnings.maxElementsExceeded) { + Clay__booleanWarnings.maxElementsExceeded = true; return; } Clay_LayoutElement *parentElement = Clay__GetOpenLayoutElement(); @@ -1974,9 +2008,6 @@ void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig) Clay__MeasureTextCacheItem *textMeasured = Clay__MeasureTextCached(&text, textConfig); Clay_ElementId elementId = Clay__HashString(CLAY_STRING("Text"), parentElement->children.length, parentElement->id); openLayoutElement->id = elementId.id; - #ifdef CLAY_DEBUG - openLayoutElement->name = CLAY_STRING("Text"); - #endif Clay__AddHashMapItem(elementId, openLayoutElement); Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); Clay_Dimensions textDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? textConfig->lineHeight : textMeasured->unwrappedDimensions.height }; @@ -1997,50 +2028,50 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { // Ephemeral Memory - reset every frame Clay__internalArena.nextAllocation = Clay__arenaResetOffset; - Clay__layoutElementChildrenBuffer = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElements = Clay_LayoutElementArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementChildrenBuffer = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__layoutElements = Clay_LayoutElementArray_Allocate_Arena(Clay__maxElementCount, arena); Clay_warnings = Clay__WarningArray_Allocate_Arena(100, arena); - Clay__layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__elementConfigBuffer = Clay__ElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__elementConfigs = Clay__ElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__rectangleElementConfigs = Clay__RectangleElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__scrollElementConfigs = Clay__ScrollElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__customElementConfigs = Clay__CustomElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__elementConfigBuffer = Clay__ElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__elementConfigs = Clay__ElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__rectangleElementConfigs = Clay__RectangleElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__scrollElementConfigs = Clay__ScrollElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__customElementConfigs = Clay__CustomElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(Clay__maxElementCount, arena); - Clay__layoutElementIdStrings = Clay__StringArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__wrappedTextLines = Clay__StringArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementChildren = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__textElementData = Clay__TextElementDataArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__imageElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__renderCommands = Clay_RenderCommandArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__treeNodeVisited = Clay__BoolArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementIdStrings = Clay__StringArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__wrappedTextLines = Clay__WrappedTextLineArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__layoutElementChildren = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__textElementData = Clay__TextElementDataArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__imageElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__renderCommands = Clay_RenderCommandArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__treeNodeVisited = Clay__BoolArray_Allocate_Arena(Clay__maxElementCount, arena); Clay__treeNodeVisited.length = Clay__treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list - Clay__openClipElementStack = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__reusableElementIndexBuffer = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementClipElementIds = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__dynamicStringData = Clay__CharArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__openClipElementStack = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__reusableElementIndexBuffer = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__layoutElementClipElementIds = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__dynamicStringData = Clay__CharArray_Allocate_Arena(Clay__maxElementCount, arena); } void Clay__InitializePersistentMemory(Clay_Arena *arena) { // Persistent memory - initialized once and not reset Clay__scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(10, arena); - Clay__layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__measureTextHashMapInternalFreeList = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__measuredWordsFreeList = Clay__int32_tArray_Allocate_Arena(CLAY_MEASURE_TEXT_CACHE_SIZE, arena); - Clay__measureTextHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__measuredWords = Clay__MeasuredWordArray_Allocate_Arena(CLAY_MEASURE_TEXT_CACHE_SIZE, arena); - Clay__pointerOverIds = Clay__ElementIdArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__debugElementData = Clay__DebugElementDataArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); + Clay__layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__measureTextHashMapInternalFreeList = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__measuredWordsFreeList = Clay__int32_tArray_Allocate_Arena(Clay__maxMeasureTextCacheWordCount, arena); + Clay__measureTextHashMap = Clay__int32_tArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__measuredWords = Clay__MeasuredWordArray_Allocate_Arena(Clay__maxMeasureTextCacheWordCount, arena); + Clay__pointerOverIds = Clay__ElementIdArray_Allocate_Arena(Clay__maxElementCount, arena); + Clay__debugElementData = Clay__DebugElementDataArray_Allocate_Arena(Clay__maxElementCount, arena); Clay__arenaResetOffset = arena->nextAllocation; } @@ -2069,7 +2100,7 @@ float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height; bool canDistribute = true; - if ((sizeToDistribute < 0 && *childSize == childSizing.sizeMinMax.min) || (sizeToDistribute > 0 && *childSize == childSizing.sizeMinMax.max)) { + if ((sizeToDistribute < 0 && *childSize == childSizing.size.minMax.min) || (sizeToDistribute > 0 && *childSize == childSizing.size.minMax.max)) { canDistribute = false; } // Currently, we don't support squishing aspect ratio images on their Y axis as it would break ratio @@ -2100,7 +2131,7 @@ float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay } float oldChildSize = *childSize; - *childSize = CLAY__MAX(CLAY__MAX(CLAY__MIN(childSizing.sizeMinMax.max, *childSize + dividedSize), childSizing.sizeMinMax.min), childMinSize); + *childSize = CLAY__MAX(CLAY__MAX(CLAY__MIN(childSizing.size.minMax.max, *childSize + dividedSize), childSizing.size.minMax.min), childMinSize); float diff = *childSize - oldChildSize; if (diff > -0.01 && diff < 0.01) { Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); @@ -2137,8 +2168,8 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { } } - rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.sizeMinMax.min), rootElement->layoutConfig->sizing.width.sizeMinMax.max); - rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.sizeMinMax.min), rootElement->layoutConfig->sizing.height.sizeMinMax.max); + 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 (int i = 0; i < bfsBuffer.length; ++i) { int32_t parentIndex = Clay__int32_tArray_Get(&bfsBuffer, i); @@ -2188,7 +2219,7 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; if (childSizing.type == CLAY__SIZING_TYPE_PERCENT) { - *childSize = (parentSize - totalPaddingAndChildGaps) * childSizing.sizePercent; + *childSize = (parentSize - totalPaddingAndChildGaps) * childSizing.size.percent; if (sizingAlongAxis) { innerContentSize += *childSize; if (childOffset > 0) { @@ -2260,9 +2291,9 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { } } if (childSizing.type == CLAY__SIZING_TYPE_FIT) { - *childSize = CLAY__MAX(childSizing.sizeMinMax.min, CLAY__MIN(*childSize, maxSize)); + *childSize = CLAY__MAX(childSizing.size.minMax.min, CLAY__MIN(*childSize, maxSize)); } else if (childSizing.type == CLAY__SIZING_TYPE_GROW) { - *childSize = CLAY__MIN(maxSize, childSizing.sizeMinMax.max); + *childSize = CLAY__MIN(maxSize, childSizing.size.minMax.max); } } } @@ -2304,7 +2335,13 @@ void Clay__AddRenderCommand(Clay_RenderCommand renderCommand) { if (Clay__renderCommands.length < Clay__renderCommands.capacity - 1) { Clay_RenderCommandArray_Add(&Clay__renderCommands, renderCommand); } else { - Clay__booleanWarnings.maxRenderCommandsExceeded = true; + if (!Clay__booleanWarnings.maxRenderCommandsExceeded) { + Clay__booleanWarnings.maxRenderCommandsExceeded = true; + Clay__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 = Clay__errorHandler.userData }); + } } } @@ -2326,7 +2363,7 @@ void Clay__CalculateFinalLayout() { // Wrap text for (int textElementIndex = 0; textElementIndex < Clay__textElementData.length; ++textElementIndex) { Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&Clay__textElementData, textElementIndex); - textElementData->wrappedLines = CLAY__INIT(Clay__StringArraySlice) { .length = 0, .internalArray = &Clay__wrappedTextLines.internalArray[Clay__wrappedTextLines.length] }; + textElementData->wrappedLines = CLAY__INIT(Clay__WrappedTextLineArraySlice) { .length = 0, .internalArray = &Clay__wrappedTextLines.internalArray[Clay__wrappedTextLines.length] }; Clay_LayoutElement *containerElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, (int)textElementData->elementIndex); Clay_TextElementConfig *textConfig = Clay__FindElementConfigWithType(containerElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig; Clay__MeasureTextCacheItem *measureTextCacheItem = Clay__MeasureTextCached(&textElementData->text, textConfig); @@ -2335,27 +2372,26 @@ void Clay__CalculateFinalLayout() { uint32_t lineLengthChars = 0; uint32_t lineStartOffset = 0; if (textElementData->preferredDimensions.width <= containerElement->dimensions.width) { - Clay__StringArray_Add(&Clay__wrappedTextLines, textElementData->text); + Clay__WrappedTextLineArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { containerElement->dimensions, textElementData->text }); textElementData->wrappedLines.length++; continue; } int32_t wordIndex = measureTextCacheItem->measuredWordsStartIndex; while (wordIndex != -1) { if (Clay__wrappedTextLines.length > Clay__wrappedTextLines.capacity - 1) { - Clay__booleanWarnings.maxStringMeasureCacheExceeded = true; break; } Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&Clay__measuredWords, wordIndex); // Only word on the line is too large, just render it anyway if (lineLengthChars == 0 && lineWidth + measuredWord->width > containerElement->dimensions.width) { - Clay__StringArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay_String) {.length = (int)measuredWord->length, .chars = &textElementData->text.chars[measuredWord->startOffset] }); + Clay__WrappedTextLineArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { CLAY__INIT(Clay_Dimensions) { measuredWord->width, lineHeight }, CLAY__INIT(Clay_String){ .length = (int)measuredWord->length, .chars = &textElementData->text.chars[measuredWord->startOffset] } }); textElementData->wrappedLines.length++; wordIndex = measuredWord->next; } // 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__StringArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay_String) {.length = (int)lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] }); + Clay__WrappedTextLineArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { CLAY__INIT(Clay_Dimensions) { lineWidth, lineHeight }, CLAY__INIT(Clay_String){ .length = (int)lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } }); textElementData->wrappedLines.length++; if (lineLengthChars == 0 || measuredWord->length == 0) { wordIndex = measuredWord->next; @@ -2370,7 +2406,7 @@ void Clay__CalculateFinalLayout() { } } if (lineLengthChars > 0) { - Clay__StringArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay_String) {.length = (int)lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] }); + Clay__WrappedTextLineArray_Add(&Clay__wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { CLAY__INIT(Clay_Dimensions) { lineWidth, lineHeight }, CLAY__INIT(Clay_String) {.length = (int)lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } }); textElementData->wrappedLines.length++; } containerElement->dimensions.height = lineHeight * textElementData->wrappedLines.length; @@ -2420,7 +2456,7 @@ void Clay__CalculateFinalLayout() { for (int j = 0; j < currentElement->children.length; ++j) { Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, currentElement->children.elements[j]); float childHeightWithPadding = CLAY__MAX(childElement->dimensions.height + layoutConfig->padding.y * 2, currentElement->dimensions.height); - currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(childHeightWithPadding, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); + 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 @@ -2430,7 +2466,7 @@ void Clay__CalculateFinalLayout() { contentHeight += childElement->dimensions.height; } contentHeight += (float)(CLAY__MAX(currentElement->children.length - 1, 0) * layoutConfig->childGap); - currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(contentHeight, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max); + currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(contentHeight, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max); } } @@ -2648,15 +2684,15 @@ void Clay__CalculateFinalLayout() { float lineHeightOffset = (finalLineHeight - naturalLineHeight) / 2; float yPosition = lineHeightOffset; for (int lineIndex = 0; lineIndex < currentElement->textElementData->wrappedLines.length; ++lineIndex) { - Clay_String wrappedLine = currentElement->textElementData->wrappedLines.internalArray[lineIndex]; // todo range check - if (wrappedLine.length == 0) { + Clay__WrappedTextLine wrappedLine = currentElement->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, currentElement->dimensions.width, naturalLineHeight }, // TODO width + .boundingBox = { currentElementBoundingBox.x, currentElementBoundingBox.y + yPosition, wrappedLine.dimensions.width, wrappedLine.dimensions.height }, // TODO width .config = configUnion, - .text = wrappedLine, + .text = wrappedLine.line, .id = Clay__HashNumber(lineIndex, currentElement->id).id, .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT, }); @@ -2855,40 +2891,37 @@ void Clay__CalculateFinalLayout() { } void Clay__AttachId(Clay_ElementId elementId) { - if (Clay__debugMaxElementsLatch) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); openLayoutElement->id = elementId.id; Clay__AddHashMapItem(elementId, openLayoutElement); Clay__StringArray_Add(&Clay__layoutElementIdStrings, elementId.stringId); - #ifdef CLAY_DEBUG - openLayoutElement->name = elementId.stringId; - #endif } void Clay__AttachLayoutConfig(Clay_LayoutConfig *config) { - if (Clay__debugMaxElementsLatch) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay__GetOpenLayoutElement()->layoutConfig = config; } void Clay__AttachElementConfig(Clay_ElementConfigUnion config, Clay__ElementConfigType type) { - if (Clay__debugMaxElementsLatch) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); openLayoutElement->elementConfigs.length++; Clay__ElementConfigArray_Add(&Clay__elementConfigBuffer, CLAY__INIT(Clay_ElementConfig) { .type = type, .config = config }); } -Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay__debugMaxElementsLatch ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay__layoutConfigs, config); } -Clay_RectangleElementConfig * Clay__StoreRectangleElementConfig(Clay_RectangleElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT : Clay__RectangleElementConfigArray_Add(&Clay__rectangleElementConfigs, config); } -Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__TEXT_ELEMENT_CONFIG_DEFAULT : Clay__TextElementConfigArray_Add(&Clay__textElementConfigs, config); } -Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay__imageElementConfigs, config); } -Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay__floatingElementConfigs, config); } -Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay__customElementConfigs, config); } -Clay_ScrollElementConfig * Clay__StoreScrollElementConfig(Clay_ScrollElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__SCROLL_ELEMENT_CONFIG_DEFAULT : Clay__ScrollElementConfigArray_Add(&Clay__scrollElementConfigs, config); } -Clay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config) { return Clay__debugMaxElementsLatch ? &CLAY__BORDER_ELEMENT_CONFIG_DEFAULT : Clay__BorderElementConfigArray_Add(&Clay__borderElementConfigs, config); } +Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay__layoutConfigs, config); } +Clay_RectangleElementConfig * Clay__StoreRectangleElementConfig(Clay_RectangleElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT : Clay__RectangleElementConfigArray_Add(&Clay__rectangleElementConfigs, config); } +Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__TEXT_ELEMENT_CONFIG_DEFAULT : Clay__TextElementConfigArray_Add(&Clay__textElementConfigs, config); } +Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay__imageElementConfigs, config); } +Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay__floatingElementConfigs, config); } +Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay__customElementConfigs, config); } +Clay_ScrollElementConfig * Clay__StoreScrollElementConfig(Clay_ScrollElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__SCROLL_ELEMENT_CONFIG_DEFAULT : Clay__ScrollElementConfigArray_Add(&Clay__scrollElementConfigs, config); } +Clay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config) { return Clay__booleanWarnings.maxElementsExceeded ? &CLAY__BORDER_ELEMENT_CONFIG_DEFAULT : Clay__BorderElementConfigArray_Add(&Clay__borderElementConfigs, config); } #pragma region DebugTools Clay_Color CLAY__DEBUGVIEW_COLOR_1 = CLAY__INIT(Clay_Color) {58, 56, 52, 255}; @@ -3081,16 +3114,16 @@ void Clay__RenderDebugLayoutSizing(Clay_SizingAxis sizing, Clay_TextElementConfi CLAY_TEXT(sizingLabel, infoTextConfig); if (sizing.type == CLAY__SIZING_TYPE_GROW || sizing.type == CLAY__SIZING_TYPE_FIT) { CLAY_TEXT(CLAY_STRING("("), infoTextConfig); - if (sizing.sizeMinMax.min != 0) { + if (sizing.size.minMax.min != 0) { CLAY_TEXT(CLAY_STRING("min: "), infoTextConfig); - CLAY_TEXT(Clay__IntToString(sizing.sizeMinMax.min), infoTextConfig); - if (sizing.sizeMinMax.max != CLAY__MAXFLOAT) { + CLAY_TEXT(Clay__IntToString(sizing.size.minMax.min), infoTextConfig); + if (sizing.size.minMax.max != CLAY__MAXFLOAT) { CLAY_TEXT(CLAY_STRING(", "), infoTextConfig); } } - if (sizing.sizeMinMax.max != CLAY__MAXFLOAT) { + if (sizing.size.minMax.max != CLAY__MAXFLOAT) { CLAY_TEXT(CLAY_STRING("max: "), infoTextConfig); - CLAY_TEXT(Clay__IntToString(sizing.sizeMinMax.max), infoTextConfig); + CLAY_TEXT(Clay__IntToString(sizing.size.minMax.max), infoTextConfig); } CLAY_TEXT(CLAY_STRING(")"), infoTextConfig); } @@ -3184,7 +3217,9 @@ void Clay__RenderDebugView() { break; } } - int32_t highlightedRow = (int32_t)((Clay__pointerInfo.position.y - scrollYOffset) / (float)CLAY__DEBUGVIEW_ROW_HEIGHT) - 1; + int32_t highlightedRow = Clay__pointerInfo.position.y < Clay__layoutDimensions.height - 300 + ? (int32_t)((Clay__pointerInfo.position.y - scrollYOffset) / (float)CLAY__DEBUGVIEW_ROW_HEIGHT) - 1 + : -1; if (Clay__pointerInfo.position.x < Clay__layoutDimensions.width - (float)Clay__debugViewWidth) { highlightedRow = -1; } @@ -3211,7 +3246,7 @@ void Clay__RenderDebugView() { CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})}, .layoutDirection = CLAY_TOP_TO_BOTTOM }), CLAY_RECTANGLE({ .color = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1 })) { Clay_ElementId panelContentsId = Clay__HashString(CLAY_STRING("Clay__DebugViewPaneOuter"), 0, 0); // Element list - CLAY(Clay__AttachId(panelContentsId), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})} }), CLAY_FLOATING({})) { + CLAY(Clay__AttachId(panelContentsId), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})} }), CLAY_FLOATING({ .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH })) { CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW({}), CLAY_SIZING_GROW({})}, .padding = {.x = CLAY__DEBUGVIEW_OUTER_PADDING }, .layoutDirection = CLAY_TOP_TO_BOTTOM })) { layoutData = Clay__RenderDebugLayoutElementsList((int32_t)initialRootsLength, highlightedRow); } @@ -3479,8 +3514,8 @@ void Clay__RenderDebugView() { // PUBLIC API FROM HERE --------------------------------------- CLAY_WASM_EXPORT("Clay_MinMemorySize") -uint32_t Clay_MinMemorySize() { - Clay_Arena fakeArena = CLAY__INIT(Clay_Arena) { .capacity = INT64_MAX }; +uint32_t Clay_MinMemorySize(void) { + Clay_Arena fakeArena = CLAY__INIT(Clay_Arena) { .capacity = SIZE_MAX }; Clay__InitializePersistentMemory(&fakeArena); Clay__InitializeEphemeralMemory(&fakeArena); return fakeArena.nextAllocation; @@ -3499,9 +3534,6 @@ Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_String *text, Clay_TextElementConfig *config)) { Clay__MeasureText = measureTextFunction; } -#endif - -#ifndef CLAY_WASM void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId)) { Clay__QueryScrollOffset = queryScrollOffsetFunction; } @@ -3514,7 +3546,7 @@ void Clay_SetLayoutDimensions(Clay_Dimensions dimensions) { CLAY_WASM_EXPORT("Clay_SetPointerState") void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) { - if (Clay__debugMaxElementsLatch) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay__pointerInfo.position = position; @@ -3581,7 +3613,7 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) { } CLAY_WASM_EXPORT("Clay_Initialize") -void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions) { +void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler) { Clay__internalArena = arena; Clay__InitializePersistentMemory(&Clay__internalArena); Clay__InitializeEphemeralMemory(&Clay__internalArena); @@ -3593,6 +3625,7 @@ void Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions) { } Clay__measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element" Clay__layoutDimensions = layoutDimensions; + Clay__errorHandler = errorHandler; } CLAY_WASM_EXPORT("Clay_UpdateScrollContainers") @@ -3709,7 +3742,7 @@ void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDe } CLAY_WASM_EXPORT("Clay_BeginLayout") -void Clay_BeginLayout() { +void Clay_BeginLayout(void) { Clay__InitializeEphemeralMemory(&Clay__internalArena); Clay__generation++; Clay__dynamicElementIndex = 0; @@ -3718,9 +3751,9 @@ void Clay_BeginLayout() { if (Clay__debugModeEnabled) { rootDimensions.width -= (float)Clay__debugViewWidth; } - if (Clay__debugMaxElementsLatch) { - return; - } + Clay__booleanWarnings.maxElementsExceeded = false; + Clay__booleanWarnings.maxTextMeasureCacheExceeded = false; + Clay__booleanWarnings.maxRenderCommandsExceeded = false; Clay__OpenElement(); CLAY_ID("Clay__RootContainer"); CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)} }); @@ -3732,20 +3765,15 @@ void Clay_BeginLayout() { Clay_TextElementConfig Clay__DebugView_ErrorTextConfig = CLAY__INIT(Clay_TextElementConfig) {.textColor = {255, 0, 0, 255}, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }; CLAY_WASM_EXPORT("Clay_EndLayout") -Clay_RenderCommandArray Clay_EndLayout() -{ +Clay_RenderCommandArray Clay_EndLayout(void) { Clay__CloseElement(); if (Clay__debugModeEnabled) { - #ifndef CLAY_DEBUG Clay__warningsEnabled = false; - #endif Clay__RenderDebugView(); - #ifndef CLAY_DEBUG Clay__warningsEnabled = true; - #endif } - if (Clay__debugMaxElementsLatch) { - Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand ) { .boundingBox = { Clay__layoutDimensions.width / 2 - 59 * 4, Clay__layoutDimensions.height / 2 }, .config = { .textElementConfig = &Clay__DebugView_ErrorTextConfig }, .text = CLAY_STRING("Clay Error: Layout elements exceeded CLAY_MAX_ELEMENT_COUNT"), .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT }); + if (Clay__booleanWarnings.maxElementsExceeded) { + Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand ) { .boundingBox = { Clay__layoutDimensions.width / 2 - 59 * 4, Clay__layoutDimensions.height / 2 }, .config = { .textElementConfig = &Clay__DebugView_ErrorTextConfig }, .text = CLAY_STRING("Clay Error: Layout elements exceeded Clay__maxElementCount"), .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT }); } else { Clay__CalculateFinalLayout(); } @@ -3762,8 +3790,8 @@ Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index) return Clay__HashString(idString, index, 0); } -bool Clay_Hovered() { - if (Clay__debugMaxElementsLatch) { +bool Clay_Hovered(void) { + if (Clay__booleanWarnings.maxElementsExceeded) { return false; } Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); @@ -3780,7 +3808,7 @@ bool Clay_Hovered() { } void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerInfo, intptr_t userData), intptr_t userData) { - if (Clay__debugMaxElementsLatch) { + if (Clay__booleanWarnings.maxElementsExceeded) { return; } Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); @@ -3834,6 +3862,16 @@ void Clay_SetExternalScrollHandlingEnabled(bool enabled) { Clay__externalScrollHandlingEnabled = enabled; } +CLAY_WASM_EXPORT("Clay_SetMaxElementCount") +void Clay_SetMaxElementCount(uint32_t maxElementCount) { + Clay__maxElementCount = maxElementCount; +} + +CLAY_WASM_EXPORT("Clay_SetMaxMeasureTextCacheWordCount") +void Clay_SetMaxMeasureTextCacheWordCount(uint32_t maxMeasureTextCacheWordCount) { + Clay__maxMeasureTextCacheWordCount = maxMeasureTextCacheWordCount; +} + #endif //CLAY_IMPLEMENTATION /* diff --git a/examples/SDL2-video-demo/CMakeLists.txt b/examples/SDL2-video-demo/CMakeLists.txt new file mode 100644 index 0000000..b058ddb --- /dev/null +++ b/examples/SDL2-video-demo/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.27) +project(SDL2_video_demo C) +set(CMAKE_C_STANDARD 99) + +include(FetchContent) +set(FETCHCONTENT_QUIET FALSE) + +FetchContent_Declare( + SDL2 + GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git" + GIT_TAG "release-2.30.10" + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(SDL2) + +FetchContent_Declare( + SDL2_ttf + GIT_REPOSITORY "https://github.com/libsdl-org/SDL_ttf.git" + GIT_TAG "release-2.22.0" + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(SDL2_ttf) + +add_executable(SDL2_video_demo main.c) + +target_compile_options(SDL2_video_demo PUBLIC) +target_include_directories(SDL2_video_demo PUBLIC .) + +target_link_libraries(SDL2_video_demo PUBLIC + SDL2::SDL2main + SDL2::SDL2-static + SDL2_ttf::SDL2_ttf-static +) +set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Werror -DCLAY_DEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +add_custom_command( + TARGET SDL2_video_demo POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/resources + ${CMAKE_CURRENT_BINARY_DIR}/resources) diff --git a/examples/SDL2-video-demo/main.c b/examples/SDL2-video-demo/main.c new file mode 100644 index 0000000..ab0e889 --- /dev/null +++ b/examples/SDL2-video-demo/main.c @@ -0,0 +1,360 @@ +#define CLAY_IMPLEMENTATION +#include "../../clay.h" +#include "../../renderers/SDL2/clay_renderer_SDL2.c" + +#include +#include + +#include +#include +#include +#include + + +const int FONT_ID_BODY_16 = 0; +Clay_Color COLOR_WHITE = { 255, 255, 255, 255}; + +void RenderHeaderButton(Clay_String text) { + CLAY( + CLAY_LAYOUT({ .padding = { 16, 8 }}), + CLAY_RECTANGLE({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = 5 + }) + ) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + .textColor = { 255, 255, 255, 255 } + })); + } +} + +void RenderDropdownMenuItem(Clay_String text) { + CLAY(CLAY_LAYOUT({ .padding = { 16, 16 }})) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + .textColor = { 255, 255, 255, 255 } + })); + } +} + +typedef struct { + Clay_String title; + Clay_String contents; +} Document; + +typedef struct { + Document *documents; + uint32_t length; +} DocumentArray; + +DocumentArray documents = { + .documents = NULL, + .length = 5 +}; + +uint32_t selectedDocumentIndex = 0; + +void HandleSidebarInteraction( + Clay_ElementId elementId, + Clay_PointerData pointerData, + intptr_t userData +) { + // If this button was clicked + if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + if (userData >= 0 && userData < documents.length) { + // Select the corresponding document + selectedDocumentIndex = userData; + } + } +} + +static Clay_RenderCommandArray CreateLayout() { + Clay_BeginLayout(); + Clay_Sizing layoutExpand = { + .width = CLAY_SIZING_GROW(), + .height = CLAY_SIZING_GROW() + }; + + Clay_RectangleElementConfig contentBackgroundConfig = { + .color = { 90, 90, 90, 255 }, + .cornerRadius = 8 + }; + + Clay_BeginLayout(); + // Build UI here + CLAY( + CLAY_ID("OuterContainer"), + CLAY_RECTANGLE({ .color = { 43, 41, 51, 255 } }), + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = layoutExpand, + .padding = { 16, 16 }, + .childGap = 16 + }) + ) { + // Child elements go inside braces + CLAY( + CLAY_ID("HeaderBar"), + CLAY_RECTANGLE(contentBackgroundConfig), + CLAY_LAYOUT({ + .sizing = { + .height = CLAY_SIZING_FIXED(60), + .width = CLAY_SIZING_GROW() + }, + .padding = { 16 }, + .childGap = 16, + .childAlignment = { + .y = CLAY_ALIGN_Y_CENTER + } + }) + ) { + // Header buttons go here + CLAY( + CLAY_ID("FileButton"), + CLAY_LAYOUT({ .padding = { 16, 8 }}), + CLAY_RECTANGLE({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = 5 + }) + ) { + CLAY_TEXT(CLAY_STRING("File"), CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + .textColor = { 255, 255, 255, 255 } + })); + + bool fileMenuVisible = + Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileButton"))) + || + Clay_PointerOver(Clay_GetElementId(CLAY_STRING("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 = CLAY_ATTACH_POINT_LEFT_BOTTOM + }, + }), + CLAY_LAYOUT({ + .padding = {0, 8 } + }) + ) { + CLAY( + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = { + .width = CLAY_SIZING_FIXED(200) + }, + }), + CLAY_RECTANGLE({ + .color = { 40, 40, 40, 255 }, + .cornerRadius = 8 + }) + ) { + // Render dropdown items here + RenderDropdownMenuItem(CLAY_STRING("New")); + RenderDropdownMenuItem(CLAY_STRING("Open")); + RenderDropdownMenuItem(CLAY_STRING("Close")); + } + } + } + } + RenderHeaderButton(CLAY_STRING("Edit")); + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }})) {} + RenderHeaderButton(CLAY_STRING("Upload")); + RenderHeaderButton(CLAY_STRING("Media")); + RenderHeaderButton(CLAY_STRING("Support")); + } + + CLAY( + CLAY_ID("LowerContent"), + CLAY_LAYOUT({ .sizing = layoutExpand, .childGap = 16 }) + ) { + CLAY( + CLAY_ID("Sidebar"), + CLAY_RECTANGLE(contentBackgroundConfig), + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .padding = { 16, 16 }, + .childGap = 8, + .sizing = { + .width = CLAY_SIZING_FIXED(250), + .height = CLAY_SIZING_GROW() + } + }) + ) { + for (int i = 0; i < documents.length; i++) { + Document document = documents.documents[i]; + Clay_LayoutConfig sidebarButtonLayout = { + .sizing = { .width = CLAY_SIZING_GROW() }, + .padding = { 16, 16 } + }; + + if (i == selectedDocumentIndex) { + CLAY( + CLAY_LAYOUT(sidebarButtonLayout), + CLAY_RECTANGLE({ + .color = { 120, 120, 120, 255 }, + .cornerRadius = 8, + }) + ) { + CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + .textColor = { 255, 255, 255, 255 } + })); + } + } else { + CLAY( + CLAY_LAYOUT(sidebarButtonLayout), + Clay_OnHover(HandleSidebarInteraction, i), + Clay_Hovered() + ? CLAY_RECTANGLE({ + .color = { 120, 120, 120, 120 }, + .cornerRadius = 8 + }) + : 0 + ) { + CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + .textColor = { 255, 255, 255, 255 } + })); + } + } + } + } + + CLAY( + CLAY_ID("MainContent"), + CLAY_RECTANGLE(contentBackgroundConfig), + CLAY_SCROLL({ .vertical = true }), + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .childGap = 16, + .padding = { 16, 16 }, + .sizing = layoutExpand + }) + ) { + Document selectedDocument = documents.documents[selectedDocumentIndex]; + CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = COLOR_WHITE + })); + CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = COLOR_WHITE + })); + } + } + } + + return Clay_EndLayout(); +} + +void HandleClayErrors(Clay_ErrorData errorData) { + printf("%s", errorData.errorText.chars); +} + +int main(void) { + documents.documents = (Document[]) { + { .title = CLAY_STRING("Squirrels"), .contents = CLAY_STRING("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") }, + { .title = CLAY_STRING("Lorem Ipsum"), .contents = CLAY_STRING("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.") }, + { .title = CLAY_STRING("Vacuum Instructions"), .contents = CLAY_STRING("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.") }, + { .title = CLAY_STRING("Article 4"), .contents = CLAY_STRING("Article 4") }, + { .title = CLAY_STRING("Article 5"), .contents = CLAY_STRING("Article 5") }, + }; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Error: could not initialize SDL: %s\n", SDL_GetError()); + return 1; + } + if (TTF_Init() < 0) { + fprintf(stderr, "Error: could not initialize TTF: %s\n", TTF_GetError()); + return 1; + } + + TTF_Font *font = TTF_OpenFont("resources/Roboto-Regular.ttf", 16); + if (!font) { + fprintf(stderr, "Error: could not load font: %s\n", TTF_GetError()); + return 1; + } + SDL2_fonts[FONT_ID_BODY_16] = (SDL2_Font) { + .fontId = FONT_ID_BODY_16, + .font = font, + }; + + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + if (SDL_CreateWindowAndRenderer(800, 600, SDL_WINDOW_RESIZABLE, &window, &renderer) < 0) { + fprintf(stderr, "Error: could not create window and renderer: %s", SDL_GetError()); + } + + uint64_t totalMemorySize = Clay_MinMemorySize(); + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize)); + + Clay_SetMeasureTextFunction(SDL2_MeasureText); + + int windowWidth = 0; + int windowHeight = 0; + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)windowWidth, (float)windowHeight }, (Clay_ErrorHandler) { HandleClayErrors }); + Uint64 NOW = SDL_GetPerformanceCounter(); + Uint64 LAST = 0; + double deltaTime = 0; + + while (true) { + Clay_Vector2 scrollDelta = {}; + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: { goto quit; } + case SDL_MOUSEWHEEL: { + scrollDelta.x = event.wheel.x; + scrollDelta.y = event.wheel.y; + break; + } + } + } + LAST = NOW; + NOW = SDL_GetPerformanceCounter(); + deltaTime = (double)((NOW - LAST)*1000 / (double)SDL_GetPerformanceFrequency() ); + + int mouseX = 0; + int mouseY = 0; + Uint32 mouseState = SDL_GetMouseState(&mouseX, &mouseY); + Clay_Vector2 mousePosition = (Clay_Vector2){ (float)mouseX, (float)mouseY }; + Clay_SetPointerState(mousePosition, mouseState & SDL_BUTTON(1)); + + Clay_UpdateScrollContainers( + true, + (Clay_Vector2) { scrollDelta.x, scrollDelta.y }, + deltaTime + ); + + SDL_GetWindowSize(window, &windowWidth, &windowHeight); + Clay_SetLayoutDimensions((Clay_Dimensions) { (float)windowWidth, (float)windowHeight }); + + Clay_RenderCommandArray renderCommands = CreateLayout(); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + Clay_SDL2_Render(renderer, renderCommands); + + SDL_RenderPresent(renderer); + } + +quit: + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + TTF_Quit(); + SDL_Quit(); + return 0; +} + diff --git a/examples/SDL2-video-demo/resources/Roboto-Regular.ttf b/examples/SDL2-video-demo/resources/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/examples/SDL2-video-demo/resources/Roboto-Regular.ttf differ diff --git a/examples/cairo-pdf-rendering/main.c b/examples/cairo-pdf-rendering/main.c index 3555b95..244161c 100644 --- a/examples/cairo-pdf-rendering/main.c +++ b/examples/cairo-pdf-rendering/main.c @@ -109,6 +109,10 @@ void Layout() { } } +void HandleClayErrors(Clay_ErrorData errorData) { + printf("%s", errorData.errorText.chars); +} + int main(void) { // First we set up our cairo surface. // In this example we will use the PDF backend, @@ -131,11 +135,11 @@ int main(void) { Clay_Cairo_Initialize(cr); uint64_t totalMemorySize = Clay_MinMemorySize(); - Clay_Arena clayMemory = (Clay_Arena) { .label = CLAY_STRING("Clay Memory Arena"), .memory = malloc(totalMemorySize), .capacity = totalMemorySize }; + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize)); Clay_SetMeasureTextFunction(Clay_Cairo_MeasureText); // We initialize Clay with the same size - Clay_Initialize(clayMemory, (Clay_Dimensions) { width, height }); + Clay_Initialize(clayMemory, (Clay_Dimensions) { width, height }, (Clay_ErrorHandler) { HandleClayErrors }); Clay_BeginLayout(); diff --git a/examples/clay-official-website/index.html b/examples/clay-official-website/index.html index 2645c6a..ad87248 100644 --- a/examples/clay-official-website/index.html +++ b/examples/clay-official-website/index.html @@ -311,23 +311,26 @@ }); const importObject = { - clay: { measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => { - let stringLength = memoryDataView.getUint32(textToMeasure, true); - let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true); - let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition); - let textDecoder = new TextDecoder("utf-8"); - let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength)); - let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`); - memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true); - memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true); - }, - queryScrollOffsetFunction: (addressOfOffset, elementId) => { - let container = document.getElementById(elementId.toString()); - if (container) { - memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); - memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); + clay: { + + measureTextFunction: (addressOfDimensions, textToMeasure, addressOfConfig) => { + let stringLength = memoryDataView.getUint32(textToMeasure, true); + let pointerToString = memoryDataView.getUint32(textToMeasure + 4, true); + let textConfig = readStructAtAddress(addressOfConfig, textConfigDefinition); + let textDecoder = new TextDecoder("utf-8"); + let text = textDecoder.decode(memoryDataView.buffer.slice(pointerToString, pointerToString + stringLength)); + let sourceDimensions = getTextDimensions(text, `${Math.round(textConfig.fontSize.value * GLOBAL_FONT_SCALING_FACTOR)}px ${fontsById[textConfig.fontId.value]}`); + memoryDataView.setFloat32(addressOfDimensions, sourceDimensions.width, true); + memoryDataView.setFloat32(addressOfDimensions + 4, sourceDimensions.height, true); + }, + queryScrollOffsetFunction: (addressOfOffset, elementId) => { + let container = document.getElementById(elementId.toString()); + if (container) { + memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); + memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); + } } - }}, + }, }; const { instance } = await WebAssembly.instantiateStreaming( fetch("/clay/index.wasm"), importObject @@ -335,7 +338,7 @@ memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); scratchSpaceAddress = instance.exports.__heap_base.value; heapSpaceAddress = instance.exports.__heap_base.value + 1024; - let arenaAddress = scratchSpaceAddress; + let arenaAddress = scratchSpaceAddress + 8; window.instance = instance; createMainArena(arenaAddress, heapSpaceAddress); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); diff --git a/examples/cpp-project-example/main.cpp b/examples/cpp-project-example/main.cpp index 8c26fd7..f31e854 100644 --- a/examples/cpp-project-example/main.cpp +++ b/examples/cpp-project-example/main.cpp @@ -4,10 +4,14 @@ Clay_LayoutConfig layoutElement = Clay_LayoutConfig { .padding = {5} }; +void HandleClayErrors(Clay_ErrorData errorData) { + printf("%s", errorData.errorText.chars); +} + int main(void) { uint64_t totalMemorySize = Clay_MinMemorySize(); - Clay_Arena clayMemory = Clay_Arena { .label = CLAY_STRING("Clay Memory Arena"), .capacity = totalMemorySize, .memory = (char *)malloc(totalMemorySize) }; - Clay_Initialize(clayMemory, Clay_Dimensions {1024,768}); + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, (char *)malloc(totalMemorySize)); + Clay_Initialize(clayMemory, Clay_Dimensions {1024,768}, Clay_ErrorHandler { HandleClayErrors }); Clay_BeginLayout(); CLAY(CLAY_RECTANGLE({ .color = {255,255,255,0} }), CLAY_LAYOUT(layoutElement)) {} Clay_EndLayout(); diff --git a/examples/introducing-clay-video-demo/CMakeLists.txt b/examples/introducing-clay-video-demo/CMakeLists.txt new file mode 100644 index 0000000..7e81e30 --- /dev/null +++ b/examples/introducing-clay-video-demo/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.27) +project(clay_examples_introducing_clay_video_demo C) +set(CMAKE_C_STANDARD 99) + +# Adding Raylib +include(FetchContent) +set(FETCHCONTENT_QUIET FALSE) +set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples +set(BUILD_GAMES OFF CACHE BOOL "" FORCE) # don't build the supplied example games + +FetchContent_Declare( + raylib + GIT_REPOSITORY "https://github.com/raysan5/raylib.git" + GIT_TAG "master" + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE +) + +FetchContent_MakeAvailable(raylib) + +add_executable(clay_examples_introducing_clay_video_demo main.c) + +target_compile_options(clay_examples_introducing_clay_video_demo PUBLIC) +target_include_directories(clay_examples_introducing_clay_video_demo PUBLIC .) + +target_link_libraries(clay_examples_introducing_clay_video_demo PUBLIC raylib) +set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Werror -DCLAY_DEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +add_custom_command( + TARGET clay_examples_introducing_clay_video_demo POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/resources + ${CMAKE_CURRENT_BINARY_DIR}/resources) \ No newline at end of file diff --git a/examples/introducing-clay-video-demo/main.c b/examples/introducing-clay-video-demo/main.c new file mode 100644 index 0000000..88a0c15 --- /dev/null +++ b/examples/introducing-clay-video-demo/main.c @@ -0,0 +1,302 @@ +#define CLAY_IMPLEMENTATION +#include "../../clay.h" +#include "../../renderers/raylib/clay_renderer_raylib.c" + +const int FONT_ID_BODY_16 = 0; +Clay_Color COLOR_WHITE = { 255, 255, 255, 255}; + +void RenderHeaderButton(Clay_String text) { + CLAY( + CLAY_LAYOUT({ .padding = { 16, 8 }}), + CLAY_RECTANGLE({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = 5 + }) + ) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + .textColor = { 255, 255, 255, 255 } + })); + } +} + +void RenderDropdownMenuItem(Clay_String text) { + CLAY(CLAY_LAYOUT({ .padding = { 16, 16 }})) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + .textColor = { 255, 255, 255, 255 } + })); + } +} + +typedef struct { + Clay_String title; + Clay_String contents; +} Document; + +typedef struct { + Document *documents; + uint32_t length; +} DocumentArray; + +DocumentArray documents = { + .documents = NULL, // TODO figure out if it's possible to const init this list + .length = 5 +}; + +uint32_t selectedDocumentIndex = 0; + +void HandleSidebarInteraction( + Clay_ElementId elementId, + Clay_PointerData pointerData, + intptr_t userData +) { + // If this button was clicked + if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + if (userData >= 0 && userData < documents.length) { + // Select the corresponding document + selectedDocumentIndex = userData; + } + } +} + +// This function is new since the video was published +void HandleClayErrors(Clay_ErrorData errorData) { + printf("%s", errorData.errorText.chars); +} + + +int main(void) { + documents.documents = (Document[]) { + { .title = CLAY_STRING("Squirrels"), .contents = CLAY_STRING("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") }, + { .title = CLAY_STRING("Lorem Ipsum"), .contents = CLAY_STRING("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.") }, + { .title = CLAY_STRING("Vacuum Instructions"), .contents = CLAY_STRING("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.") }, + { .title = CLAY_STRING("Article 4"), .contents = CLAY_STRING("Article 4") }, + { .title = CLAY_STRING("Article 5"), .contents = CLAY_STRING("Article 5") }, + }; + Clay_Raylib_Initialize(1024, 768, "Introducing Clay Demo", FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); // Extra parameters to this function are new since the video was published + + uint64_t clayRequiredMemory = Clay_MinMemorySize(); + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory)); + Clay_Initialize(clayMemory, (Clay_Dimensions) { + .width = GetScreenWidth(), + .height = GetScreenHeight() + }, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published + Clay_SetMeasureTextFunction(Raylib_MeasureText); + Raylib_fonts[FONT_ID_BODY_16] = (Raylib_Font) { + .font = LoadFontEx("resources/Roboto-Regular.ttf", 48, 0, 400), + .fontId = FONT_ID_BODY_16 + }; + SetTextureFilter(Raylib_fonts[FONT_ID_BODY_16].font.texture, TEXTURE_FILTER_BILINEAR); + + while (!WindowShouldClose()) { + // Run once per frame + Clay_SetLayoutDimensions((Clay_Dimensions) { + .width = GetScreenWidth(), + .height = GetScreenHeight() + }); + + Vector2 mousePosition = GetMousePosition(); + Vector2 scrollDelta = GetMouseWheelMoveV(); + Clay_SetPointerState( + (Clay_Vector2) { mousePosition.x, mousePosition.y }, + IsMouseButtonDown(0) + ); + Clay_UpdateScrollContainers( + true, + (Clay_Vector2) { scrollDelta.x, scrollDelta.y }, + GetFrameTime() + ); + + Clay_Sizing layoutExpand = { + .width = CLAY_SIZING_GROW(), + .height = CLAY_SIZING_GROW() + }; + + Clay_RectangleElementConfig contentBackgroundConfig = { + .color = { 90, 90, 90, 255 }, + .cornerRadius = 8 + }; + + Clay_BeginLayout(); + // Build UI here + CLAY( + CLAY_ID("OuterContainer"), + CLAY_RECTANGLE({ .color = { 43, 41, 51, 255 } }), + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = layoutExpand, + .padding = { 16, 16 }, + .childGap = 16 + }) + ) { + // Child elements go inside braces + CLAY( + CLAY_ID("HeaderBar"), + CLAY_RECTANGLE(contentBackgroundConfig), + CLAY_LAYOUT({ + .sizing = { + .height = CLAY_SIZING_FIXED(60), + .width = CLAY_SIZING_GROW() + }, + .padding = { 16 }, + .childGap = 16, + .childAlignment = { + .y = CLAY_ALIGN_Y_CENTER + } + }) + ) { + // Header buttons go here + CLAY( + CLAY_ID("FileButton"), + CLAY_LAYOUT({ .padding = { 16, 8 }}), + CLAY_RECTANGLE({ + .color = { 140, 140, 140, 255 }, + .cornerRadius = 5 + }) + ) { + CLAY_TEXT(CLAY_STRING("File"), CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 16, + .textColor = { 255, 255, 255, 255 } + })); + + bool fileMenuVisible = + Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileButton"))) + || + Clay_PointerOver(Clay_GetElementId(CLAY_STRING("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 = CLAY_ATTACH_POINT_LEFT_BOTTOM + }, + }), + CLAY_LAYOUT({ + .padding = {0, 8 } + }) + ) { + CLAY( + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = { + .width = CLAY_SIZING_FIXED(200) + }, + }), + CLAY_RECTANGLE({ + .color = { 40, 40, 40, 255 }, + .cornerRadius = 8 + }) + ) { + // Render dropdown items here + RenderDropdownMenuItem(CLAY_STRING("New")); + RenderDropdownMenuItem(CLAY_STRING("Open")); + RenderDropdownMenuItem(CLAY_STRING("Close")); + } + } + } + } + RenderHeaderButton(CLAY_STRING("Edit")); + CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW() }})) {} + RenderHeaderButton(CLAY_STRING("Upload")); + RenderHeaderButton(CLAY_STRING("Media")); + RenderHeaderButton(CLAY_STRING("Support")); + } + + CLAY( + CLAY_ID("LowerContent"), + CLAY_LAYOUT({ .sizing = layoutExpand, .childGap = 16 }) + ) { + CLAY( + CLAY_ID("Sidebar"), + CLAY_RECTANGLE(contentBackgroundConfig), + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .padding = { 16, 16 }, + .childGap = 8, + .sizing = { + .width = CLAY_SIZING_FIXED(250), + .height = CLAY_SIZING_GROW() + } + }) + ) { + for (int i = 0; i < documents.length; i++) { + Document document = documents.documents[i]; + Clay_LayoutConfig sidebarButtonLayout = { + .sizing = { .width = CLAY_SIZING_GROW() }, + .padding = { 16, 16 } + }; + + if (i == selectedDocumentIndex) { + CLAY( + CLAY_LAYOUT(sidebarButtonLayout), + CLAY_RECTANGLE({ + .color = { 120, 120, 120, 255 }, + .cornerRadius = 8, + }) + ) { + CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + .textColor = { 255, 255, 255, 255 } + })); + } + } else { + CLAY( + CLAY_LAYOUT(sidebarButtonLayout), + Clay_OnHover(HandleSidebarInteraction, i), + Clay_Hovered() + ? CLAY_RECTANGLE({ + .color = { 120, 120, 120, 120 }, + .cornerRadius = 8 + }) + : 0 + ) { + CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 20, + .textColor = { 255, 255, 255, 255 } + })); + } + } + } + } + + CLAY( + CLAY_ID("MainContent"), + CLAY_RECTANGLE(contentBackgroundConfig), + CLAY_SCROLL({ .vertical = true }), + CLAY_LAYOUT({ + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .childGap = 16, + .padding = { 16, 16 }, + .sizing = layoutExpand + }) + ) { + Document selectedDocument = documents.documents[selectedDocumentIndex]; + CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = COLOR_WHITE + })); + CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({ + .fontId = FONT_ID_BODY_16, + .fontSize = 24, + .textColor = COLOR_WHITE + })); + } + } + } + + Clay_RenderCommandArray renderCommands = Clay_EndLayout(); + + BeginDrawing(); + ClearBackground(BLACK); + Clay_Raylib_Render(renderCommands); + EndDrawing(); + } +} diff --git a/examples/introducing-clay-video-demo/resources/Roboto-Regular.ttf b/examples/introducing-clay-video-demo/resources/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/examples/introducing-clay-video-demo/resources/Roboto-Regular.ttf differ diff --git a/examples/raylib-sidebar-scrolling-container/main.c b/examples/raylib-sidebar-scrolling-container/main.c index 8fc1d63..5a39fc1 100644 --- a/examples/raylib-sidebar-scrolling-container/main.c +++ b/examples/raylib-sidebar-scrolling-container/main.c @@ -202,11 +202,24 @@ void UpdateDrawFrame(void) //---------------------------------------------------------------------------------- } +bool reinitializeClay = false; + +void HandleClayErrors(Clay_ErrorData errorData) { + printf("%s", errorData.errorText.chars); + if (errorData.errorType == CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED) { + reinitializeClay = true; + Clay_SetMaxElementCount(Clay__maxElementCount * 2); + } else if (errorData.errorType == CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED) { + reinitializeClay = true; + Clay_SetMaxMeasureTextCacheWordCount(Clay__maxMeasureTextCacheWordCount * 2); + } +} + int main(void) { uint64_t totalMemorySize = Clay_MinMemorySize(); - Clay_Arena clayMemory = (Clay_Arena) { .label = CLAY_STRING("Clay Memory Arena"), .memory = malloc(totalMemorySize), .capacity = totalMemorySize }; + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize)); Clay_SetMeasureTextFunction(Raylib_MeasureText); - Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }); + Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }, (Clay_ErrorHandler) { HandleClayErrors }); Clay_Raylib_Initialize(1024, 768, "Clay - Raylib Renderer Example", FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT); profilePicture = LoadTextureFromImage(LoadImage("resources/profile-picture.png")); Raylib_fonts[FONT_ID_BODY_24] = (Raylib_Font) { @@ -226,6 +239,13 @@ int main(void) { // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { + if (reinitializeClay) { + Clay_SetMaxElementCount(8192); + totalMemorySize = Clay_MinMemorySize(); + clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize)); + Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }, (Clay_ErrorHandler) { HandleClayErrors }); + reinitializeClay = false; + } UpdateDrawFrame(); } return 0; diff --git a/generator/array_add.template.c b/generator/array_add.template.c index 847cbcc..a51f891 100644 --- a/generator/array_add.template.c +++ b/generator/array_add.template.c @@ -1,5 +1,5 @@ $TYPE$ *$NAME$_Add($NAME$ *array, $TYPE$ item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } diff --git a/generator/array_add_value.template.c b/generator/array_add_value.template.c index 53dde0d..a153146 100644 --- a/generator/array_add_value.template.c +++ b/generator/array_add_value.template.c @@ -1,5 +1,5 @@ void $NAME$_Add($NAME$ *array, $TYPE$ item) { - if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { + if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; } } \ No newline at end of file diff --git a/generator/array_set.template.c b/generator/array_set.template.c index 31bbc6a..c83d335 100644 --- a/generator/array_set.template.c +++ b/generator/array_set.template.c @@ -1,13 +1,6 @@ void $NAME$_Set($NAME$ *array, int index, $TYPE$ value) { - if (index < array->capacity && index >= 0) { + if (Clay__Array_RangeCheck(index, array->capacity)) { array->internalArray[index] = value; array->length = index < array->length ? array->length : index + 1; - } else { - if (Clay__warningsEnabled) { - Clay__WarningArray_Add(&Clay_warnings, CLAY__INIT(Clay__Warning) { CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow.") }); - } - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif } } \ No newline at end of file diff --git a/renderers/SDL2/README b/renderers/SDL2/README new file mode 100644 index 0000000..582e4db --- /dev/null +++ b/renderers/SDL2/README @@ -0,0 +1,13 @@ +Please note, the SDL2 renderer is not 100% feature complete. It is currently missing: + +- Border rendering +- Image rendering +- Rounded rectangle corners + +Note: on Mac OSX, SDL2 for some reason decides to automatically disable momentum scrolling on macbook trackpads. +You can re enable it in objective C using: + +```C +[[NSUserDefaults standardUserDefaults] setBool: YES + forKey: @"AppleMomentumScrollSupported"]; +``` \ No newline at end of file diff --git a/renderers/SDL2/clay_renderer_SDL2.c b/renderers/SDL2/clay_renderer_SDL2.c new file mode 100644 index 0000000..698f0a3 --- /dev/null +++ b/renderers/SDL2/clay_renderer_SDL2.c @@ -0,0 +1,101 @@ +#include "../../clay.h" +#include +#include + +typedef struct +{ + uint32_t fontId; + TTF_Font *font; +} SDL2_Font; + +static SDL2_Font SDL2_fonts[1]; + +static Clay_Dimensions SDL2_MeasureText(Clay_String *text, Clay_TextElementConfig *config) +{ + TTF_Font *font = SDL2_fonts[config->fontId].font; + char *chars = (char *)calloc(text->length + 1, 1); + memcpy(chars, text->chars, text->length); + int width = 0; + int height = 0; + if (TTF_SizeUTF8(font, chars, &width, &height) < 0) { + fprintf(stderr, "Error: could not measure text: %s\n", TTF_GetError()); + exit(1); + } + free(chars); + return (Clay_Dimensions) { + .width = (float)width, + .height = (float)height, + }; +} + +SDL_Rect currentClippingRectangle; + +static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray renderCommands) +{ + for (uint32_t i = 0; i < renderCommands.length; i++) + { + Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i); + Clay_BoundingBox boundingBox = renderCommand->boundingBox; + switch (renderCommand->commandType) + { + case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { + Clay_RectangleElementConfig *config = renderCommand->config.rectangleElementConfig; + Clay_Color color = config->color; + SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); + SDL_FRect rect = (SDL_FRect) { + .x = boundingBox.x, + .y = boundingBox.y, + .w = boundingBox.width, + .h = boundingBox.height, + }; + SDL_RenderFillRectF(renderer, &rect); + break; + } + case CLAY_RENDER_COMMAND_TYPE_TEXT: { + Clay_TextElementConfig *config = renderCommand->config.textElementConfig; + Clay_String text = renderCommand->text; + char *cloned = (char *)calloc(text.length + 1, 1); + memcpy(cloned, text.chars, text.length); + TTF_Font* font = SDL2_fonts[config->fontId].font; + SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) { + .r = (Uint8)config->textColor.r, + .g = (Uint8)config->textColor.g, + .b = (Uint8)config->textColor.b, + .a = (Uint8)config->textColor.a, + }); + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); + + SDL_Rect destination = (SDL_Rect){ + .x = boundingBox.x, + .y = boundingBox.y, + .w = boundingBox.width, + .h = boundingBox.height, + }; + SDL_RenderCopy(renderer, texture, NULL, &destination); + + SDL_DestroyTexture(texture); + SDL_FreeSurface(surface); + free(cloned); + break; + } + case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: { + currentClippingRectangle = (SDL_Rect) { + .x = boundingBox.x, + .y = boundingBox.y, + .w = boundingBox.width, + .h = boundingBox.height, + }; + SDL_RenderSetClipRect(renderer, ¤tClippingRectangle); + break; + } + case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: { + SDL_RenderSetClipRect(renderer, NULL); + break; + } + default: { + fprintf(stderr, "Error: unhandled render command: %d\n", renderCommand->commandType); + exit(1); + } + } + } +} \ No newline at end of file diff --git a/renderers/raylib/clay_renderer_raylib.c b/renderers/raylib/clay_renderer_raylib.c index 64b62fb..0fe545a 100644 --- a/renderers/raylib/clay_renderer_raylib.c +++ b/renderers/raylib/clay_renderer_raylib.c @@ -4,9 +4,6 @@ #include "string.h" #include "stdio.h" #include "stdlib.h" -#ifdef CLAY_OVERFLOW_TRAP -#include "signal.h" -#endif #define CLAY_RECTANGLE_TO_RAYLIB_RECTANGLE(rectangle) (Rectangle) { .x = rectangle.x, .y = rectangle.y, .width = rectangle.width, .height = rectangle.height } #define CLAY_COLOR_TO_RAYLIB_COLOR(color) (Color) { .r = (unsigned char)roundf(color.r), .g = (unsigned char)roundf(color.g), .b = (unsigned char)roundf(color.b), .a = (unsigned char)roundf(color.a) } @@ -229,9 +226,6 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands) } default: { printf("Error: unhandled render command."); - #ifdef CLAY_OVERFLOW_TRAP - raise(SIGTRAP); - #endif exit(1); } } diff --git a/renderers/raylib/raylib.h b/renderers/raylib/raylib.h index 1cf34f0..a26b8ce 100644 --- a/renderers/raylib/raylib.h +++ b/renderers/raylib/raylib.h @@ -1,22 +1,22 @@ /********************************************************************************************** * -* raylib v5.5-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v5.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib * - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, * MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. * - Written in plain C code (C99) in PascalCase/camelCase notation -* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile) +* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile) * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts) +* - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts) * - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) * - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! * - Flexible Materials system, supporting classic maps and PBR maps -* - Animated 3D models supported (skeletal bones animation) (IQM) +* - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF) * - Shaders support, including Model shaders and Postprocessing shaders * - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) +* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, QOA, XM, MOD) * - VR stereo rendering with configurable HMD device parameters * - Bindings to multiple programming languages available! * @@ -27,29 +27,35 @@ * - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) * * DEPENDENCIES (included): -* [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP) -* [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP) +* [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input +* [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input +* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading * [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management * * OPTIONAL DEPENDENCIES (included): * [rcore] msf_gif (Miles Fogle) for GIF recording * [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm * [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm +* [rcore] rprand (Ramon Snatamaria) for pseudo-random numbers generation +* [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage * [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) * [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) -* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms +* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms +* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation * [rtext] stb_truetype (Sean Barret) for ttf fonts loading * [rtext] stb_rect_pack (Sean Barret) for rectangles packing * [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation * [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) * [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) -* [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) +* [rmodels] m3d (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) +* [rmodels] vox_loader (Johann Nadalutti) for models loading (VOX) * [raudio] dr_wav (David Reid) for WAV audio file loading * [raudio] dr_flac (David Reid) for FLAC audio file loading * [raudio] dr_mp3 (David Reid) for MP3 audio file loading * [raudio] stb_vorbis (Sean Barret) for OGG audio loading * [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading * [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading +* [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage * * * LICENSE: zlib/libpng @@ -84,7 +90,7 @@ #define RAYLIB_VERSION_MAJOR 5 #define RAYLIB_VERSION_MINOR 5 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.5-dev" +#define RAYLIB_VERSION "5.5" // Function specifiers in case library is build/used as a shared library // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll @@ -352,8 +358,10 @@ typedef struct Mesh { // Animation vertex data float *animVertices; // Animated vertex positions (after bones transformations) float *animNormals; // Animated normals (after bones transformations) - unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) - float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) + unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7) + Matrix *boneMatrices; // Bones animated transformation matrices + int boneCount; // Number of bones // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -790,7 +798,10 @@ typedef enum { SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter - SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf + SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf + SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds + SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights + SHADER_LOC_BONE_MATRICES // Shader location: array of matrices uniform: boneMatrices } ShaderLocationIndex; #define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO @@ -872,8 +883,7 @@ typedef enum { CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces - CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces - CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirrectangular map) + CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE // Layout is defined by a 4x3 cross with cubemap faces } CubemapLayout; // Font type, defines generation method @@ -960,36 +970,36 @@ RLAPI void CloseWindow(void); // Close windo RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked) RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen -RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) -RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized (only PLATFORM_DESKTOP) -RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized (only PLATFORM_DESKTOP) -RLAPI bool IsWindowFocused(void); // Check if window is currently focused (only PLATFORM_DESKTOP) +RLAPI bool IsWindowHidden(void); // Check if window is currently hidden +RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized +RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized +RLAPI bool IsWindowFocused(void); // Check if window is currently focused RLAPI bool IsWindowResized(void); // Check if window has been resized last frame RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled -RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) +RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags -RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) -RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed (only PLATFORM_DESKTOP) -RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) -RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) -RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) +RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution +RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed, resizes window to match monitor resolution +RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable +RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable +RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized +RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit) +RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit) +RLAPI void SetWindowTitle(const char *title); // Set title for window +RLAPI void SetWindowPosition(int x, int y); // Set window position on screen RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) RLAPI void SetWindowMaxSize(int width, int height); // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE) RLAPI void SetWindowSize(int width, int height); // Set window dimensions -RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) -RLAPI void SetWindowFocused(void); // Set window focused (only PLATFORM_DESKTOP) +RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] +RLAPI void SetWindowFocused(void); // Set window focused RLAPI void *GetWindowHandle(void); // Get native window handle RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI) RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI) RLAPI int GetMonitorCount(void); // Get number of connected monitors -RLAPI int GetCurrentMonitor(void); // Get current connected monitor +RLAPI int GetCurrentMonitor(void); // Get current monitor where window is placed RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor) RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor) @@ -1001,6 +1011,7 @@ RLAPI Vector2 GetWindowScaleDPI(void); // Get window RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the specified monitor RLAPI void SetClipboardText(const char *text); // Set clipboard text content RLAPI const char *GetClipboardText(void); // Get clipboard text content +RLAPI Image GetClipboardImage(void); // Get clipboard image content RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling @@ -1039,7 +1050,7 @@ RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR s // NOTE: Shader functionality is not available on OpenGL 1.1 RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations -RLAPI bool IsShaderReady(Shader shader); // Check if a shader is ready +RLAPI bool IsShaderValid(Shader shader); // Check if a shader is valid (loaded on GPU) RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value @@ -1122,11 +1133,12 @@ RLAPI const char *GetDirectoryPath(const char *filePath); // Get full pa RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) +RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths -RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan +RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths @@ -1138,6 +1150,10 @@ RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int * RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() +RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code +RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) +RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) + // Automation events functionality RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS @@ -1155,7 +1171,7 @@ RLAPI void PlayAutomationEvent(AutomationEvent event); // Input-related functions: keyboard RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once -RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again (Only PLATFORM_DESKTOP) +RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed @@ -1174,7 +1190,7 @@ RLAPI int GetGamepadButtonPressed(void); RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) -RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor); // Set gamepad vibration for both motors +RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds) // Input-related functions: mouse RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once @@ -1205,7 +1221,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected RLAPI int GetGestureDetected(void); // Get latest detected gesture -RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds +RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector RLAPI float GetGestureDragAngle(void); // Get gesture drag angle RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta @@ -1228,8 +1244,8 @@ RLAPI Texture2D GetShapesTexture(void); // Get t RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing // Basic shapes drawing functions -RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel -RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) +RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care] +RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel using geometry (Vector version) [Can be slow, use with care] RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (using gl lines) RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads) @@ -1238,7 +1254,7 @@ RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color c RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle +RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) @@ -1250,9 +1266,9 @@ RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color) RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters -RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle -RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle -RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors +RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); // Draw a vertical-gradient-filled rectangle +RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); // Draw a horizontal-gradient-filled rectangle +RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges @@ -1289,13 +1305,13 @@ RLAPI Vector2 GetSplinePointBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vect RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle +RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2); // Check if circle collides with a line created betweeen two points [p1] and [p2] RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle +RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] RLAPI bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount); // Check if point is within a polygon described by array of vertices RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference -RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] -RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2); // Check if circle collides with a line created betweeen two points [p1] and [p2] RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision //------------------------------------------------------------------------------------ @@ -1306,13 +1322,12 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // NOTE: These functions do not require GPU access RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) RLAPI Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data RLAPI Image LoadImageFromScreen(void); // Load image from screen buffer and (screenshot) -RLAPI bool IsImageReady(Image image); // Check if an image is ready +RLAPI bool IsImageValid(Image image); // Check if an image is valid (data and parameters) RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer @@ -1398,9 +1413,9 @@ RLAPI Texture2D LoadTexture(const char *fileName); RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) -RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready +RLAPI bool IsTextureValid(Texture2D texture); // Check if a texture is valid (loaded in GPU) RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) -RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready +RLAPI bool IsRenderTextureValid(RenderTexture2D target); // Check if a render texture is valid (loaded in GPU) RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data @@ -1431,6 +1446,7 @@ RLAPI Color ColorBrightness(Color color, float factor); // G RLAPI Color ColorContrast(Color color, float contrast); // Get color with contrast correction, contrast values between -1.0f and 1.0f RLAPI Color ColorAlpha(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint); // Get src alpha-blended into dst color with tint +RLAPI Color ColorLerp(Color color1, Color color2, float factor); // Get color lerp interpolation between two colors, factor [0.0f..1.0f] RLAPI Color GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value RLAPI Color GetPixelColor(void *srcPtr, int format); // Get Color from a source pixel pointer of certain format RLAPI void SetPixelColor(void *dstPtr, Color color, int format); // Set color formatted into destination pixel pointer @@ -1443,10 +1459,10 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G // Font loading/unloading functions RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set +RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' -RLAPI bool IsFontReady(Font font); // Check if a font is ready +RLAPI bool IsFontValid(Font font); // Check if a font is valid (font data loaded, WARNING: GPU texture not checked) RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) @@ -1536,7 +1552,7 @@ RLAPI void DrawGrid(int slices, float spacing); // Model management functions RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) -RLAPI bool IsModelReady(Model model); // Check if a model is ready +RLAPI bool IsModelValid(Model model); // Check if a model is valid (loaded in GPU, VAO/VBOs) RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) @@ -1545,6 +1561,8 @@ RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters +RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points +RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source @@ -1577,14 +1595,15 @@ RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Material loading/unloading functions RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI bool IsMaterialReady(Material material); // Check if a material is ready +RLAPI bool IsMaterialValid(Material material); // Check if a material is valid (shader assigned, map textures loaded in GPU) RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh // Model animations loading/unloading functions RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file -RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU) +RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning) RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match @@ -1614,11 +1633,11 @@ RLAPI float GetMasterVolume(void); // Get mas // Wave/Sound loading/unloading functions RLAPI Wave LoadWave(const char *fileName); // Load wave data from file RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' -RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready +RLAPI bool IsWaveValid(Wave wave); // Checks if wave data is valid (data loaded and parameters) RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data -RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready +RLAPI bool IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized) RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound @@ -1644,7 +1663,7 @@ RLAPI void UnloadWaveSamples(float *samples); // Unload // Music management functions RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data -RLAPI bool IsMusicReady(Music music); // Checks if a music stream is ready +RLAPI bool IsMusicValid(Music music); // Checks if a music stream is valid (context and buffers initialized) RLAPI void UnloadMusicStream(Music music); // Unload music stream RLAPI void PlayMusicStream(Music music); // Start music playing RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing @@ -1661,7 +1680,7 @@ RLAPI float GetMusicTimePlayed(Music music); // Get cur // AudioStream management functions RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data) -RLAPI bool IsAudioStreamReady(AudioStream stream); // Checks if an audio stream is ready +RLAPI bool IsAudioStreamValid(AudioStream stream); // Checks if an audio stream is valid (buffers initialized) RLAPI void UnloadAudioStream(AudioStream stream); // Unload audio stream and free memory RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill diff --git a/renderers/raylib/raymath.h b/renderers/raylib/raymath.h index 4dc1552..e522113 100644 --- a/renderers/raylib/raymath.h +++ b/renderers/raylib/raymath.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions +* raymath v2.0 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * * CONVENTIONS: * - Matrix structure is defined as row-major (memory layout) but parameters naming AND all @@ -12,7 +12,7 @@ * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) -* - Functions use always a "result" variable for return +* - Functions use always a "result" variable for return (except C++ operators) * - Functions are always defined inline * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) * - No compound literals used to make sure libray is compatible with C++ @@ -27,6 +27,8 @@ * Define static inline functions code, so #include header suffices for use. * This may use up lots of memory. * +* #define RAYMATH_DISABLE_CPP_OPERATORS +* Disables C++ operator overloads for raymath types. * * LICENSE: zlib/libpng * @@ -2567,7 +2569,13 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio if (!FloatEquals(det, 0)) { clone.m0 /= s.x; + clone.m4 /= s.x; + clone.m8 /= s.x; + clone.m1 /= s.y; clone.m5 /= s.y; + clone.m9 /= s.y; + clone.m2 /= s.z; + clone.m6 /= s.z; clone.m10 /= s.z; // Extract rotation @@ -2580,4 +2588,354 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio } } -#endif // RAYMATH_H \ No newline at end of file +#if defined(__cplusplus) && !defined(RAYMATH_DISABLE_CPP_OPERATORS) + +// Optional C++ math operators +//------------------------------------------------------------------------------- + +// Vector2 operators +static constexpr Vector2 Vector2Zeros = { 0, 0 }; +static constexpr Vector2 Vector2Ones = { 1, 1 }; +static constexpr Vector2 Vector2UnitX = { 1, 0 }; +static constexpr Vector2 Vector2UnitY = { 0, 1 }; + +inline Vector2 operator + (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Add(lhs, rhs); +} + +inline const Vector2& operator += (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Add(lhs, rhs); + return lhs; +} + +inline Vector2 operator - (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Subtract(lhs, rhs); +} + +inline const Vector2& operator -= (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Subtract(lhs, rhs); + return lhs; +} + +inline Vector2 operator * (const Vector2& lhs, const float& rhs) +{ + return Vector2Scale(lhs, rhs); +} + +inline const Vector2& operator *= (Vector2& lhs, const float& rhs) +{ + lhs = Vector2Scale(lhs, rhs); + return lhs; +} + +inline Vector2 operator * (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Multiply(lhs, rhs); +} + +inline const Vector2& operator *= (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Multiply(lhs, rhs); + return lhs; +} + +inline Vector2 operator * (const Vector2& lhs, const Matrix& rhs) +{ + return Vector2Transform(lhs, rhs); +} + +inline const Vector2& operator -= (Vector2& lhs, const Matrix& rhs) +{ + lhs = Vector2Transform(lhs, rhs); + return lhs; +} + +inline Vector2 operator / (const Vector2& lhs, const float& rhs) +{ + return Vector2Scale(lhs, 1.0f / rhs); +} + +inline const Vector2& operator /= (Vector2& lhs, const float& rhs) +{ + lhs = Vector2Scale(lhs, rhs); + return lhs; +} + +inline Vector2 operator / (const Vector2& lhs, const Vector2& rhs) +{ + return Vector2Divide(lhs, rhs); +} + +inline const Vector2& operator /= (Vector2& lhs, const Vector2& rhs) +{ + lhs = Vector2Divide(lhs, rhs); + return lhs; +} + +inline bool operator == (const Vector2& lhs, const Vector2& rhs) +{ + return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y); +} + +inline bool operator != (const Vector2& lhs, const Vector2& rhs) +{ + return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y); +} + +// Vector3 operators +static constexpr Vector3 Vector3Zeros = { 0, 0, 0 }; +static constexpr Vector3 Vector3Ones = { 1, 1, 1 }; +static constexpr Vector3 Vector3UnitX = { 1, 0, 0 }; +static constexpr Vector3 Vector3UnitY = { 0, 1, 0 }; +static constexpr Vector3 Vector3UnitZ = { 0, 0, 1 }; + +inline Vector3 operator + (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Add(lhs, rhs); +} + +inline const Vector3& operator += (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Add(lhs, rhs); + return lhs; +} + +inline Vector3 operator - (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Subtract(lhs, rhs); +} + +inline const Vector3& operator -= (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Subtract(lhs, rhs); + return lhs; +} + +inline Vector3 operator * (const Vector3& lhs, const float& rhs) +{ + return Vector3Scale(lhs, rhs); +} + +inline const Vector3& operator *= (Vector3& lhs, const float& rhs) +{ + lhs = Vector3Scale(lhs, rhs); + return lhs; +} + +inline Vector3 operator * (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Multiply(lhs, rhs); +} + +inline const Vector3& operator *= (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Multiply(lhs, rhs); + return lhs; +} + +inline Vector3 operator * (const Vector3& lhs, const Matrix& rhs) +{ + return Vector3Transform(lhs, rhs); +} + +inline const Vector3& operator -= (Vector3& lhs, const Matrix& rhs) +{ + lhs = Vector3Transform(lhs, rhs); + return lhs; +} + +inline Vector3 operator / (const Vector3& lhs, const float& rhs) +{ + return Vector3Scale(lhs, 1.0f / rhs); +} + +inline const Vector3& operator /= (Vector3& lhs, const float& rhs) +{ + lhs = Vector3Scale(lhs, rhs); + return lhs; +} + +inline Vector3 operator / (const Vector3& lhs, const Vector3& rhs) +{ + return Vector3Divide(lhs, rhs); +} + +inline const Vector3& operator /= (Vector3& lhs, const Vector3& rhs) +{ + lhs = Vector3Divide(lhs, rhs); + return lhs; +} + +inline bool operator == (const Vector3& lhs, const Vector3& rhs) +{ + return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z); +} + +inline bool operator != (const Vector3& lhs, const Vector3& rhs) +{ + return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z); +} + +// Vector4 operators +static constexpr Vector4 Vector4Zeros = { 0, 0, 0, 0 }; +static constexpr Vector4 Vector4Ones = { 1, 1, 1, 1 }; +static constexpr Vector4 Vector4UnitX = { 1, 0, 0, 0 }; +static constexpr Vector4 Vector4UnitY = { 0, 1, 0, 0 }; +static constexpr Vector4 Vector4UnitZ = { 0, 0, 1, 0 }; +static constexpr Vector4 Vector4UnitW = { 0, 0, 0, 1 }; + +inline Vector4 operator + (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Add(lhs, rhs); +} + +inline const Vector4& operator += (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Add(lhs, rhs); + return lhs; +} + +inline Vector4 operator - (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Subtract(lhs, rhs); +} + +inline const Vector4& operator -= (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Subtract(lhs, rhs); + return lhs; +} + +inline Vector4 operator * (const Vector4& lhs, const float& rhs) +{ + return Vector4Scale(lhs, rhs); +} + +inline const Vector4& operator *= (Vector4& lhs, const float& rhs) +{ + lhs = Vector4Scale(lhs, rhs); + return lhs; +} + +inline Vector4 operator * (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Multiply(lhs, rhs); +} + +inline const Vector4& operator *= (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Multiply(lhs, rhs); + return lhs; +} + +inline Vector4 operator / (const Vector4& lhs, const float& rhs) +{ + return Vector4Scale(lhs, 1.0f / rhs); +} + +inline const Vector4& operator /= (Vector4& lhs, const float& rhs) +{ + lhs = Vector4Scale(lhs, rhs); + return lhs; +} + +inline Vector4 operator / (const Vector4& lhs, const Vector4& rhs) +{ + return Vector4Divide(lhs, rhs); +} + +inline const Vector4& operator /= (Vector4& lhs, const Vector4& rhs) +{ + lhs = Vector4Divide(lhs, rhs); + return lhs; +} + +inline bool operator == (const Vector4& lhs, const Vector4& rhs) +{ + return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z) && FloatEquals(lhs.w, rhs.w); +} + +inline bool operator != (const Vector4& lhs, const Vector4& rhs) +{ + return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z) || !FloatEquals(lhs.w, rhs.w); +} + +// Quaternion operators +static constexpr Quaternion QuaternionZeros = { 0, 0, 0, 0 }; +static constexpr Quaternion QuaternionOnes = { 1, 1, 1, 1 }; +static constexpr Quaternion QuaternionUnitX = { 0, 0, 0, 1 }; + +inline Quaternion operator + (const Quaternion& lhs, const float& rhs) +{ + return QuaternionAddValue(lhs, rhs); +} + +inline const Quaternion& operator += (Quaternion& lhs, const float& rhs) +{ + lhs = QuaternionAddValue(lhs, rhs); + return lhs; +} + +inline Quaternion operator - (const Quaternion& lhs, const float& rhs) +{ + return QuaternionSubtractValue(lhs, rhs); +} + +inline const Quaternion& operator -= (Quaternion& lhs, const float& rhs) +{ + lhs = QuaternionSubtractValue(lhs, rhs); + return lhs; +} + +inline Quaternion operator * (const Quaternion& lhs, const Matrix& rhs) +{ + return QuaternionTransform(lhs, rhs); +} + +inline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs) +{ + lhs = QuaternionTransform(lhs, rhs); + return lhs; +} + +// Matrix operators +inline Matrix operator + (const Matrix& lhs, const Matrix& rhs) +{ + return MatrixAdd(lhs, rhs); +} + +inline const Matrix& operator += (Matrix& lhs, const Matrix& rhs) +{ + lhs = MatrixAdd(lhs, rhs); + return lhs; +} + +inline Matrix operator - (const Matrix& lhs, const Matrix& rhs) +{ + return MatrixSubtract(lhs, rhs); +} + +inline const Matrix& operator -= (Matrix& lhs, const Matrix& rhs) +{ + lhs = MatrixSubtract(lhs, rhs); + return lhs; +} + +inline Matrix operator * (const Matrix& lhs, const Matrix& rhs) +{ + return MatrixMultiply(lhs, rhs); +} + +inline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs) +{ + lhs = MatrixMultiply(lhs, rhs); + return lhs; +} +//------------------------------------------------------------------------------- +#endif // C++ operators + +#endif // RAYMATH_H