clay/clay.h
2024-08-23 16:06:08 +12:00

2274 lines
116 KiB
C

#pragma once
#ifndef CLAY_IMPLEMENTATION
#define CLAY_IMPLEMENTATION
#ifdef CLAY_WASM
#define CLAY_WASM_EXPORT(name) __attribute__((export_name(name)))
#else
#define CLAY_WASM_EXPORT(null)
#endif
#include "stdint.h"
#include "stdbool.h"
#ifdef CLAY_OVERFLOW_TRAP
#include "signal.h"
#endif
#ifndef CLAY_MAX_ELEMENT_COUNT
#define CLAY_MAX_ELEMENT_COUNT 8192
#endif
#ifndef CLAY__NULL
#define CLAY__NULL 0
#endif
#ifndef CLAY__MAXFLOAT
#define CLAY__MAXFLOAT 3.40282346638528859812e+38F
#endif
#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))
#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y))
// Publicly visible config macro -----------------------------------------------------
#define CLAY_LAYOUT(...) Clay_LayoutConfigArray_Add(&Clay__layoutConfigs, (Clay_LayoutConfig) {__VA_ARGS__ })
#define CLAY_RECTANGLE_CONFIG(...) Clay_RectangleElementConfigArray_Add(&Clay__rectangleElementConfigs, (Clay_RectangleElementConfig) {__VA_ARGS__ })
#define CLAY_TEXT_CONFIG(...) Clay_TextElementConfigArray_Add(&Clay__textElementConfigs, (Clay_TextElementConfig) {__VA_ARGS__ })
#define CLAY_IMAGE_CONFIG(...) Clay_ImageElementConfigArray_Add(&Clay__imageElementConfigs, (Clay_ImageElementConfig) {__VA_ARGS__ })
#define CLAY_FLOATING_CONFIG(...) Clay_FloatingElementConfigArray_Add(&Clay__floatingElementConfigs, (Clay_FloatingElementConfig) {__VA_ARGS__ })
#define CLAY_CUSTOM_ELEMENT_CONFIG(...) Clay_CustomElementConfigArray_Add(&Clay__customElementConfigs, (Clay_CustomElementConfig) {__VA_ARGS__ })
#define CLAY_SCROLL_CONFIG(...) Clay_ScrollContainerElementConfigArray_Add(&Clay__scrollElementConfigs, (Clay_ScrollContainerElementConfig) {__VA_ARGS__ })
#define CLAY_BORDER_CONFIG(...) Clay_BorderContainerElementConfigArray_Add(&Clay__borderElementConfigs, (Clay_BorderContainerElementConfig ) { __VA_ARGS__ })
#define CLAY_BORDER_CONFIG_OUTSIDE(...) Clay_BorderContainerElementConfigArray_Add(&Clay__borderElementConfigs, (Clay_BorderContainerElementConfig ) { .left = { __VA_ARGS__ }, .right = { __VA_ARGS__ }, .top = { __VA_ARGS__ }, .bottom = { __VA_ARGS__ } })
#define CLAY_BORDER_CONFIG_OUTSIDE_RADIUS(width, color, radius) Clay_BorderContainerElementConfigArray_Add(&Clay__borderElementConfigs, (Clay_BorderContainerElementConfig ) { .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .cornerRadius = { radius, radius, radius, radius } })
#define CLAY_BORDER_CONFIG_ALL(...) Clay_BorderContainerElementConfigArray_Add(&Clay__borderElementConfigs, (Clay_BorderContainerElementConfig ) { .left = { __VA_ARGS__ }, .right = { __VA_ARGS__ }, .top = { __VA_ARGS__ }, .bottom = { __VA_ARGS__ }, .betweenChildren = { __VA_ARGS__ } })
#define CLAY_BORDER_CONFIG_ALL_RADIUS(width, color, radius) Clay_BorderContainerElementConfigArray_Add(&Clay__borderElementConfigs, (Clay_BorderContainerElementConfig ) { .left = { __VA_ARGS__ }, .right = { __VA_ARGS__ }, .top = { __VA_ARGS__ }, .bottom = { __VA_ARGS__ }, .betweenChildren = { __VA_ARGS__ }, .cornerRadius = { radius, radius, radius, radius }})
#define CLAY_CORNER_RADIUS(radius) (Clay_CornerRadius) { radius, radius, radius, radius }
#define CLAY_SIZING_FIT(...) (Clay_SizingAxis) { .type = CLAY__SIZING_TYPE_FIT, .sizeMinMax = (Clay_SizingMinMax) {__VA_ARGS__} }
#define CLAY_SIZING_GROW(...) (Clay_SizingAxis) { .type = CLAY__SIZING_TYPE_GROW, .sizeMinMax = (Clay_SizingMinMax) {__VA_ARGS__} }
#define CLAY_SIZING_FIXED(fixedSize) (Clay_SizingAxis) { .type = CLAY__SIZING_TYPE_GROW, .sizeMinMax = { fixedSize, fixedSize } }
#define CLAY_SIZING_PERCENT(percentOfParent) (Clay_SizingAxis) { .type = CLAY__SIZING_TYPE_PERCENT, .sizePercent = percentOfParent }
#define CLAY_ID(label) Clay__HashString(CLAY_STRING(label), 0)
#define CLAY_IDI(label, index) Clay__HashString(CLAY_STRING(label), index)
#define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof(s[0])) - sizeof(s[0]))
#define CLAY_STRING(string) (Clay_String) { .length = CLAY__STRING_LENGTH(string), .chars = string }
// Publicly visible layout element macros -----------------------------------------------------
#define CLAY_CONTAINER(id, layoutConfig, children) \
Clay__OpenContainerElement(id, layoutConfig); \
children \
Clay__CloseContainerElement()
#define CLAY_RECTANGLE(id, layoutConfig, rectangleConfig, children) \
Clay__OpenRectangleElement(id, layoutConfig, rectangleConfig); \
children \
Clay__CloseContainerElement()
#define CLAY_TEXT(id, text, textConfig) Clay__OpenTextElement(id, text, textConfig)
#define CLAY_IMAGE(id, layoutConfig, imageConfig, children) \
Clay__OpenImageContainerElement(id, layoutConfig, imageConfig); \
children \
Clay__CloseContainerElement()
#define CLAY_SCROLL_CONTAINER(id, layoutConfig, scrollConfig, children) \
Clay__OpenScrollElement(id, layoutConfig, scrollConfig); \
children \
Clay__CloseScrollContainerElement()
#define CLAY_FLOATING_CONTAINER(id, layoutConfig, floatingConfig, children) \
Clay__OpenFloatingContainerElement(id, layoutConfig, floatingConfig); \
children \
Clay__CloseFloatingContainer()
#define CLAY_BORDER_CONTAINER(id, layoutConfig, borderConfig, children) \
Clay__OpenBorderContainerElement(id, layoutConfig, borderConfig); \
children \
Clay__CloseContainerElement()
#define CLAY_CUSTOM_ELEMENT(id, layoutConfig, customElementConfig, children) \
Clay__OpenCustomElement(id, layoutConfig, customElementConfig); \
children \
Clay__CloseContainerElement()
// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
// but it is also used to represent slices.
typedef struct {
int length;
const char *chars;
} Clay_String;
Clay_String CLAY__SPACECHAR = (Clay_String) { .length = 1, .chars = " " };
Clay_String CLAY__STRING_DEFAULT = (Clay_String) { .length = 0, .chars = "" };
typedef struct {
Clay_String label;
uint64_t nextAllocation;
uint64_t capacity;
char *memory;
} Clay_Arena;
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_String *internalArray;
} Clay_StringArray;
Clay_StringArray Clay_warnings = (Clay_StringArray) {};
Clay_String *Clay_StringArray_Add(Clay_StringArray *array, Clay_String item)
{
if (array->length < array->capacity) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
else {
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
}
return &CLAY__STRING_DEFAULT;
}
Clay_StringArray Clay_StringArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena)
{
uint64_t totalSizeBytes = capacity * sizeof(Clay_String);
Clay_StringArray array = (Clay_StringArray){.capacity = capacity, .length = 0};
uint64_t arenaOffsetAligned = arena->nextAllocation + (arena->nextAllocation % sizeof(Clay_String));
if (arenaOffsetAligned + totalSizeBytes <= arena->capacity) {
array.internalArray = (Clay_String*)(arena->memory + arenaOffsetAligned);
arena->nextAllocation = arenaOffsetAligned + totalSizeBytes;
}
else {
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
}
return array;
}
void* Clay__Array_Allocate_Arena(uint32_t capacity, uint32_t itemSize, Clay_Arena *arena)
{
uint64_t totalSizeBytes = capacity * itemSize;
uint64_t arenaOffsetAligned = arena->nextAllocation + (arena->nextAllocation % itemSize);
if (arenaOffsetAligned + totalSizeBytes <= arena->capacity) {
arena->nextAllocation = arenaOffsetAligned + totalSizeBytes;
return (void*)(arena->memory + arenaOffsetAligned);
}
else {
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
}
return CLAY__NULL;
}
bool Clay__Array_RangeCheck(int index, uint32_t length)
{
if (index < length && index >= 0) {
return true;
}
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Array access out of bounds."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
return false;
}
bool Clay__Array_IncrementCapacityCheck(uint32_t length, uint32_t capacity)
{
if (length < capacity) {
return true;
}
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to add to array that is already at capacity."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
return false;
}
bool CLAY__BOOL_DEFAULT = false;
// __GENERATED__ template array_define TYPE=bool NAME=Clay__BoolArray
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
bool *internalArray;
} Clay__BoolArray;
Clay__BoolArray Clay__BoolArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__BoolArray){.capacity = capacity, .length = 0, .internalArray = (bool *)Clay__Array_Allocate_Arena(capacity, sizeof(bool), arena)};
}
#pragma endregion
// __GENERATED__ template
typedef struct {
float r, g, b, a;
} Clay_Color;
typedef struct {
float x, y, width, height;
} Clay_Rectangle;
typedef struct {
float width, height;
} Clay_Dimensions;
typedef struct {
float x, y;
} Clay_Vector2;
typedef enum __attribute__((__packed__)) {
CLAY_LEFT_TO_RIGHT,
CLAY_TOP_TO_BOTTOM,
} Clay_LayoutDirection;
typedef enum __attribute__((__packed__)) {
CLAY_ALIGN_X_LEFT,
CLAY_ALIGN_X_RIGHT,
CLAY_ALIGN_X_CENTER,
} Clay_LayoutAlignmentX;
typedef enum __attribute__((__packed__)) {
CLAY_ALIGN_Y_TOP,
CLAY_ALIGN_Y_BOTTOM,
CLAY_ALIGN_Y_CENTER,
} Clay_LayoutAlignmentY;
typedef enum __attribute__((__packed__)) {
CLAY__SIZING_TYPE_FIT,
CLAY__SIZING_TYPE_GROW,
CLAY__SIZING_TYPE_PERCENT,
} Clay__SizingType;
typedef enum {
CLAY_RENDER_COMMAND_TYPE_NONE,
CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
CLAY_RENDER_COMMAND_TYPE_BORDER,
CLAY_RENDER_COMMAND_TYPE_TEXT,
CLAY_RENDER_COMMAND_TYPE_IMAGE,
CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,
CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,
CLAY_RENDER_COMMAND_TYPE_CUSTOM,
} Clay_RenderCommandType;
typedef enum __attribute__((__packed__)) {
CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER,
CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE,
CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER,
CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER,
CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER,
CLAY__LAYOUT_ELEMENT_TYPE_IMAGE,
CLAY__LAYOUT_ELEMENT_TYPE_TEXT,
CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM,
} Clay__LayoutElementType;
Clay_RenderCommandType Clay__LayoutElementTypeToRenderCommandType[] = {
[CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER] = CLAY_RENDER_COMMAND_TYPE_NONE,
[CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE] = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
[CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER] = CLAY_RENDER_COMMAND_TYPE_NONE,
[CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER] = CLAY_RENDER_COMMAND_TYPE_NONE,
[CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER] = CLAY_RENDER_COMMAND_TYPE_BORDER,
[CLAY__LAYOUT_ELEMENT_TYPE_IMAGE] = CLAY_RENDER_COMMAND_TYPE_IMAGE,
[CLAY__LAYOUT_ELEMENT_TYPE_TEXT] = CLAY_RENDER_COMMAND_TYPE_TEXT,
[CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM] = CLAY_RENDER_COMMAND_TYPE_CUSTOM,
};
typedef enum __attribute__((__packed__)) {
CLAY_ATTACH_POINT_LEFT_TOP,
CLAY_ATTACH_POINT_LEFT_CENTER,
CLAY_ATTACH_POINT_LEFT_BOTTOM,
CLAY_ATTACH_POINT_CENTER_TOP,
CLAY_ATTACH_POINT_CENTER_CENTER,
CLAY_ATTACH_POINT_CENTER_BOTTOM,
CLAY_ATTACH_POINT_RIGHT_TOP,
CLAY_ATTACH_POINT_RIGHT_CENTER,
CLAY_ATTACH_POINT_RIGHT_BOTTOM,
} Clay_FloatingAttachPointType;
typedef struct
{
Clay_FloatingAttachPointType element;
Clay_FloatingAttachPointType parent;
} Clay_FloatingAttachPoints;
typedef struct {
Clay_LayoutAlignmentX x;
Clay_LayoutAlignmentY y;
} Clay_ChildAlignment;
typedef struct {
float min;
float max;
} Clay_SizingMinMax;
typedef struct {
union {
Clay_SizingMinMax sizeMinMax;
float sizePercent;
};
Clay__SizingType type;
} Clay_SizingAxis;
typedef struct {
Clay_SizingAxis width;
Clay_SizingAxis height;
} Clay_Sizing;
typedef struct {
uint16_t x;
uint16_t y;
} Clay_Padding;
typedef struct {
float topLeft;
float topRight;
float bottomLeft;
float bottomRight;
} Clay_CornerRadius;
typedef struct {
Clay_Sizing sizing;
Clay_Padding padding;
uint16_t childGap;
Clay_LayoutDirection layoutDirection;
Clay_ChildAlignment childAlignment;
} Clay_LayoutConfig;
Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = (Clay_LayoutConfig){};
// __GENERATED__ template array_define,array_add TYPE=Clay_LayoutConfig NAME=Clay_LayoutConfigArray DEFAULT_VALUE=&CLAY_LAYOUT_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_LayoutConfig *internalArray;
} Clay_LayoutConfigArray;
Clay_LayoutConfigArray Clay_LayoutConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_LayoutConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutConfig), arena)};
}
Clay_LayoutConfig *Clay_LayoutConfigArray_Add(Clay_LayoutConfigArray *array, Clay_LayoutConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY_LAYOUT_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct {
Clay_Color color;
Clay_CornerRadius cornerRadius;
#ifdef CLAY_EXTEND_CONFIG_RECTANGLE
CLAY_EXTEND_CONFIG_RECTANGLE
#endif
} Clay_RectangleElementConfig;
Clay_RectangleElementConfig CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT = (Clay_RectangleElementConfig){0};
// __GENERATED__ template array_define,array_add TYPE=Clay_RectangleElementConfig NAME=Clay_RectangleElementConfigArray DEFAULT_VALUE=&CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_RectangleElementConfig *internalArray;
} Clay_RectangleElementConfigArray;
Clay_RectangleElementConfigArray Clay_RectangleElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_RectangleElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_RectangleElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_RectangleElementConfig), arena)};
}
Clay_RectangleElementConfig *Clay_RectangleElementConfigArray_Add(Clay_RectangleElementConfigArray *array, Clay_RectangleElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__RECTANGLE_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_Color textColor;
uint16_t fontId;
uint16_t fontSize;
uint16_t letterSpacing;
uint16_t lineSpacing;
#ifdef CLAY_EXTEND_CONFIG_TEXT
CLAY_EXTEND_CONFIG_TEXT
#endif
} Clay_TextElementConfig;
Clay_TextElementConfig CLAY__TEXT_ELEMENT_CONFIG_DEFAULT = (Clay_TextElementConfig) {};
// __GENERATED__ template array_define,array_add TYPE=Clay_TextElementConfig NAME=Clay_TextElementConfigArray DEFAULT_VALUE=&CLAY__TEXT_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_TextElementConfig *internalArray;
} Clay_TextElementConfigArray;
Clay_TextElementConfigArray Clay_TextElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_TextElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_TextElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_TextElementConfig), arena)};
}
Clay_TextElementConfig *Clay_TextElementConfigArray_Add(Clay_TextElementConfigArray *array, Clay_TextElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__TEXT_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
void * imageData;
Clay_Dimensions sourceDimensions;
#ifdef CLAY_EXTEND_CONFIG_IMAGE
CLAY_EXTEND_CONFIG_IMAGE
#endif
} Clay_ImageElementConfig;
Clay_ImageElementConfig CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT = (Clay_ImageElementConfig) {};
// __GENERATED__ template array_define,array_add TYPE=Clay_ImageElementConfig NAME=Clay_ImageElementConfigArray DEFAULT_VALUE=&CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_ImageElementConfig *internalArray;
} Clay_ImageElementConfigArray;
Clay_ImageElementConfigArray Clay_ImageElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_ImageElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ImageElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ImageElementConfig), arena)};
}
Clay_ImageElementConfig *Clay_ImageElementConfigArray_Add(Clay_ImageElementConfigArray *array, Clay_ImageElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__IMAGE_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_Vector2 offset;
Clay_Dimensions expand;
uint16_t zIndex;
uint32_t parentId;
Clay_FloatingAttachPoints attachment;
} Clay_FloatingElementConfig;
Clay_FloatingElementConfig CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT = (Clay_FloatingElementConfig) {};
// __GENERATED__ template array_define,array_add TYPE=Clay_FloatingElementConfig NAME=Clay_FloatingElementConfigArray DEFAULT_VALUE=&CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_FloatingElementConfig *internalArray;
} Clay_FloatingElementConfigArray;
Clay_FloatingElementConfigArray Clay_FloatingElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_FloatingElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_FloatingElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_FloatingElementConfig), arena)};
}
Clay_FloatingElementConfig *Clay_FloatingElementConfigArray_Add(Clay_FloatingElementConfigArray *array, Clay_FloatingElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__FLOATING_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
#ifndef CLAY_EXTEND_CONFIG_CUSTOM
void* customData;
#else
CLAY_EXTEND_CONFIG_CUSTOM
#endif
} Clay_CustomElementConfig;
Clay_CustomElementConfig CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT = (Clay_CustomElementConfig) {};
// __GENERATED__ template array_define,array_add TYPE=Clay_CustomElementConfig NAME=Clay_CustomElementConfigArray DEFAULT_VALUE=&CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_CustomElementConfig *internalArray;
} Clay_CustomElementConfigArray;
Clay_CustomElementConfigArray Clay_CustomElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_CustomElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_CustomElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_CustomElementConfig), arena)};
}
Clay_CustomElementConfig *Clay_CustomElementConfigArray_Add(Clay_CustomElementConfigArray *array, Clay_CustomElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__CUSTOM_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
bool horizontal;
bool vertical;
} Clay_ScrollContainerElementConfig;
Clay_ScrollContainerElementConfig CLAY__SCROLL_CONTAINER_ELEMENT_CONFIG_DEFAULT = (Clay_ScrollContainerElementConfig ) {};
// __GENERATED__ template array_define,array_add TYPE=Clay_ScrollContainerElementConfig NAME=Clay_ScrollContainerElementConfigArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_ScrollContainerElementConfig *internalArray;
} Clay_ScrollContainerElementConfigArray;
Clay_ScrollContainerElementConfigArray Clay_ScrollContainerElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_ScrollContainerElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_ScrollContainerElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_ScrollContainerElementConfig), arena)};
}
Clay_ScrollContainerElementConfig *Clay_ScrollContainerElementConfigArray_Add(Clay_ScrollContainerElementConfigArray *array, Clay_ScrollContainerElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__SCROLL_CONTAINER_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
uint32_t width;
Clay_Color color;
} Clay_Border;
typedef struct
{
Clay_Border left;
Clay_Border right;
Clay_Border top;
Clay_Border bottom;
Clay_Border betweenChildren;
Clay_CornerRadius cornerRadius;
} Clay_BorderContainerElementConfig;
Clay_BorderContainerElementConfig CLAY__BORDER_CONTAINER_ELEMENT_CONFIG_DEFAULT = (Clay_BorderContainerElementConfig ) {};
// __GENERATED__ template array_define,array_add TYPE=Clay_BorderContainerElementConfig NAME=Clay_BorderContainerElementConfigArray DEFAULT_VALUE=&CLAY__BORDER_CONTAINER_ELEMENT_CONFIG_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_BorderContainerElementConfig *internalArray;
} Clay_BorderContainerElementConfigArray;
Clay_BorderContainerElementConfigArray Clay_BorderContainerElementConfigArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_BorderContainerElementConfigArray){.capacity = capacity, .length = 0, .internalArray = (Clay_BorderContainerElementConfig *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_BorderContainerElementConfig), arena)};
}
Clay_BorderContainerElementConfig *Clay_BorderContainerElementConfigArray_Add(Clay_BorderContainerElementConfigArray *array, Clay_BorderContainerElementConfig item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__BORDER_CONTAINER_ELEMENT_CONFIG_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
struct t_Clay_LayoutElement **elements;
uint16_t length;
} Clay__LayoutElementChildren;
typedef union
{
Clay_RectangleElementConfig *rectangleElementConfig;
Clay_TextElementConfig *textElementConfig;
Clay_ImageElementConfig *imageElementConfig;
Clay_FloatingElementConfig *floatingElementConfig;
Clay_CustomElementConfig *customElementConfig;
Clay_ScrollContainerElementConfig *scrollElementConfig;
Clay_BorderContainerElementConfig *borderElementConfig;
} Clay_ElementConfigUnion;
typedef struct t_Clay_LayoutElement
{
#ifdef CLAY_DEBUG
Clay_String name;
#endif
union {
Clay__LayoutElementChildren children;
Clay_String text;
};
Clay_Dimensions dimensions;
Clay_Dimensions minDimensions;
Clay_LayoutConfig *layoutConfig;
Clay_ElementConfigUnion elementConfig;
uint32_t id;
Clay__LayoutElementType elementType;
} Clay_LayoutElement;
Clay_LayoutElement CLAY__LAYOUT_ELEMENT_DEFAULT = (Clay_LayoutElement) {};
// __GENERATED__ template array_define,array_add,array_get TYPE=Clay_LayoutElement NAME=Clay_LayoutElementArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_LayoutElement *internalArray;
} Clay_LayoutElementArray;
Clay_LayoutElementArray Clay_LayoutElementArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_LayoutElementArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElement *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElement), arena)};
}
Clay_LayoutElement *Clay_LayoutElementArray_Add(Clay_LayoutElementArray *array, Clay_LayoutElement item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__LAYOUT_ELEMENT_DEFAULT;
}
Clay_LayoutElement *Clay_LayoutElementArray_Get(Clay_LayoutElementArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
// __GENERATED__ template array_define,array_add,array_get,array_remove_swapback TYPE=Clay_LayoutElement* NAME=Clay__LayoutElementPointerArray DEFAULT_VALUE=CLAY__NULL
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_LayoutElement* *internalArray;
} Clay__LayoutElementPointerArray;
Clay__LayoutElementPointerArray Clay__LayoutElementPointerArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__LayoutElementPointerArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElement* *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElement*), arena)};
}
Clay_LayoutElement* *Clay__LayoutElementPointerArray_Add(Clay__LayoutElementPointerArray *array, Clay_LayoutElement* item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return CLAY__NULL;
}
Clay_LayoutElement* *Clay__LayoutElementPointerArray_Get(Clay__LayoutElementPointerArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : CLAY__NULL;
}
Clay_LayoutElement* Clay__LayoutElementPointerArray_RemoveSwapback(Clay__LayoutElementPointerArray *array, int index) {
if (Clay__Array_RangeCheck(index, array->length)) {
array->length--;
Clay_LayoutElement* removed = array->internalArray[index];
array->internalArray[index] = array->internalArray[array->length];
return removed;
}
return CLAY__NULL;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_Rectangle boundingBox;
Clay_ElementConfigUnion config;
Clay_String text; // TODO I wish there was a way to avoid having to have this on every render command
uint32_t id;
Clay_RenderCommandType commandType;
} Clay_RenderCommand;
Clay_RenderCommand CLAY__RENDER_COMMAND_DEFAULT = (Clay_RenderCommand) {};
// __GENERATED__ template array_define TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_RenderCommand *internalArray;
} Clay_RenderCommandArray;
Clay_RenderCommandArray Clay_RenderCommandArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay_RenderCommandArray){.capacity = capacity, .length = 0, .internalArray = (Clay_RenderCommand *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_RenderCommand), arena)};
}
#pragma endregion
// __GENERATED__ template
// __GENERATED__ template array_add TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray DEFAULT_VALUE=&CLAY__RENDER_COMMAND_DEFAULT
#pragma region generated
Clay_RenderCommand *Clay_RenderCommandArray_Add(Clay_RenderCommandArray *array, Clay_RenderCommand item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__RENDER_COMMAND_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
// __GENERATED__ template array_get TYPE=Clay_RenderCommand NAME=Clay_RenderCommandArray DEFAULT_VALUE=&CLAY__RENDER_COMMAND_DEFAULT
#pragma region generated
Clay_RenderCommand *Clay_RenderCommandArray_Get(Clay_RenderCommandArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__RENDER_COMMAND_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_LayoutElement *layoutElement;
Clay_Rectangle boundingBox;
Clay_Dimensions contentSize;
Clay_Vector2 scrollOrigin;
Clay_Vector2 pointerOrigin;
Clay_Vector2 scrollMomentum;
Clay_Vector2 scrollPosition;
float momentumTime;
uint32_t elementId;
bool openThisFrame;
bool pointerScrollActive;
} Clay__ScrollContainerData;
Clay__ScrollContainerData CLAY__SCROLL_CONTAINER_DEFAULT = (Clay__ScrollContainerData) {};
// __GENERATED__ template array_define TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay__ScrollContainerData *internalArray;
} Clay__ScrollContainerDataArray;
Clay__ScrollContainerDataArray Clay__ScrollContainerDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__ScrollContainerDataArray){.capacity = capacity, .length = 0, .internalArray = (Clay__ScrollContainerData *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__ScrollContainerData), arena)};
}
#pragma endregion
// __GENERATED__ template
// __GENERATED__ template array_add TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT
#pragma region generated
Clay__ScrollContainerData *Clay__ScrollContainerDataArray_Add(Clay__ScrollContainerDataArray *array, Clay__ScrollContainerData item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__SCROLL_CONTAINER_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
// __GENERATED__ template array_get TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT
#pragma region generated
Clay__ScrollContainerData *Clay__ScrollContainerDataArray_Get(Clay__ScrollContainerDataArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__SCROLL_CONTAINER_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
// __GENERATED__ template array_remove_swapback TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray DEFAULT_VALUE=CLAY__SCROLL_CONTAINER_DEFAULT
#pragma region generated
Clay__ScrollContainerData Clay__ScrollContainerDataArray_RemoveSwapback(Clay__ScrollContainerDataArray *array, int index) {
if (Clay__Array_RangeCheck(index, array->length)) {
array->length--;
Clay__ScrollContainerData removed = array->internalArray[index];
array->internalArray[index] = array->internalArray[array->length];
return removed;
}
return CLAY__SCROLL_CONTAINER_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_Rectangle boundingBox;
uint32_t id;
Clay_LayoutElement* layoutElement;
int32_t nextIndex;
} Clay_LayoutElementHashMapItem;
Clay_LayoutElementHashMapItem CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT = (Clay_LayoutElementHashMapItem) { .layoutElement = &CLAY__LAYOUT_ELEMENT_DEFAULT };
// __GENERATED__ template array_define,array_get,array_add,array_set TYPE=Clay_LayoutElementHashMapItem NAME=Clay__LayoutElementHashMapItemArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay_LayoutElementHashMapItem *internalArray;
} Clay__LayoutElementHashMapItemArray;
Clay__LayoutElementHashMapItemArray Clay__LayoutElementHashMapItemArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__LayoutElementHashMapItemArray){.capacity = capacity, .length = 0, .internalArray = (Clay_LayoutElementHashMapItem *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay_LayoutElementHashMapItem), arena)};
}
Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Get(Clay__LayoutElementHashMapItemArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT;
}
Clay_LayoutElementHashMapItem *Clay__LayoutElementHashMapItemArray_Add(Clay__LayoutElementHashMapItemArray *array, Clay_LayoutElementHashMapItem item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__LAYOUT_ELEMENT_HASH_MAP_ITEM_DEFAULT;
}
void Clay__LayoutElementHashMapItemArray_Set(Clay__LayoutElementHashMapItemArray *array, int index, Clay_LayoutElementHashMapItem value) {
if (index < array->capacity && index >= 0) {
array->internalArray[index] = value;
array->length = index < array->length ? array->length : index + 1;
} else {
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
}
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_Dimensions dimensions;
uint32_t id;
int32_t nextIndex;
} Clay__MeasureTextCacheItem;
Clay__MeasureTextCacheItem CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT = (Clay__MeasureTextCacheItem) { };
// __GENERATED__ template array_define,array_get,array_add,array_set TYPE=Clay__MeasureTextCacheItem NAME=Clay__MeasureTextCacheItemArray DEFAULT_VALUE=&CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay__MeasureTextCacheItem *internalArray;
} Clay__MeasureTextCacheItemArray;
Clay__MeasureTextCacheItemArray Clay__MeasureTextCacheItemArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__MeasureTextCacheItemArray){.capacity = capacity, .length = 0, .internalArray = (Clay__MeasureTextCacheItem *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__MeasureTextCacheItem), arena)};
}
Clay__MeasureTextCacheItem *Clay__MeasureTextCacheItemArray_Get(Clay__MeasureTextCacheItemArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__MEASURE_TEXT_CACHE_ITEM_DEFAULT;
}
Clay__MeasureTextCacheItem *Clay__MeasureTextCacheItemArray_Add(Clay__MeasureTextCacheItemArray *array, Clay__MeasureTextCacheItem item) {
if (Clay__Array_IncrementCapacityCheck(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) {
array->internalArray[index] = value;
array->length = index < array->length ? array->length : index + 1;
} else {
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
}
}
#pragma endregion
// __GENERATED__ template
int32_t CLAY__INDEX_ARRAY_DEFAULT_VALUE = -1;
// __GENERATED__ template array_define,array_get,array_add,array_set TYPE=int32_t NAME=Clay__int32_tArray DEFAULT_VALUE=&CLAY__INDEX_ARRAY_DEFAULT_VALUE
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
int32_t *internalArray;
} Clay__int32_tArray;
Clay__int32_tArray Clay__int32_tArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__int32_tArray){.capacity = capacity, .length = 0, .internalArray = (int32_t *)Clay__Array_Allocate_Arena(capacity, sizeof(int32_t), arena)};
}
int32_t *Clay__int32_tArray_Get(Clay__int32_tArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__INDEX_ARRAY_DEFAULT_VALUE;
}
int32_t *Clay__int32_tArray_Add(Clay__int32_tArray *array, int32_t item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__INDEX_ARRAY_DEFAULT_VALUE;
}
void Clay__int32_tArray_Set(Clay__int32_tArray *array, int index, int32_t value) {
if (index < array->capacity && index >= 0) {
array->internalArray[index] = value;
array->length = index < array->length ? array->length : index + 1;
} else {
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Attempting to allocate array in arena, but arena is already at capacity and would overflow."));
#ifdef CLAY_OVERFLOW_TRAP
raise(SIGTRAP);
#endif
}
}
#pragma endregion
// __GENERATED__ template
Clay_LayoutElement *Clay__openLayoutElement = CLAY__NULL;
typedef struct
{
Clay_LayoutElement *layoutElement;
Clay_Vector2 position;
Clay_Vector2 nextChildOffset;
} Clay__LayoutElementTreeNode;
Clay__LayoutElementTreeNode CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT = (Clay__LayoutElementTreeNode) {};
// __GENERATED__ template array_define,array_add,array_get TYPE=Clay__LayoutElementTreeNode NAME=Clay__LayoutElementTreeNodeArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay__LayoutElementTreeNode *internalArray;
} Clay__LayoutElementTreeNodeArray;
Clay__LayoutElementTreeNodeArray Clay__LayoutElementTreeNodeArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__LayoutElementTreeNodeArray){.capacity = capacity, .length = 0, .internalArray = (Clay__LayoutElementTreeNode *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__LayoutElementTreeNode), arena)};
}
Clay__LayoutElementTreeNode *Clay__LayoutElementTreeNodeArray_Add(Clay__LayoutElementTreeNodeArray *array, Clay__LayoutElementTreeNode item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT;
}
Clay__LayoutElementTreeNode *Clay__LayoutElementTreeNodeArray_Get(Clay__LayoutElementTreeNodeArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_TREE_NODE_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
typedef struct
{
Clay_LayoutElement *layoutElement;
uint32_t parentId; // This can be zero in the case of the root layout tree
uint32_t clipElementId; // This can be zero if there is no clip element
uint32_t zIndex;
} Clay__LayoutElementTreeRoot;
Clay__LayoutElementTreeRoot CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT = (Clay__LayoutElementTreeRoot) {};
// __GENERATED__ template array_define,array_add,array_get TYPE=Clay__LayoutElementTreeRoot NAME=Clay__LayoutElementTreeRootArray DEFAULT_VALUE=&CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT
#pragma region generated
typedef struct
{
uint32_t capacity;
uint32_t length;
Clay__LayoutElementTreeRoot *internalArray;
} Clay__LayoutElementTreeRootArray;
Clay__LayoutElementTreeRootArray Clay__LayoutElementTreeRootArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) {
return (Clay__LayoutElementTreeRootArray){.capacity = capacity, .length = 0, .internalArray = (Clay__LayoutElementTreeRoot *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__LayoutElementTreeRoot), arena)};
}
Clay__LayoutElementTreeRoot *Clay__LayoutElementTreeRootArray_Add(Clay__LayoutElementTreeRootArray *array, Clay__LayoutElementTreeRoot item) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return &CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT;
}
Clay__LayoutElementTreeRoot *Clay__LayoutElementTreeRootArray_Get(Clay__LayoutElementTreeRootArray *array, int index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__LAYOUT_ELEMENT_TREE_ROOT_DEFAULT;
}
#pragma endregion
// __GENERATED__ template
Clay_Vector2 Clay__pointerPosition = (Clay_Vector2) { -1, -1 };
uint64_t Clay__arenaResetOffset = 0;
Clay_Arena Clay__internalArena;
// Layout Elements / Render Commands
Clay_LayoutElementArray Clay__layoutElements;
Clay_RenderCommandArray Clay__renderCommands;
Clay__LayoutElementPointerArray Clay__openLayoutElementStack;
Clay__LayoutElementPointerArray Clay__layoutElementChildren;
Clay__LayoutElementPointerArray Clay__layoutElementChildrenBuffer;
Clay__LayoutElementPointerArray Clay__textElementPointers;
Clay__LayoutElementPointerArray Clay__imageElementPointers;
Clay__LayoutElementPointerArray Clay__layoutElementReusableBuffer;
// Configs
Clay_LayoutConfigArray Clay__layoutConfigs;
Clay_RectangleElementConfigArray Clay__rectangleElementConfigs;
Clay_TextElementConfigArray Clay__textElementConfigs;
Clay_ImageElementConfigArray Clay__imageElementConfigs;
Clay_FloatingElementConfigArray Clay__floatingElementConfigs;
Clay_ScrollContainerElementConfigArray Clay__scrollElementConfigs;
Clay_CustomElementConfigArray Clay__customElementConfigs;
Clay_BorderContainerElementConfigArray Clay__borderElementConfigs;
// Misc Data Structures
Clay__LayoutElementTreeNodeArray Clay__layoutElementTreeNodeArray1;
Clay__LayoutElementTreeRootArray Clay__layoutElementTreeRoots;
Clay__LayoutElementHashMapItemArray Clay__layoutElementsHashMapInternal;
Clay__int32_tArray Clay__layoutElementsHashMap;
Clay__MeasureTextCacheItemArray Clay__measureTextHashMapInternal;
Clay__int32_tArray Clay__measureTextHashMap;
Clay__int32_tArray Clay__openClipElementStack;
Clay__int32_tArray Clay__pointerOverIds;
Clay__ScrollContainerDataArray Clay__scrollContainerOffsets;
Clay__BoolArray Clay__treeNodeVisited;
#if CLAY_WASM
__attribute__((import_module("clay"), import_name("measureTextFunction"))) Clay_Dimensions Clay__MeasureText(Clay_String *text, Clay_TextElementConfig *config);
#else
Clay_Dimensions (*Clay__MeasureText)(Clay_String *text, Clay_TextElementConfig *config);
#endif
Clay_String LAST_HASH;
uint32_t Clay__HashString(Clay_String key, const uint32_t offset) {
uint32_t hash = 0;
for (int i = 0; i < key.length; i++) {
hash += key.chars[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += offset;
hash += (hash << 10);
hash ^= (hash >> 6);
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
#ifdef CLAY_DEBUG
LAST_HASH = key;
LAST_HASH.length = (int)offset;
#endif
return hash + 1; // Reserve the hash result of zero as "null id"
}
uint32_t Clay__RehashWithNumber(uint32_t id, uint32_t number) {
id += number;
id += (id << 10);
id ^= (id >> 6);
id += (id << 3);
id ^= (id >> 11);
id += (id << 15);
return id;
}
uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *config) {
union {
float fontSize;
uint32_t bits;
} fontSizeBits = { .fontSize = config->fontSize };
uint32_t hash = 0;
uint64_t pointerAsNumber = (uint64_t)text->chars;
hash += pointerAsNumber;
hash += (hash << 10);
hash ^= (hash >> 6);
hash += text->length;
hash += (hash << 10);
hash ^= (hash >> 6);
hash += config->fontId;
hash += (hash << 10);
hash ^= (hash >> 6);
hash += fontSizeBits.bits;
hash += (hash << 10);
hash ^= (hash >> 6);
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash + 1; // Reserve the hash result of zero as "null id"
}
Clay_Dimensions Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) {
if (text->length < 50) {
return Clay__MeasureText(text, config);
}
uint32_t id = Clay__HashTextWithConfig(text, config);
uint32_t hashBucket = id % Clay__measureTextHashMap.capacity;
int32_t elementIndexPrevious = 0;
int32_t elementIndex = Clay__measureTextHashMap.internalArray[hashBucket];
while (elementIndex != 0) {
Clay__MeasureTextCacheItem *hashEntry = Clay__MeasureTextCacheItemArray_Get(&Clay__measureTextHashMapInternal, elementIndex);
if (hashEntry->id == id) {
return hashEntry->dimensions;
}
elementIndexPrevious = elementIndex;
elementIndex = hashEntry->nextIndex;
}
Clay_Dimensions measured = Clay__MeasureText(text, config);
if (elementIndexPrevious != 0) {
Clay__MeasureTextCacheItemArray_Get(&Clay__measureTextHashMapInternal, elementIndexPrevious)->nextIndex = (int32_t)Clay__measureTextHashMapInternal.length - 1;
} else {
Clay__measureTextHashMap.internalArray[hashBucket] = (int32_t)Clay__measureTextHashMapInternal.length - 1;
}
return measured;
}
bool Clay__PointIsInsideRect(Clay_Vector2 point, Clay_Rectangle rect) {
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
}
Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(uint32_t id, Clay_LayoutElement* layoutElement) {
Clay_LayoutElementHashMapItem item = (Clay_LayoutElementHashMapItem) { .id = id, .layoutElement = layoutElement, .nextIndex = -1 };
uint32_t hashBucket = id % Clay__layoutElementsHashMap.capacity;
int32_t hashItemPrevious = -1;
int32_t hashItemIndex = Clay__layoutElementsHashMap.internalArray[hashBucket];
while (hashItemIndex != -1) { // Just replace collision, not a big deal - leave it up to the end user
Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemIndex);
if (hashItem->id == id) { // Collision - swap out linked list item
item.nextIndex = hashItem->nextIndex;
Clay__LayoutElementHashMapItemArray_Set(&Clay__layoutElementsHashMapInternal, hashItemIndex, item);
return Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemIndex);
}
hashItemPrevious = hashItemIndex;
hashItemIndex = hashItem->nextIndex;
}
if (hashItemPrevious != -1) {
Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, hashItemPrevious)->nextIndex = (int32_t)Clay__layoutElementsHashMapInternal.length;
}
Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Add(&Clay__layoutElementsHashMapInternal, item);
Clay__layoutElementsHashMap.internalArray[hashBucket] = (int32_t)Clay__layoutElementsHashMapInternal.length - 1;
return hashItem;
}
Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) {
uint32_t hashBucket = id % Clay__layoutElementsHashMap.capacity;
int32_t elementIndex = Clay__layoutElementsHashMap.internalArray[hashBucket];
while (elementIndex != -1) {
Clay_LayoutElementHashMapItem *hashEntry = Clay__LayoutElementHashMapItemArray_Get(&Clay__layoutElementsHashMapInternal, elementIndex);
if (hashEntry->id == id) {
return hashEntry;
}
elementIndex = hashEntry->nextIndex;
}
return CLAY__NULL;
}
Clay_LayoutElement *Clay__OpenElementWithParent(uint32_t id, Clay__LayoutElementType commandType, Clay_LayoutConfig* layoutConfig, Clay_ElementConfigUnion elementConfig) {
Clay_LayoutElement layoutElement = (Clay_LayoutElement) {
#ifdef CLAY_DEBUG
.name = LAST_HASH,
#endif
.id = id,
.elementType = commandType,
.minDimensions = (Clay_Dimensions) { (float)layoutConfig->padding.x * 2, (float)layoutConfig->padding.y * 2 },
.children = (Clay__LayoutElementChildren) { .length = 0 },
.layoutConfig = layoutConfig,
.elementConfig = elementConfig,
};
if (layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {
layoutElement.dimensions.width = (float)layoutConfig->padding.x * 2;
layoutElement.minDimensions.width = CLAY__MAX(layoutElement.minDimensions.width, layoutConfig->sizing.width.sizeMinMax.min);
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.height.type != CLAY__SIZING_TYPE_PERCENT) {
layoutElement.dimensions.height = (float)layoutConfig->padding.y * 2;
layoutElement.minDimensions.height = CLAY__MAX(layoutElement.minDimensions.height, layoutConfig->sizing.height.sizeMinMax.min);
if (layoutConfig->sizing.height.sizeMinMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier
layoutConfig->sizing.height.sizeMinMax.max = CLAY__MAXFLOAT;
}
}
Clay__openLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, layoutElement);
Clay__LayoutElementPointerArray_Add(&Clay__openLayoutElementStack, Clay__openLayoutElement);
Clay__AddHashMapItem(id, Clay__openLayoutElement);
return Clay__openLayoutElement;
}
Clay_LayoutElement *Clay__OpenElement(uint32_t id, Clay__LayoutElementType commandType, Clay_LayoutConfig *layoutConfig, Clay_ElementConfigUnion elementConfig) {
Clay__openLayoutElement->children.length++;
Clay_LayoutElement *element = Clay__OpenElementWithParent(id, commandType, layoutConfig, elementConfig);
Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildrenBuffer, element);
return element;
}
void Clay__OpenContainerElement(int id, Clay_LayoutConfig *layoutConfig) {
Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ CLAY__NULL });
}
void Clay__OpenRectangleElement(int id, Clay_LayoutConfig *layoutConfig, Clay_RectangleElementConfig *rectangleConfig) {
Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_RECTANGLE, layoutConfig, (Clay_ElementConfigUnion) { .rectangleElementConfig = rectangleConfig });
}
void Clay__OpenImageContainerElement(int id, Clay_LayoutConfig *layoutConfig, Clay_ImageElementConfig *imageConfig) {
Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_IMAGE, layoutConfig, (Clay_ElementConfigUnion) { .imageElementConfig = imageConfig });
Clay__LayoutElementPointerArray_Add(&Clay__imageElementPointers, Clay__openLayoutElement);
}
void Clay__OpenBorderContainerElement(int id, Clay_LayoutConfig *layoutConfig, Clay_BorderContainerElementConfig *borderConfig) {
Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ .borderElementConfig = borderConfig });
}
void Clay__OpenCustomElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_CustomElementConfig *customElementConfig) {
Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_CUSTOM, layoutConfig, (Clay_ElementConfigUnion) { .customElementConfig = customElementConfig });
}
Clay_LayoutElement *Clay__OpenScrollElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_ScrollContainerElementConfig *scrollConfig) {
Clay_LayoutElement *scrollElement = Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ .scrollElementConfig = scrollConfig });
Clay__int32_tArray_Add(&Clay__openClipElementStack, (int)scrollElement->id);
Clay__ScrollContainerData *scrollOffset = CLAY__NULL;
for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) {
Clay__ScrollContainerData *mapping = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i);
if (id == mapping->elementId) {
scrollOffset = mapping;
scrollOffset->layoutElement = scrollElement;
scrollOffset->openThisFrame = true;
}
}
if (!scrollOffset) {
Clay__ScrollContainerDataArray_Add(&Clay__scrollContainerOffsets, (Clay__ScrollContainerData){.elementId = id, .layoutElement = scrollElement, .scrollOrigin = {-1,-1}, .openThisFrame = true});
}
return scrollElement;
}
Clay_LayoutElement *Clay__OpenFloatingContainerElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_FloatingElementConfig *floatingElementConfig) {
Clay_LayoutElement *parent = Clay__openLayoutElement;
if (floatingElementConfig->parentId > 0) {
Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId);
if (!parentItem) {
Clay_StringArray_Add(&Clay_warnings, CLAY_STRING("Clay Warning: Couldn't find parent container to attach floating container to."));
} else {
parent = parentItem->layoutElement;
}
}
Clay__OpenElementWithParent(id, CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER, layoutConfig, (Clay_ElementConfigUnion) { .floatingElementConfig = floatingElementConfig });
Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, (Clay__LayoutElementTreeRoot) {
.layoutElement = Clay__openLayoutElement,
.parentId = parent->id,
.zIndex = floatingElementConfig->zIndex,
.clipElementId = Clay__openClipElementStack.length > 0 ? (uint32_t)*Clay__int32_tArray_Get(&Clay__openClipElementStack, (int)Clay__openClipElementStack.length - 1) : 0,
});
return Clay__openLayoutElement;
}
void Clay__AttachContainerChildren() {
Clay_LayoutConfig *layoutConfig = Clay__openLayoutElement->layoutConfig;
Clay__openLayoutElement->children.elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length];
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
for (int i = 0; i < Clay__openLayoutElement->children.length; i++) {
Clay_LayoutElement *child = *Clay__LayoutElementPointerArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - Clay__openLayoutElement->children.length + i);
Clay__openLayoutElement->dimensions.width += child->dimensions.width;
Clay__openLayoutElement->dimensions.height = CLAY__MAX(Clay__openLayoutElement->dimensions.height, child->dimensions.height + layoutConfig->padding.y * 2);
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents
if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->horizontal) {
Clay__openLayoutElement->minDimensions.width += child->minDimensions.width;
}
if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->vertical) {
Clay__openLayoutElement->minDimensions.height = CLAY__MAX(Clay__openLayoutElement->minDimensions.height, child->minDimensions.height + layoutConfig->padding.y * 2);
}
Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildren, child);
}
float childGap = (float)(CLAY__MAX(Clay__openLayoutElement->children.length - 1, 0) * layoutConfig->childGap);
Clay__openLayoutElement->dimensions.width += childGap;
Clay__openLayoutElement->minDimensions.width += childGap;
}
else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
for (int i = 0; i < Clay__openLayoutElement->children.length; i++) {
Clay_LayoutElement *child = *Clay__LayoutElementPointerArray_Get(&Clay__layoutElementChildrenBuffer, (int)Clay__layoutElementChildrenBuffer.length - Clay__openLayoutElement->children.length + i);
Clay__openLayoutElement->dimensions.height += child->dimensions.height;
Clay__openLayoutElement->dimensions.width = CLAY__MAX(Clay__openLayoutElement->dimensions.width, child->dimensions.width + layoutConfig->padding.x * 2);
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents
if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->vertical) {
Clay__openLayoutElement->minDimensions.height += child->minDimensions.height;
}
if (Clay__openLayoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || !Clay__openLayoutElement->elementConfig.scrollElementConfig->horizontal) {
Clay__openLayoutElement->minDimensions.width = CLAY__MAX(Clay__openLayoutElement->minDimensions.width, child->minDimensions.width + layoutConfig->padding.x * 2);
}
Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildren, child);
}
float childGap = (float)(CLAY__MAX(Clay__openLayoutElement->children.length - 1, 0) * layoutConfig->childGap);
Clay__openLayoutElement->dimensions.height += childGap;
Clay__openLayoutElement->minDimensions.height += childGap;
}
Clay__layoutElementChildrenBuffer.length -= Clay__openLayoutElement->children.length;
}
void Clay__CloseElement() {
Clay_LayoutConfig *layoutConfig = Clay__openLayoutElement->layoutConfig;
if (layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {
// TODO I think minsize has already been applied by this point so no need to do it again
Clay__openLayoutElement->dimensions.width = CLAY__MIN(CLAY__MAX(Clay__openLayoutElement->dimensions.width, layoutConfig->sizing.width.sizeMinMax.min), layoutConfig->sizing.width.sizeMinMax.max);
} else {
Clay__openLayoutElement->dimensions.width = 0;
}
if (layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) {
Clay__openLayoutElement->dimensions.height = CLAY__MIN(CLAY__MAX(Clay__openLayoutElement->dimensions.height, layoutConfig->sizing.height.sizeMinMax.min), layoutConfig->sizing.height.sizeMinMax.max);
} else {
Clay__openLayoutElement->dimensions.height = 0;
}
Clay__LayoutElementPointerArray_RemoveSwapback(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1);
Clay__openLayoutElement = *Clay__LayoutElementPointerArray_Get(&Clay__openLayoutElementStack, (int)Clay__openLayoutElementStack.length - 1);
}
void Clay__OpenTextElement(int id, Clay_String text, Clay_TextElementConfig *textConfig) {
Clay_LayoutElement *internalElement = Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_TEXT, &CLAY_LAYOUT_DEFAULT, (Clay_ElementConfigUnion) { .textElementConfig = textConfig });
Clay_Dimensions textMeasured = Clay__MeasureTextCached(&text, textConfig);
internalElement->dimensions.width = textMeasured.width;
internalElement->dimensions.height = textMeasured.height;
internalElement->text = text;
internalElement->minDimensions = (Clay_Dimensions) { .width = textMeasured.height, .height = textMeasured.height }; // TODO not sure this is the best way to decide min width for text
Clay__LayoutElementPointerArray_Add(&Clay__textElementPointers, internalElement);
Clay__CloseElement();
}
void Clay__CloseContainerElement() {
Clay__AttachContainerChildren();
Clay__CloseElement();
}
void Clay__CloseScrollContainerElement() {
Clay__openClipElementStack.length--;
Clay__CloseContainerElement();
}
void Clay__CloseFloatingContainer() {
Clay__AttachContainerChildren();
Clay__CloseElement();
}
void Clay__InitializeEphemeralMemory(Clay_Arena *arena) {
// Ephemeral Memory - reset every frame
Clay__internalArena.nextAllocation = Clay__arenaResetOffset;
Clay__layoutElementChildrenBuffer = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__layoutElements = Clay_LayoutElementArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay_warnings = Clay_StringArray_Allocate_Arena(100, arena);
Clay__layoutConfigs = Clay_LayoutConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__rectangleElementConfigs = Clay_RectangleElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
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_ScrollContainerElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__customElementConfigs = Clay_CustomElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__borderElementConfigs = Clay_BorderContainerElementConfigArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__layoutElementChildren = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__openLayoutElementStack = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__textElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__imageElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__layoutElementReusableBuffer = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__renderCommands = Clay_RenderCommandArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__treeNodeVisited = Clay__BoolArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__treeNodeVisited.length = Clay__treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list
Clay__openClipElementStack = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
}
void Clay__InitializePersistentMemory(Clay_Arena *arena) {
Clay__scrollContainerOffsets = Clay__ScrollContainerDataArray_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__measureTextHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__pointerOverIds = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__arenaResetOffset = arena->nextAllocation;
}
typedef enum
{
CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER,
CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER,
CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER,
} Clay__SizeDistributionType;
// Because of the max and min sizing properties, we can't predict ahead of time how (or if) all the excess width
// will actually be distributed. So we keep looping until either all the excess width is distributed or
// we have exhausted all our containers that can change size along this axis
float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay__LayoutElementPointerArray resizableContainerBuffer, Clay__SizeDistributionType distributionType) {
Clay__LayoutElementPointerArray backBuffer = Clay__layoutElementReusableBuffer;
backBuffer.length = 0;
Clay__LayoutElementPointerArray remainingElements = resizableContainerBuffer;
float totalDistributedSize;
while (sizeToDistribute != 0 && remainingElements.length > 0) {
totalDistributedSize = 0;
for (int childOffset = 0; childOffset < remainingElements.length; childOffset++) {
Clay_LayoutElement *childElement = *Clay__LayoutElementPointerArray_Get(&remainingElements, childOffset);
Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height;
if ((sizeToDistribute < 0 && *childSize == childSizing.sizeMinMax.min) || (sizeToDistribute > 0 && *childSize == childSizing.sizeMinMax.max)) {
continue;
}
if (!xAxis && childElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_IMAGE) {
continue; // Currently, we don't support squishing aspect ratio images on their Y axis as it would break ratio
}
switch (distributionType) {
case CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER: break;
case CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER: if (childSizing.type != CLAY__SIZING_TYPE_GROW) { continue; } break;
case CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER: if ((childElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER || (xAxis && !childElement->elementConfig.scrollElementConfig->horizontal) || (!xAxis && !childElement->elementConfig.scrollElementConfig->vertical))) { continue; } break;
}
float dividedSize = sizeToDistribute / (float)(remainingElements.length - childOffset);
float oldChildSize = *childSize;
*childSize = CLAY__MAX(CLAY__MAX(CLAY__MIN(childSizing.sizeMinMax.max, *childSize + dividedSize), childSizing.sizeMinMax.min), childMinSize);
float diff = *childSize - oldChildSize;
if (diff != 0) {
Clay__LayoutElementPointerArray_Add(&backBuffer, childElement);
}
sizeToDistribute -= diff;
totalDistributedSize += diff;
}
if (totalDistributedSize == 0) {
break;
}
// Flip the buffers
Clay__LayoutElementPointerArray temp = remainingElements;
remainingElements = backBuffer;
backBuffer = temp;
}
return sizeToDistribute;
}
void Clay__SizeContainersAlongAxis(bool xAxis) {
Clay__LayoutElementPointerArray bfsBuffer = Clay__layoutElementChildrenBuffer;
Clay__LayoutElementPointerArray resizableContainerBuffer = Clay__openLayoutElementStack;
for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) {
bfsBuffer.length = 0;
Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex);
Clay_LayoutElement *rootElement = root->layoutElement;
Clay__LayoutElementPointerArray_Add(&bfsBuffer, root->layoutElement);
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);
for (int i = 0; i < bfsBuffer.length; ++i) {
Clay_LayoutElement *parent = *Clay__LayoutElementPointerArray_Get(&bfsBuffer, i);
Clay_LayoutConfig *parentStyleConfig = parent->layoutConfig;
float parentSize = xAxis ? parent->dimensions.width : parent->dimensions.height;
float parentPadding = (float)(xAxis ? parent->layoutConfig->padding.x : parent->layoutConfig->padding.y);
float innerContentSize = 0, totalPaddingAndChildGaps = parentPadding * 2;
bool sizingAlongAxis = (xAxis && parentStyleConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) || (!xAxis && parentStyleConfig->layoutDirection == CLAY_TOP_TO_BOTTOM);
resizableContainerBuffer.length = 0;
float parentChildGap = parentStyleConfig->childGap;
for (int childOffset = 0; childOffset < parent->children.length; childOffset++) {
Clay_LayoutElement *childElement = parent->children.elements[childOffset];
Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height;
if (childElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT && childElement->children.length > 0) {
Clay__LayoutElementPointerArray_Add(&bfsBuffer, childElement);
}
if (childSizing.type != CLAY__SIZING_TYPE_PERCENT) {
Clay__LayoutElementPointerArray_Add(&resizableContainerBuffer, childElement);
}
if (sizingAlongAxis) {
innerContentSize += (childSizing.type == CLAY__SIZING_TYPE_PERCENT ? 0 : childSize);
if (childOffset > 0) {
innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child
totalPaddingAndChildGaps += parentChildGap;
}
} else {
innerContentSize = CLAY__MAX(childSize, innerContentSize);
}
}
// Expand percentage containers to size
for (int childOffset = 0; childOffset < parent->children.length; childOffset++) {
Clay_LayoutElement *childElement = parent->children.elements[childOffset];
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;
if (sizingAlongAxis) {
innerContentSize += *childSize;
if (childOffset > 0) {
innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child
totalPaddingAndChildGaps += parentChildGap;
}
} else {
innerContentSize = CLAY__MAX(*childSize, innerContentSize);
}
}
}
if (sizingAlongAxis) {
float sizeToDistribute = parentSize - parentPadding * 2 - innerContentSize;
// If the content is too large, compress the children as much as possible
if (sizeToDistribute < 0) {
// If the parent can scroll in the axis direction in this direction, just leave the children alone
if (parent->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) {
if (((xAxis && parent->elementConfig.scrollElementConfig->horizontal) || (!xAxis && parent->elementConfig.scrollElementConfig->vertical))) {
continue;
}
}
// Scrolling containers preferentially compress before others
sizeToDistribute = Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER);
// If there is still height to make up, remove it from all containers that haven't hit their minimum size
if (sizeToDistribute < 0) {
Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER);
}
// The content is too small, allow SIZING_GROW containers to expand
} else {
Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER);
}
// Sizing along the non layout axis ("off axis")
} else {
for (int childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) {
Clay_LayoutElement *childElement = *Clay__LayoutElementPointerArray_Get(&resizableContainerBuffer, childOffset);
Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
if (!xAxis && childElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_IMAGE) {
continue; // Currently we don't support resizing aspect ratio images on the Y axis because it would break the ratio
}
// If we're laying out the children of a scroll panel, grow containers expand to the height of the inner content, not the outer container
float maxSize = parentSize - parentPadding * 2;
if (parent->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER && ((xAxis && parent->elementConfig.scrollElementConfig->horizontal) || (!xAxis && parent->elementConfig.scrollElementConfig->vertical))) {
maxSize = CLAY__MAX(maxSize, innerContentSize);
}
if (childSizing.type == CLAY__SIZING_TYPE_FIT) {
*childSize = CLAY__MAX(childSizing.sizeMinMax.min, CLAY__MIN(*childSize, maxSize));
} else if (childSizing.type == CLAY__SIZING_TYPE_GROW) {
*childSize = CLAY__MIN(maxSize, childSizing.sizeMinMax.max);
}
}
}
}
}
}
void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) {
// layoutElementsHashMap has non-linear access pattern so just resetting .length won't zero out the data.
// Need to zero it all out here
for (int i = 0; i < Clay__layoutElementsHashMap.capacity; ++i) {
Clay__layoutElementsHashMap.internalArray[i] = -1;
}
Clay__layoutElementsHashMapInternal.length = 0;
// Calculate sizing along the X axis
Clay__SizeContainersAlongAxis(true);
// Wrap text
uint32_t originalTextLayoutElementDataLength = Clay__textElementPointers.length;
for (int i = 0; i < originalTextLayoutElementDataLength; ++i) {
Clay_LayoutElement *containerElement = *Clay__LayoutElementPointerArray_Get(&Clay__textElementPointers, i);
Clay_String text = containerElement->text;
Clay_TextElementConfig *textConfig = containerElement->elementConfig.textElementConfig;
containerElement->elementType = CLAY__LAYOUT_ELEMENT_TYPE_CONTAINER;
// Clone the style config to prevent pollution of other elements that share this config
containerElement->layoutConfig = Clay_LayoutConfigArray_Add(&Clay__layoutConfigs, *containerElement->layoutConfig);
containerElement->layoutConfig->layoutDirection = CLAY_TOP_TO_BOTTOM;
containerElement->layoutConfig->childGap = textConfig->lineSpacing;
containerElement->dimensions.height = 0;
float fontSize = containerElement->elementConfig.textElementConfig->fontSize;
int lineStartIndex = 0;
int wordStartIndex = 0;
int wordEndIndex = 0;
containerElement->children = (Clay__LayoutElementChildren) { // Note: this overwrites the text property
.length = 0,
.elements = &Clay__layoutElementChildren.internalArray[Clay__layoutElementChildren.length]
};
Clay_Dimensions lineDimensions = (Clay_Dimensions){};
float spaceWidth = Clay__MeasureText(&CLAY__SPACECHAR, textConfig).width; // todo may as well cache it somewhere
while (wordStartIndex < text.length) {
if (text.chars[wordEndIndex] == ' ' || text.chars[wordEndIndex] == '\n' || wordEndIndex == text.length) {
Clay_String stringToRender = (Clay_String) { .length = wordEndIndex - lineStartIndex, .chars = text.chars + lineStartIndex };
Clay_String wordToMeasure = (Clay_String) { .length = wordEndIndex - wordStartIndex, .chars = text.chars + wordStartIndex };
// Clip off trailing spaces and newline characters
Clay_Dimensions wordDimensions = Clay__MeasureTextCached(&wordToMeasure, textConfig);
lineDimensions.width = lineDimensions.width + wordDimensions.width + spaceWidth;
lineDimensions.height = wordDimensions.height;
bool isOverlappingBoundaries = (lineDimensions.width - spaceWidth) > containerElement->dimensions.width + 0.01f; // Epsilon for floating point inaccuracy of adding components
// Need to wrap
if (isOverlappingBoundaries) {
lineDimensions.width -= spaceWidth;
// We can wrap at the most recent word start
if (wordStartIndex != lineStartIndex) {
stringToRender = (Clay_String) { .length = wordStartIndex - lineStartIndex - 1, .chars = text.chars + lineStartIndex };
lineDimensions.width -= (wordDimensions.width + spaceWidth);
lineStartIndex = wordStartIndex;
wordStartIndex = lineStartIndex;
wordEndIndex = lineStartIndex;
containerElement->dimensions.width = CLAY__MAX(containerElement->dimensions.width, lineDimensions.width);
// The single word is larger than the entire container - just render it in place
} else {
lineStartIndex = wordEndIndex + 1;
wordStartIndex = lineStartIndex;
wordEndIndex = lineStartIndex;
containerElement->dimensions.width = CLAY__MAX(containerElement->dimensions.width, lineDimensions.width);
}
// If we're at a space character and the current phrase fits, just keep going
} else if (text.chars[wordEndIndex] == ' ') {
wordStartIndex = wordEndIndex + 1;
wordEndIndex = wordStartIndex;
continue;
// Newline or end of string
} else {
lineStartIndex = wordEndIndex + 1;
wordStartIndex = lineStartIndex;
wordEndIndex = lineStartIndex;
}
Clay_LayoutElement *newTextLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, (Clay_LayoutElement) {
.id = Clay__RehashWithNumber(containerElement->id, containerElement->children.length),
.elementType = CLAY__LAYOUT_ELEMENT_TYPE_TEXT,
.text = stringToRender,
.layoutConfig = &CLAY_LAYOUT_DEFAULT,
.elementConfig.textElementConfig = containerElement->elementConfig.textElementConfig,
.dimensions = { lineDimensions.width, lineDimensions.height },
});
containerElement->dimensions.height += lineDimensions.height + (float)(containerElement->children.length > 0 ? textConfig->lineSpacing : 0);
containerElement->children.length++;
lineDimensions = (Clay_Dimensions) {};
Clay__LayoutElementPointerArray_Add(&Clay__layoutElementChildren, newTextLayoutElement);
} else {
// In the middle of a word
wordEndIndex++;
}
}
}
// Scale vertical image heights according to aspect ratio
for (int i = 0; i < Clay__imageElementPointers.length; ++i) {
Clay_LayoutElement* imageElement = *Clay__LayoutElementPointerArray_Get(&Clay__imageElementPointers, i);
Clay_ImageElementConfig *config = imageElement->elementConfig.imageElementConfig;
imageElement->dimensions.height = (config->sourceDimensions.height / CLAY__MAX(config->sourceDimensions.width, 1)) * imageElement->dimensions.width;
}
// Propagate effect of text wrapping, image aspect scaling etc. on height of parents
Clay__LayoutElementTreeNodeArray dfsBuffer = Clay__layoutElementTreeNodeArray1;
dfsBuffer.length = 0;
for (int i = 0; i < Clay__layoutElementTreeRoots.length; ++i) {
Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, i);
Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = root->layoutElement });
}
Clay__treeNodeVisited.internalArray[0] = false;
while (dfsBuffer.length > 0) {
Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);
Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement;
if (!Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
// If the element has no children or is the container for a text element, don't bother inspecting it
if (currentElement->children.length == 0 || (currentElement->children.length > 0 && currentElement->children.elements[0]->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT)) {
dfsBuffer.length--;
continue;
}
// Add the children to the DFS buffer (needs to be pushed in reverse so that stack traversal is in correct layout order)
for (int i = 0; i < currentElement->children.length; i++) {
Clay__treeNodeVisited.internalArray[dfsBuffer.length] = false;
Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = currentElement->children.elements[i] });
}
continue;
}
dfsBuffer.length--;
// DFS node has been visited, this is on the way back up to the root
Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig;
if (layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_PERCENT) {
continue;
}
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
// Resize any parent containers that have grown in height along their non layout axis
for (int j = 0; j < currentElement->children.length; ++j) {
Clay_LayoutElement *childElement = 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);
}
} else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
// Resizing along the layout axis
float contentHeight = (float)layoutConfig->padding.y * 2;
for (int j = 0; j < currentElement->children.length; ++j) {
Clay_LayoutElement *childElement = currentElement->children.elements[j];
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);
}
}
// Calculate sizing along the Y axis
Clay__SizeContainersAlongAxis(false);
// Calculate final positions and generate render commands
Clay__renderCommands.length = 0;
dfsBuffer.length = 0;
for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) {
dfsBuffer.length = 0;
Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex);
Clay_Vector2 rootPosition = (Clay_Vector2) {};
Clay_LayoutElementHashMapItem *parentHashMapItem = Clay__GetHashMapItem(root->parentId);
// Position root floating containers
if (parentHashMapItem) {
Clay_FloatingElementConfig *config = root->layoutElement->elementConfig.floatingElementConfig;
Clay_Dimensions rootDimensions = root->layoutElement->dimensions;
Clay_Rectangle parentBoundingBox = parentHashMapItem->boundingBox;
// Set X position
Clay_Vector2 targetAttachPosition = (Clay_Vector2){};
switch (config->attachment.parent) {
case CLAY_ATTACH_POINT_LEFT_TOP:
case CLAY_ATTACH_POINT_LEFT_CENTER:
case CLAY_ATTACH_POINT_LEFT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x; break;
case CLAY_ATTACH_POINT_CENTER_TOP:
case CLAY_ATTACH_POINT_CENTER_CENTER:
case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + (parentBoundingBox.width / 2); break;
case CLAY_ATTACH_POINT_RIGHT_TOP:
case CLAY_ATTACH_POINT_RIGHT_CENTER:
case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + parentBoundingBox.width; break;
}
switch (config->attachment.element) {
case CLAY_ATTACH_POINT_LEFT_TOP:
case CLAY_ATTACH_POINT_LEFT_CENTER:
case CLAY_ATTACH_POINT_LEFT_BOTTOM: break;
case CLAY_ATTACH_POINT_CENTER_TOP:
case CLAY_ATTACH_POINT_CENTER_CENTER:
case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x -= (rootDimensions.width / 2); break;
case CLAY_ATTACH_POINT_RIGHT_TOP:
case CLAY_ATTACH_POINT_RIGHT_CENTER:
case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x -= rootDimensions.width; break;
}
switch (config->attachment.parent) { // I know I could merge the x and y switch statements, but this is easier to read
case CLAY_ATTACH_POINT_LEFT_TOP:
case CLAY_ATTACH_POINT_RIGHT_TOP:
case CLAY_ATTACH_POINT_CENTER_TOP: targetAttachPosition.y = parentBoundingBox.y; break;
case CLAY_ATTACH_POINT_LEFT_CENTER:
case CLAY_ATTACH_POINT_CENTER_CENTER:
case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y = parentBoundingBox.y + (parentBoundingBox.height / 2); break;
case CLAY_ATTACH_POINT_LEFT_BOTTOM:
case CLAY_ATTACH_POINT_CENTER_BOTTOM:
case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y = parentBoundingBox.y + parentBoundingBox.height; break;
}
switch (config->attachment.element) {
case CLAY_ATTACH_POINT_LEFT_TOP:
case CLAY_ATTACH_POINT_RIGHT_TOP:
case CLAY_ATTACH_POINT_CENTER_TOP: break;
case CLAY_ATTACH_POINT_LEFT_CENTER:
case CLAY_ATTACH_POINT_CENTER_CENTER:
case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y -= (rootDimensions.height / 2); break;
case CLAY_ATTACH_POINT_LEFT_BOTTOM:
case CLAY_ATTACH_POINT_CENTER_BOTTOM:
case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y -= rootDimensions.height; break;
}
targetAttachPosition.x += config->offset.x;
targetAttachPosition.y += config->offset.y;
rootPosition = targetAttachPosition;
}
if (root->clipElementId) {
Clay_LayoutElementHashMapItem *clipHashMapItem = Clay__GetHashMapItem(root->clipElementId);
if (clipHashMapItem) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) {
.id = Clay__RehashWithNumber(root->layoutElement->id, 10), // TODO need a better strategy for managing derived ids
.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,
.boundingBox = clipHashMapItem->boundingBox,
});
}
}
Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, (Clay__LayoutElementTreeNode) { .layoutElement = root->layoutElement, .position = rootPosition, .nextChildOffset = (Clay_Vector2) { .x = (float)root->layoutElement->layoutConfig->padding.x, .y = (float)root->layoutElement->layoutConfig->padding.y } });
Clay__treeNodeVisited.internalArray[0] = false;
while (dfsBuffer.length > 0) {
Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);
Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement;
Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig;
Clay_Vector2 scrollOffset = {0};
// This will only be run a single time for each element in downwards DFS order
if (!Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
Clay_Rectangle currentElementBoundingBox = (Clay_Rectangle) { currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height };
if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER) {
Clay_FloatingElementConfig *floatingElementConfig = currentElement->elementConfig.floatingElementConfig;
Clay_Dimensions expand = floatingElementConfig->expand;
currentElementBoundingBox.x -= expand.width;
currentElementBoundingBox.width += expand.width * 2;
currentElementBoundingBox.y -= expand.height;
currentElementBoundingBox.height += expand.height * 2;
}
Clay__ScrollContainerData *scrollContainerData = CLAY__NULL;
// Apply scroll offsets to container
if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) {
.id = Clay__RehashWithNumber(currentElement->id, 10),
.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,
.boundingBox = currentElementBoundingBox,
});
// This linear scan could theoretically be slow under very strange conditions, but I can't imagine a real UI with more than a few 10's of scroll containers
for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) {
Clay__ScrollContainerData *mapping = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i);
if (mapping->layoutElement == currentElement) {
scrollContainerData = mapping;
mapping->boundingBox = currentElementBoundingBox;
Clay_ScrollContainerElementConfig *config = mapping->layoutElement->elementConfig.scrollElementConfig;
if (config->horizontal) {
scrollOffset.x = mapping->scrollPosition.x;
}
if (config->vertical) {
scrollOffset.y = mapping->scrollPosition.y;
}
break;
}
}
}
// Create the render command for this element
Clay_RenderCommand renderCommand = (Clay_RenderCommand) {
.id = currentElement->id,
.commandType = Clay__LayoutElementTypeToRenderCommandType[currentElement->elementType],
.boundingBox = currentElementBoundingBox,
.config = currentElement->elementConfig
};
Clay_LayoutElementHashMapItem *hashMapItem = Clay__AddHashMapItem(currentElement->id, currentElement);
if (hashMapItem) {
hashMapItem->boundingBox = renderCommand.boundingBox;
}
// Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow
bool offscreen = currentElementBoundingBox.x > (float)screenWidth || currentElementBoundingBox.y > (float)screenHeight || currentElementBoundingBox.x + currentElementBoundingBox.width < 0 || currentElementBoundingBox.y + currentElementBoundingBox.height < 0;
bool shouldRender = !offscreen;
switch (renderCommand.commandType) {
case CLAY_RENDER_COMMAND_TYPE_NONE: {
shouldRender = false;
break;
}
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
renderCommand.text = currentElement->text;
break;
}
case CLAY_RENDER_COMMAND_TYPE_BORDER: { // We render borders on close because they need to render above children
shouldRender = false;
break;
}
default: break;
}
if (shouldRender) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, renderCommand);
}
if (offscreen) {
// NOTE: You may be tempted to try an early return / continue if an element is off screen. Why bother calculating layout for its children, right?
// Unfortunately, a FLOATING_CONTAINER may be defined that attaches to a child or grandchild of this element, which is large enough to still
// be on screen, even if this element isn't. That depends on this element and it's children being laid out correctly (even if they are entirely off screen)
}
// Handle child alignment along the layout axis
if (currentElementTreeNode->layoutElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT) {
dfsBuffer.length += currentElement->children.length;
Clay_Dimensions contentSize = (Clay_Dimensions) {0,0};
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
for (int i = 0; i < currentElement->children.length; ++i) {
Clay_LayoutElement *childElement = currentElement->children.elements[i];
contentSize.width += childElement->dimensions.width;
contentSize.height = CLAY__MAX(contentSize.height, childElement->dimensions.height);
}
contentSize.width += (float)(CLAY__MAX(currentElement->children.length - 1, 0) * layoutConfig->childGap);
float extraSpace = currentElement->dimensions.width - (float)layoutConfig->padding.x * 2 - contentSize.width;
switch (layoutConfig->childAlignment.x) {
case CLAY_ALIGN_X_LEFT: extraSpace = 0; break;
case CLAY_ALIGN_X_CENTER: extraSpace /= 2; break;
default: break;
}
currentElementTreeNode->nextChildOffset.x += extraSpace;
} else {
for (int i = 0; i < currentElement->children.length; ++i) {
Clay_LayoutElement *childElement = currentElement->children.elements[i];
contentSize.width = CLAY__MAX(contentSize.width, childElement->dimensions.width);
contentSize.height += childElement->dimensions.height;
}
contentSize.height += (float)(CLAY__MAX(currentElement->children.length - 1, 0) * layoutConfig->childGap);
float extraSpace = currentElement->dimensions.height - (float)layoutConfig->padding.y * 2 - contentSize.height;
switch (layoutConfig->childAlignment.y) {
case CLAY_ALIGN_Y_TOP: extraSpace = 0; break;
case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break;
default: break;
}
currentElementTreeNode->nextChildOffset.y += extraSpace;
}
if (scrollContainerData) {
scrollContainerData->contentSize = contentSize;
}
}
} else {
// DFS is returning upwards backwards
if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) {
.id = Clay__RehashWithNumber(currentElement->id, 11),
.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,
});
// Borders between elements are expressed as additional rectangle render commands
} else if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_BORDER_CONTAINER) {
Clay_Rectangle currentElementBoundingBox = (Clay_Rectangle) { currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height };
Clay_BorderContainerElementConfig *borderConfig = currentElement->elementConfig.borderElementConfig;
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) {
.id = currentElement->id,
.commandType = CLAY_RENDER_COMMAND_TYPE_BORDER,
.boundingBox = currentElementBoundingBox,
.config = currentElement->elementConfig
});
// Render border elements between children
if (borderConfig->betweenChildren.width > 0 && borderConfig->betweenChildren.color.a > 0) {
Clay_Vector2 borderOffset = { (float)layoutConfig->padding.x, (float)layoutConfig->padding.y };
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
for (int i = 0; i < currentElement->children.length; ++i) {
Clay_LayoutElement *childElement = currentElement->children.elements[i];
if (i > 0) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) {
.id = Clay__RehashWithNumber(currentElement->id, 5 + i),
.commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
.boundingBox = { currentElementBoundingBox.x + borderOffset.x, currentElementBoundingBox.y, (float)borderConfig->betweenChildren.width, currentElement->dimensions.height },
.config = CLAY_RECTANGLE_CONFIG(.color = borderConfig->betweenChildren.color)
});
}
borderOffset.x += (childElement->dimensions.width + (float)layoutConfig->childGap / 2);
}
} else {
for (int i = 0; i < currentElement->children.length; ++i) {
Clay_LayoutElement *childElement = currentElement->children.elements[i];
if (i > 0) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) {
.id = Clay__RehashWithNumber(currentElement->id, 5 + i),
.commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
.boundingBox = { currentElementBoundingBox.x, currentElementBoundingBox.y + borderOffset.y, currentElement->dimensions.width, (float)borderConfig->betweenChildren.width },
.config = CLAY_RECTANGLE_CONFIG(.color = borderConfig->betweenChildren.color)
});
}
borderOffset.y += (childElement->dimensions.height + (float)layoutConfig->childGap / 2);
}
}
}
}
dfsBuffer.length--;
continue;
}
// Add children to the DFS buffer
if (currentElement->elementType != CLAY__LAYOUT_ELEMENT_TYPE_TEXT) {
for (int i = 0; i < currentElement->children.length; ++i) {
Clay_LayoutElement *childElement = currentElement->children.elements[i];
// Alignment along non layout axis
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
currentElementTreeNode->nextChildOffset.y = currentElement->layoutConfig->padding.y;
float whiteSpaceAroundChild = currentElement->dimensions.height - (float)currentElement->layoutConfig->padding.y * 2 - childElement->dimensions.height;
switch (layoutConfig->childAlignment.y) {
case CLAY_ALIGN_Y_TOP: break;
case CLAY_ALIGN_Y_CENTER: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild / 2; break;
case CLAY_ALIGN_Y_BOTTOM: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild; break;
}
} else {
currentElementTreeNode->nextChildOffset.x = currentElement->layoutConfig->padding.x;
float whiteSpaceAroundChild = currentElement->dimensions.width - (float)currentElement->layoutConfig->padding.x * 2 - childElement->dimensions.width;
switch (layoutConfig->childAlignment.x) {
case CLAY_ALIGN_X_LEFT: break;
case CLAY_ALIGN_X_CENTER: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild / 2; break;
case CLAY_ALIGN_X_RIGHT: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild; break;
}
}
Clay_Vector2 childPosition = (Clay_Vector2) {
currentElementTreeNode->position.x + currentElementTreeNode->nextChildOffset.x + scrollOffset.x,
currentElementTreeNode->position.y + currentElementTreeNode->nextChildOffset.y + scrollOffset.y,
};
// DFS buffer elements need to be added in reverse because stack traversal happens backwards
uint32_t newNodeIndex = dfsBuffer.length - 1 - i;
dfsBuffer.internalArray[newNodeIndex] = (Clay__LayoutElementTreeNode) {
.layoutElement = childElement,
.position = (Clay_Vector2) { childPosition.x, childPosition.y },
.nextChildOffset = (Clay_Vector2) { .x = (float)childElement->layoutConfig->padding.x, .y = (float)childElement->layoutConfig->padding.y },
};
Clay__treeNodeVisited.internalArray[newNodeIndex] = false;
// Update parent offsets
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
currentElementTreeNode->nextChildOffset.x += childElement->dimensions.width + (float)layoutConfig->childGap;
} else {
currentElementTreeNode->nextChildOffset.y += childElement->dimensions.height + (float)layoutConfig->childGap;
}
}
}
}
if (root->clipElementId) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { .id = Clay__RehashWithNumber(root->layoutElement->id, 11), .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END });
}
}
}
// PUBLIC API FROM HERE ---------------------------------------
CLAY_WASM_EXPORT("Clay_MinMemorySize")
uint32_t Clay_MinMemorySize() {
Clay_Arena fakeArena = (Clay_Arena) { .capacity = INT64_MAX };
Clay__InitializePersistentMemory(&fakeArena);
Clay__InitializeEphemeralMemory(&fakeArena);
return fakeArena.nextAllocation;
}
CLAY_WASM_EXPORT("Clay_CreateArenaWithCapacityAndMemory")
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset) {
Clay_Arena arena = (Clay_Arena) {
.capacity = capacity,
.memory = (char *)offset
};
return arena;
}
#ifndef CLAY_WASM
void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_String *text, Clay_TextElementConfig *config)) {
Clay__MeasureText = measureTextFunction;
}
#endif
CLAY_WASM_EXPORT("Clay_SetPointerPosition")
void Clay_SetPointerPosition(Clay_Vector2 position) {
Clay__pointerPosition = position;
Clay__pointerOverIds.length = 0;
Clay__LayoutElementPointerArray dfsBuffer = Clay__layoutElementChildrenBuffer;
for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) {
dfsBuffer.length = 0;
Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex);
Clay__LayoutElementPointerArray_Add(&dfsBuffer, root->layoutElement);
Clay__treeNodeVisited.internalArray[0] = false;
while (dfsBuffer.length > 0) {
if (Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
dfsBuffer.length--;
continue;
}
Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
Clay_LayoutElement *currentElement = *Clay__LayoutElementPointerArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);
Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO I wish there was a way around this, maybe the fact that it's essentially a binary tree limits the cost, have to measure
if ((mapItem && Clay__PointIsInsideRect(position, mapItem->boundingBox)) || (!mapItem && Clay__PointIsInsideRect(position, (Clay_Rectangle) {0,0, currentElement->dimensions.width, currentElement->dimensions.height}))) {
Clay__int32_tArray_Add(&Clay__pointerOverIds, (int)currentElement->id);
if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_TEXT) {
dfsBuffer.length--;
continue;
}
for (int i = currentElement->children.length - 1; i >= 0; --i) {
Clay__LayoutElementPointerArray_Add(&dfsBuffer, currentElement->children.elements[i]);
Clay__treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked
}
} else {
dfsBuffer.length--;
}
}
}
}
CLAY_WASM_EXPORT("Clay_Initialize")
void Clay_Initialize(Clay_Arena arena) {
Clay__internalArena = arena;
Clay__InitializePersistentMemory(&Clay__internalArena);
Clay__InitializeEphemeralMemory(&Clay__internalArena);
for (int i = 0; i < Clay__layoutElementsHashMap.capacity; ++i) {
Clay__layoutElementsHashMap.internalArray[i] = -1;
}
Clay__measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element"
}
CLAY_WASM_EXPORT("Clay_UpdateScrollContainers")
void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, float deltaTime) {
// Don't apply scroll events to ancestors of the inner element
int32_t highestPriorityElementIndex = -1;
Clay__ScrollContainerData *highestPriorityScrollData = CLAY__NULL;
for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) {
Clay__ScrollContainerData *scrollData = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i);
if (!scrollData->openThisFrame) {
Clay__ScrollContainerDataArray_RemoveSwapback(&Clay__scrollContainerOffsets, i);
continue;
}
scrollData->openThisFrame = false;
Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId);
// Element isn't rendered this frame but scroll offset has been retained
if (!hashMapItem) {
Clay__ScrollContainerDataArray_RemoveSwapback(&Clay__scrollContainerOffsets, i);
continue;
}
// Touch / click is released
if (!isPointerActive && scrollData->pointerScrollActive) {
float xDiff = scrollData->scrollPosition.x - scrollData->scrollOrigin.x;
if (xDiff < -10 || xDiff > 10) {
scrollData->scrollMomentum.x = (scrollData->scrollPosition.x - scrollData->scrollOrigin.x) / (scrollData->momentumTime * 25);
}
float yDiff = scrollData->scrollPosition.y - scrollData->scrollOrigin.y;
if (yDiff < -10 || yDiff > 10) {
scrollData->scrollMomentum.y = (scrollData->scrollPosition.y - scrollData->scrollOrigin.y) / (scrollData->momentumTime * 25);
}
scrollData->pointerScrollActive = false;
scrollData->pointerOrigin = (Clay_Vector2){0,0};
scrollData->scrollOrigin = (Clay_Vector2){0,0};
scrollData->momentumTime = 0;
}
// Apply existing momentum
scrollData->scrollPosition.x += scrollData->scrollMomentum.x;
scrollData->scrollMomentum.x *= 0.95f;
bool scrollOccurred = scrollDelta.x != 0 || scrollDelta.y != 0;
if ((scrollData->scrollMomentum.x > -0.1f && scrollData->scrollMomentum.x < 0.1f) || scrollOccurred) {
scrollData->scrollMomentum.x = 0;
}
scrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(scrollData->scrollPosition.x, 0), -(scrollData->contentSize.width - scrollData->layoutElement->dimensions.width));
scrollData->scrollPosition.y += scrollData->scrollMomentum.y;
scrollData->scrollMomentum.y *= 0.95f;
if ((scrollData->scrollMomentum.y > -0.1f && scrollData->scrollMomentum.y < 0.1f) || scrollOccurred) {
scrollData->scrollMomentum.y = 0;
}
scrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(scrollData->scrollPosition.y, 0), -(scrollData->contentSize.height - scrollData->layoutElement->dimensions.height));
for (int j = 0; j < Clay__pointerOverIds.length; ++j) { // TODO n & m are small here but this being n*m gives me the creeps
if (scrollData->layoutElement->id == *Clay__int32_tArray_Get(&Clay__pointerOverIds, j)) {
highestPriorityElementIndex = j;
highestPriorityScrollData = scrollData;
}
}
}
if (highestPriorityElementIndex > -1 && highestPriorityScrollData) {
Clay_LayoutElement *scrollElement = highestPriorityScrollData->layoutElement;
bool canScrollVertically = scrollElement->elementConfig.scrollElementConfig->vertical && highestPriorityScrollData->contentSize.height > scrollElement->dimensions.height;
bool canScrollHorizontally = scrollElement->elementConfig.scrollElementConfig->horizontal && highestPriorityScrollData->contentSize.width > scrollElement->dimensions.width;
// Handle wheel scroll
if (canScrollVertically) {
highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollPosition.y + scrollDelta.y * 10;
}
if (canScrollHorizontally) {
highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollPosition.x + scrollDelta.x * 10;
}
// Handle click / touch scroll
if (isPointerActive) {
highestPriorityScrollData->scrollMomentum = (Clay_Vector2){0};
if (!highestPriorityScrollData->pointerScrollActive) {
highestPriorityScrollData->pointerOrigin = Clay__pointerPosition;
highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition;
highestPriorityScrollData->pointerScrollActive = true;
} else {
float scrollDeltaX = 0, scrollDeltaY = 0;
if (canScrollHorizontally) {
float oldXScrollPosition = highestPriorityScrollData->scrollPosition.x;
highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollOrigin.x + (Clay__pointerPosition.x - highestPriorityScrollData->pointerOrigin.x);
highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - highestPriorityScrollData->boundingBox.width));
scrollDeltaX = highestPriorityScrollData->scrollPosition.x - oldXScrollPosition;
}
if (canScrollVertically) {
float oldYScrollPosition = highestPriorityScrollData->scrollPosition.y;
highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollOrigin.y + (Clay__pointerPosition.y - highestPriorityScrollData->pointerOrigin.y);
highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - highestPriorityScrollData->boundingBox.height));
scrollDeltaX = highestPriorityScrollData->scrollPosition.x - oldYScrollPosition;
}
if (scrollDeltaX > -0.1f && scrollDeltaX < 0.1f && scrollDeltaY > -0.1f && scrollDeltaY < 0.1f && highestPriorityScrollData->momentumTime > 0.5f) {
highestPriorityScrollData->momentumTime = 0;
highestPriorityScrollData->pointerOrigin = Clay__pointerPosition;
highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition;
} else {
highestPriorityScrollData->momentumTime += deltaTime;
}
}
}
// Clamp any changes to scroll position to the maximum size of the contents
if (canScrollVertically) {
highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - scrollElement->dimensions.height));
}
if (canScrollHorizontally) {
highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - scrollElement->dimensions.width));
}
}
}
CLAY_WASM_EXPORT("Clay_BeginLayout")
void Clay_BeginLayout(int screenWidth, int screenHeight) {
Clay__InitializeEphemeralMemory(&Clay__internalArena);
// Set up the root container that covers the entire window
Clay_LayoutElement rootLayoutElement = (Clay_LayoutElement){.layoutConfig = CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED((float)screenWidth), CLAY_SIZING_FIXED((float)screenHeight)})};
Clay__openLayoutElement = Clay_LayoutElementArray_Add(&Clay__layoutElements, rootLayoutElement);
Clay__LayoutElementPointerArray_Add(&Clay__openLayoutElementStack, Clay__openLayoutElement);
Clay__LayoutElementTreeRootArray_Add(&Clay__layoutElementTreeRoots, (Clay__LayoutElementTreeRoot) { .layoutElement = Clay__openLayoutElement });
}
CLAY_WASM_EXPORT("Clay_EndLayout")
Clay_RenderCommandArray Clay_EndLayout(int screenWidth, int screenHeight)
{
Clay__AttachContainerChildren();
Clay__CalculateFinalLayout(screenWidth, screenHeight);
return Clay__renderCommands;
}
CLAY_WASM_EXPORT("Clay_PointerOver")
bool Clay_PointerOver(uint32_t id) { // TODO return priority for separating multiple results
for (int i = 0; i < Clay__pointerOverIds.length; ++i) {
if (*Clay__int32_tArray_Get(&Clay__pointerOverIds, i) == id) {
return true;
}
}
return false;
}
#endif //CLAY_IMPLEMENTATION