summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
m---------clay0
-rw-r--r--src/Makefile36
-rw-r--r--src/clay.h4454
-rw-r--r--src/encryption.c2
-rwxr-xr-xsrc/main.c13
-rw-r--r--src/shared.c32
-rw-r--r--src/threadpool.c10
8 files changed, 4526 insertions, 24 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..691d8c8
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
1[submodule "clay"]
2 path = clay
3 url = https://github.com/nicbarker/clay
diff --git a/clay b/clay
new file mode 160000
Subproject 76ec3632d80c145158136fd44db501448e7b17c
diff --git a/src/Makefile b/src/Makefile
index 7be2382..5463c86 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,25 +2,40 @@ CC = gcc
2SHELL := /usr/bin/env 2SHELL := /usr/bin/env
3.SHELLFLAGS := -S bash -c 3.SHELLFLAGS := -S bash -c
4 4
5# I need to get better at makefiles so I can write this in a way that isn't absolutely insane/stupid 5CFLAGS := -std=c2x $$(pkg-config --cflags libsodium)
6# RELEASE_CFLAGS := -O3 -fipa-pta -fipa-cp -fuse-linker-plugin -flto=auto 6DEBUG_CFLAGS := -Wall -Wextra -Wpedantic -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0
7# RELEASE_LDFLAGS := -fuse-linker-plugin -flto=auto 7RELEASE_CFLAGS := -O3 -fipa-pta -fipa-cp -fuse-linker-plugin -flto=auto
8 8
9CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 9DEFS := -D_GNU_SOURCE=1
10DEBUG_DEFS := -DDEBUG=1
11RELEASE_DEFS := -DRELEASE=1
10 12
11CFLAGS += $$(pkg-config --cflags libsodium) 13LDLIBS := $$(pkg-config --libs-only-l libsodium)
12LDLIBS += $$(pkg-config --libs-only-l libsodium) 14
13LDFLAGS += $$(pkg-config --libs-only-L libsodium) 15LDFLAGS := $$(pkg-config --libs-only-L libsodium)
16RELEASE_LDFLAGS := -fuse-linker-plugin -flto=auto
14 17
15SOURCES := $(wildcard *.c) 18SOURCES := $(wildcard *.c)
16TIMESTAMP_DIR := .timestamps 19TIMESTAMP_DIR := .timestamps
17TIMESTAMPS := $(patsubst %.c,$(TIMESTAMP_DIR)/%.t,$(SOURCES)) 20TIMESTAMPS := $(patsubst %.c,$(TIMESTAMP_DIR)/%.t,$(SOURCES))
18 21
19.PHONY: all c clean v val t test 22.PHONY: debug release c clean v val t test test
20.DELETE_ON_ERROR: 23.DELETE_ON_ERROR:
21.ONESHELL: 24.ONESHELL:
22 25
23all: main 26tests debug: CFLAGS += $(DEBUG_CFLAGS)
27tests debug: DEFS += $(DEBUG_DEFS)
28debug: main
29
30release: CFLAGS += $(RELEASE_CFLAGS)
31release: LDFLAGS += $(RELEASE_LDFLAGS)
32release: DEFS += $(RELEASE_DEFS)
33release: main
34
35clay.h:
36 cd ..
37 git submodule update --init --recursive
38 cp -fl clay/clay.h src/
24 39
25$(TIMESTAMP_DIR): 40$(TIMESTAMP_DIR):
26 mkdir -p $(TIMESTAMP_DIR) 41 mkdir -p $(TIMESTAMP_DIR)
@@ -28,8 +43,9 @@ $(TIMESTAMP_DIR):
28$(TIMESTAMPS): $(TIMESTAMP_DIR)/%.t: %.c | $(TIMESTAMP_DIR) 43$(TIMESTAMPS): $(TIMESTAMP_DIR)/%.t: %.c | $(TIMESTAMP_DIR)
29 touch $@ 44 touch $@
30 45
46main: clay.h
31main tests: %: %.c $(TIMESTAMPS) 47main tests: %: %.c $(TIMESTAMPS)
32 $(CC) -D_GNU_SOURCE=1 $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ 48 $(CC) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
33 49
34 50
35# Phony rules 51# Phony rules
diff --git a/src/clay.h b/src/clay.h
new file mode 100644
index 0000000..80d8393
--- /dev/null
+++ b/src/clay.h
@@ -0,0 +1,4454 @@
1// VERSION: 0.14
2
3/*
4 NOTE: In order to use this library you must define
5 the following macro in exactly one file, _before_ including clay.h:
6
7 #define CLAY_IMPLEMENTATION
8 #include "clay.h"
9
10 See the examples folder for details.
11*/
12
13#include <stdint.h>
14#include <stdbool.h>
15#include <stddef.h>
16
17// SIMD includes on supported platforms
18#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
19#include <emmintrin.h>
20#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)
21#include <arm_neon.h>
22#endif
23
24// -----------------------------------------
25// HEADER DECLARATIONS ---------------------
26// -----------------------------------------
27
28#ifndef CLAY_HEADER
29#define CLAY_HEADER
30
31#if !( \
32 (defined(__cplusplus) && __cplusplus >= 202002L) || \
33 (defined(__STDC__) && __STDC__ == 1 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \
34 defined(_MSC_VER) || \
35 defined(__OBJC__) \
36)
37#error "Clay requires C99, C++20, or MSVC"
38#endif
39
40#ifdef CLAY_WASM
41#define CLAY_WASM_EXPORT(name) __attribute__((export_name(name)))
42#else
43#define CLAY_WASM_EXPORT(null)
44#endif
45
46#ifdef CLAY_DLL
47#define CLAY_DLL_EXPORT __declspec(dllexport) __stdcall
48#else
49#define CLAY_DLL_EXPORT
50#endif
51
52// Public Macro API ------------------------
53
54#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))
55#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y))
56
57#define CLAY_TEXT_CONFIG(...) Clay__StoreTextElementConfig(CLAY__CONFIG_WRAPPER(Clay_TextElementConfig, __VA_ARGS__))
58
59#define CLAY_BORDER_OUTSIDE(widthValue) {widthValue, widthValue, widthValue, widthValue, 0}
60
61#define CLAY_BORDER_ALL(widthValue) {widthValue, widthValue, widthValue, widthValue, widthValue}
62
63#define CLAY_CORNER_RADIUS(radius) (CLAY__INIT(Clay_CornerRadius) { radius, radius, radius, radius })
64
65#define CLAY_PADDING_ALL(padding) CLAY__CONFIG_WRAPPER(Clay_Padding, { padding, padding, padding, padding })
66
67#define CLAY_SIZING_FIT(...) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { __VA_ARGS__ } }, .type = CLAY__SIZING_TYPE_FIT })
68
69#define CLAY_SIZING_GROW(...) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { __VA_ARGS__ } }, .type = CLAY__SIZING_TYPE_GROW })
70
71#define CLAY_SIZING_FIXED(fixedSize) (CLAY__INIT(Clay_SizingAxis) { .size = { .minMax = { fixedSize, fixedSize } }, .type = CLAY__SIZING_TYPE_FIXED })
72
73#define CLAY_SIZING_PERCENT(percentOfParent) (CLAY__INIT(Clay_SizingAxis) { .size = { .percent = (percentOfParent) }, .type = CLAY__SIZING_TYPE_PERCENT })
74
75// Note: If a compile error led you here, you might be trying to use CLAY_ID with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SID instead.
76#define CLAY_ID(label) CLAY_SID(CLAY_STRING(label))
77
78#define CLAY_SID(label) Clay__HashString(label, 0)
79
80// Note: If a compile error led you here, you might be trying to use CLAY_IDI with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SIDI instead.
81#define CLAY_IDI(label, index) CLAY_SIDI(CLAY_STRING(label), index)
82
83#define CLAY_SIDI(label, index) Clay__HashStringWithOffset(label, index, 0)
84
85// Note: If a compile error led you here, you might be trying to use CLAY_ID_LOCAL with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SID_LOCAL instead.
86#define CLAY_ID_LOCAL(label) CLAY_SID_LOCAL(CLAY_STRING(label))
87
88#define CLAY_SID_LOCAL(label) Clay__HashString(label, Clay__GetParentElementId())
89
90// Note: If a compile error led you here, you might be trying to use CLAY_IDI_LOCAL with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SIDI_LOCAL instead.
91#define CLAY_IDI_LOCAL(label, index) CLAY_SIDI_LOCAL(CLAY_STRING(label), index)
92
93#define CLAY_SIDI_LOCAL(label, index) Clay__HashStringWithOffset(label, index, Clay__GetParentElementId())
94
95#define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof((s)[0])) - sizeof((s)[0]))
96
97#define CLAY__ENSURE_STRING_LITERAL(x) ("" x "")
98
99// Note: If an error led you here, it's because CLAY_STRING can only be used with string literals, i.e. CLAY_STRING("SomeString") and not CLAY_STRING(yourString)
100#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) })
101
102#define CLAY_STRING_CONST(string) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }
103
104static uint8_t CLAY__ELEMENT_DEFINITION_LATCH;
105
106// GCC marks the above CLAY__ELEMENT_DEFINITION_LATCH as an unused variable for files that include clay.h but don't declare any layout
107// This is to suppress that warning
108static inline void Clay__SuppressUnusedLatchDefinitionVariableWarning(void) { (void) CLAY__ELEMENT_DEFINITION_LATCH; }
109
110// Publicly visible layout element macros -----------------------------------------------------
111
112/* This macro looks scary on the surface, but is actually quite simple.
113 It turns a macro call like this:
114
115 CLAY({
116 .id = CLAY_ID("Container"),
117 .backgroundColor = { 255, 200, 200, 255 }
118 }) {
119 ...children declared here
120 }
121
122 Into calls like this:
123
124 Clay__OpenElement();
125 Clay__ConfigureOpenElement((Clay_ElementDeclaration) {
126 .id = CLAY_ID("Container"),
127 .backgroundColor = { 255, 200, 200, 255 }
128 });
129 ...children declared here
130 Clay__CloseElement();
131
132 The for loop will only ever run a single iteration, putting Clay__CloseElement() in the increment of the loop
133 means that it will run after the body - where the children are declared. It just exists to make sure you don't forget
134 to call Clay_CloseElement().
135*/
136#define CLAY_AUTO_ID(...) \
137 for ( \
138 CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElement(), Clay__ConfigureOpenElement(CLAY__CONFIG_WRAPPER(Clay_ElementDeclaration, __VA_ARGS__)), 0); \
139 CLAY__ELEMENT_DEFINITION_LATCH < 1; \
140 CLAY__ELEMENT_DEFINITION_LATCH=1, Clay__CloseElement() \
141 )
142
143#define CLAY(id, ...) \
144 for ( \
145 CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElementWithId(id), Clay__ConfigureOpenElement(CLAY__CONFIG_WRAPPER(Clay_ElementDeclaration, __VA_ARGS__)), 0); \
146 CLAY__ELEMENT_DEFINITION_LATCH < 1; \
147 CLAY__ELEMENT_DEFINITION_LATCH=1, Clay__CloseElement() \
148 )
149
150// These macros exist to allow the CLAY() macro to be called both with an inline struct definition, such as
151// CLAY({ .id = something... });
152// As well as by passing a predefined declaration struct
153// Clay_ElementDeclaration declarationStruct = ...
154// CLAY(declarationStruct);
155#define CLAY__WRAPPER_TYPE(type) Clay__##type##Wrapper
156#define CLAY__WRAPPER_STRUCT(type) typedef struct { type wrapped; } CLAY__WRAPPER_TYPE(type)
157#define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(CLAY__WRAPPER_TYPE(type)) { __VA_ARGS__ }).wrapped
158
159#define CLAY_TEXT(text, textConfig) Clay__OpenTextElement(text, textConfig)
160
161#ifdef __cplusplus
162
163#define CLAY__INIT(type) type
164
165#define CLAY_PACKED_ENUM enum : uint8_t
166
167#define CLAY__DEFAULT_STRUCT {}
168
169#else
170
171#define CLAY__INIT(type) (type)
172
173#if defined(_MSC_VER) && !defined(__clang__)
174#define CLAY_PACKED_ENUM __pragma(pack(push, 1)) enum __pragma(pack(pop))
175#else
176#define CLAY_PACKED_ENUM enum __attribute__((__packed__))
177#endif
178
179#if __STDC_VERSION__ >= 202311L
180#define CLAY__DEFAULT_STRUCT {}
181#else
182#define CLAY__DEFAULT_STRUCT {0}
183#endif
184
185#endif // __cplusplus
186
187#ifdef __cplusplus
188extern "C" {
189#endif
190
191// Utility Structs -------------------------
192
193// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
194// but it is also used to represent slices.
195typedef struct Clay_String {
196 // Set this boolean to true if the char* data underlying this string will live for the entire lifetime of the program.
197 // This will automatically be set for strings created with CLAY_STRING, as the macro requires a string literal.
198 bool isStaticallyAllocated;
199 int32_t length;
200 // The underlying character memory. Note: this will not be copied and will not extend the lifetime of the underlying memory.
201 const char *chars;
202} Clay_String;
203
204// Clay_StringSlice is used to represent non owning string slices, and includes
205// a baseChars field which points to the string this slice is derived from.
206typedef struct Clay_StringSlice {
207 int32_t length;
208 const char *chars;
209 const char *baseChars; // The source string / char* that this slice was derived from
210} Clay_StringSlice;
211
212typedef struct Clay_Context Clay_Context;
213
214// Clay_Arena is a memory arena structure that is used by clay to manage its internal allocations.
215// Rather than creating it by hand, it's easier to use Clay_CreateArenaWithCapacityAndMemory()
216typedef struct Clay_Arena {
217 uintptr_t nextAllocation;
218 size_t capacity;
219 char *memory;
220} Clay_Arena;
221
222typedef struct Clay_Dimensions {
223 float width, height;
224} Clay_Dimensions;
225
226typedef struct Clay_Vector2 {
227 float x, y;
228} Clay_Vector2;
229
230// Internally clay conventionally represents colors as 0-255, but interpretation is up to the renderer.
231typedef struct Clay_Color {
232 float r, g, b, a;
233} Clay_Color;
234
235typedef struct Clay_BoundingBox {
236 float x, y, width, height;
237} Clay_BoundingBox;
238
239// Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros.
240// Represents a hashed string ID used for identifying and finding specific clay UI elements, required
241// by functions such as Clay_PointerOver() and Clay_GetElementData().
242typedef struct Clay_ElementId {
243 uint32_t id; // The resulting hash generated from the other fields.
244 uint32_t offset; // A numerical offset applied after computing the hash from stringId.
245 uint32_t baseId; // A base hash value to start from, for example the parent element ID is used when calculating CLAY_ID_LOCAL().
246 Clay_String stringId; // The string id to hash.
247} Clay_ElementId;
248
249// A sized array of Clay_ElementId.
250typedef struct
251{
252 int32_t capacity;
253 int32_t length;
254 Clay_ElementId *internalArray;
255} Clay_ElementIdArray;
256
257// Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
258// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
259typedef struct Clay_CornerRadius {
260 float topLeft;
261 float topRight;
262 float bottomLeft;
263 float bottomRight;
264} Clay_CornerRadius;
265
266// Element Configs ---------------------------
267
268// Controls the direction in which child elements will be automatically laid out.
269typedef CLAY_PACKED_ENUM {
270 // (Default) Lays out child elements from left to right with increasing x.
271 CLAY_LEFT_TO_RIGHT,
272 // Lays out child elements from top to bottom with increasing y.
273 CLAY_TOP_TO_BOTTOM,
274} Clay_LayoutDirection;
275
276// Controls the alignment along the x axis (horizontal) of child elements.
277typedef CLAY_PACKED_ENUM {
278 // (Default) Aligns child elements to the left hand side of this element, offset by padding.width.left
279 CLAY_ALIGN_X_LEFT,
280 // Aligns child elements to the right hand side of this element, offset by padding.width.right
281 CLAY_ALIGN_X_RIGHT,
282 // Aligns child elements horizontally to the center of this element
283 CLAY_ALIGN_X_CENTER,
284} Clay_LayoutAlignmentX;
285
286// Controls the alignment along the y axis (vertical) of child elements.
287typedef CLAY_PACKED_ENUM {
288 // (Default) Aligns child elements to the top of this element, offset by padding.width.top
289 CLAY_ALIGN_Y_TOP,
290 // Aligns child elements to the bottom of this element, offset by padding.width.bottom
291 CLAY_ALIGN_Y_BOTTOM,
292 // Aligns child elements vertically to the center of this element
293 CLAY_ALIGN_Y_CENTER,
294} Clay_LayoutAlignmentY;
295
296// Controls how the element takes up space inside its parent container.
297typedef CLAY_PACKED_ENUM {
298 // (default) Wraps tightly to the size of the element's contents.
299 CLAY__SIZING_TYPE_FIT,
300 // Expands along this axis to fill available space in the parent element, sharing it with other GROW elements.
301 CLAY__SIZING_TYPE_GROW,
302 // Expects 0-1 range. Clamps the axis size to a percent of the parent container's axis size minus padding and child gaps.
303 CLAY__SIZING_TYPE_PERCENT,
304 // Clamps the axis size to an exact size in pixels.
305 CLAY__SIZING_TYPE_FIXED,
306} Clay__SizingType;
307
308// Controls how child elements are aligned on each axis.
309typedef struct Clay_ChildAlignment {
310 Clay_LayoutAlignmentX x; // Controls alignment of children along the x axis.
311 Clay_LayoutAlignmentY y; // Controls alignment of children along the y axis.
312} Clay_ChildAlignment;
313
314// Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to,
315// overriding sizing types such as FIT or GROW.
316typedef struct Clay_SizingMinMax {
317 float min; // The smallest final size of the element on this axis will be this value in pixels.
318 float max; // The largest final size of the element on this axis will be this value in pixels.
319} Clay_SizingMinMax;
320
321// Controls the sizing of this element along one axis inside its parent container.
322typedef struct Clay_SizingAxis {
323 union {
324 Clay_SizingMinMax minMax; // Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to, overriding sizing types such as FIT or GROW.
325 float percent; // Expects 0-1 range. Clamps the axis size to a percent of the parent container's axis size minus padding and child gaps.
326 } size;
327 Clay__SizingType type; // Controls how the element takes up space inside its parent container.
328} Clay_SizingAxis;
329
330// Controls the sizing of this element along one axis inside its parent container.
331typedef struct Clay_Sizing {
332 Clay_SizingAxis width; // Controls the width sizing of the element, along the x axis.
333 Clay_SizingAxis height; // Controls the height sizing of the element, along the y axis.
334} Clay_Sizing;
335
336// Controls "padding" in pixels, which is a gap between the bounding box of this element and where its children
337// will be placed.
338typedef struct Clay_Padding {
339 uint16_t left;
340 uint16_t right;
341 uint16_t top;
342 uint16_t bottom;
343} Clay_Padding;
344
345CLAY__WRAPPER_STRUCT(Clay_Padding);
346
347// Controls various settings that affect the size and position of an element, as well as the sizes and positions
348// of any child elements.
349typedef struct Clay_LayoutConfig {
350 Clay_Sizing sizing; // Controls the sizing of this element inside it's parent container, including FIT, GROW, PERCENT and FIXED sizing.
351 Clay_Padding padding; // Controls "padding" in pixels, which is a gap between the bounding box of this element and where its children will be placed.
352 uint16_t childGap; // Controls the gap in pixels between child elements along the layout axis (horizontal gap for LEFT_TO_RIGHT, vertical gap for TOP_TO_BOTTOM).
353 Clay_ChildAlignment childAlignment; // Controls how child elements are aligned on each axis.
354 Clay_LayoutDirection layoutDirection; // Controls the direction in which child elements will be automatically laid out.
355} Clay_LayoutConfig;
356
357CLAY__WRAPPER_STRUCT(Clay_LayoutConfig);
358
359extern Clay_LayoutConfig CLAY_LAYOUT_DEFAULT;
360
361// Controls how text "wraps", that is how it is broken into multiple lines when there is insufficient horizontal space.
362typedef CLAY_PACKED_ENUM {
363 // (default) breaks on whitespace characters.
364 CLAY_TEXT_WRAP_WORDS,
365 // Don't break on space characters, only on newlines.
366 CLAY_TEXT_WRAP_NEWLINES,
367 // Disable text wrapping entirely.
368 CLAY_TEXT_WRAP_NONE,
369} Clay_TextElementConfigWrapMode;
370
371// Controls how wrapped lines of text are horizontally aligned within the outer text bounding box.
372typedef CLAY_PACKED_ENUM {
373 // (default) Horizontally aligns wrapped lines of text to the left hand side of their bounding box.
374 CLAY_TEXT_ALIGN_LEFT,
375 // Horizontally aligns wrapped lines of text to the center of their bounding box.
376 CLAY_TEXT_ALIGN_CENTER,
377 // Horizontally aligns wrapped lines of text to the right hand side of their bounding box.
378 CLAY_TEXT_ALIGN_RIGHT,
379} Clay_TextAlignment;
380
381// Controls various functionality related to text elements.
382typedef struct Clay_TextElementConfig {
383 // A pointer that will be transparently passed through to the resulting render command.
384 void *userData;
385 // The RGBA color of the font to render, conventionally specified as 0-255.
386 Clay_Color textColor;
387 // An integer transparently passed to Clay_MeasureText to identify the font to use.
388 // The debug view will pass fontId = 0 for its internal text.
389 uint16_t fontId;
390 // Controls the size of the font. Handled by the function provided to Clay_MeasureText.
391 uint16_t fontSize;
392 // Controls extra horizontal spacing between characters. Handled by the function provided to Clay_MeasureText.
393 uint16_t letterSpacing;
394 // Controls additional vertical space between wrapped lines of text.
395 uint16_t lineHeight;
396 // Controls how text "wraps", that is how it is broken into multiple lines when there is insufficient horizontal space.
397 // CLAY_TEXT_WRAP_WORDS (default) breaks on whitespace characters.
398 // CLAY_TEXT_WRAP_NEWLINES doesn't break on space characters, only on newlines.
399 // CLAY_TEXT_WRAP_NONE disables wrapping entirely.
400 Clay_TextElementConfigWrapMode wrapMode;
401 // Controls how wrapped lines of text are horizontally aligned within the outer text bounding box.
402 // CLAY_TEXT_ALIGN_LEFT (default) - Horizontally aligns wrapped lines of text to the left hand side of their bounding box.
403 // CLAY_TEXT_ALIGN_CENTER - Horizontally aligns wrapped lines of text to the center of their bounding box.
404 // CLAY_TEXT_ALIGN_RIGHT - Horizontally aligns wrapped lines of text to the right hand side of their bounding box.
405 Clay_TextAlignment textAlignment;
406} Clay_TextElementConfig;
407
408CLAY__WRAPPER_STRUCT(Clay_TextElementConfig);
409
410// Aspect Ratio --------------------------------
411
412// Controls various settings related to aspect ratio scaling element.
413typedef struct Clay_AspectRatioElementConfig {
414 float aspectRatio; // A float representing the target "Aspect ratio" for an element, which is its final width divided by its final height.
415} Clay_AspectRatioElementConfig;
416
417CLAY__WRAPPER_STRUCT(Clay_AspectRatioElementConfig);
418
419// Image --------------------------------
420
421// Controls various settings related to image elements.
422typedef struct Clay_ImageElementConfig {
423 void* imageData; // A transparent pointer used to pass image data through to the renderer.
424} Clay_ImageElementConfig;
425
426CLAY__WRAPPER_STRUCT(Clay_ImageElementConfig);
427
428// Floating -----------------------------
429
430// Controls where a floating element is offset relative to its parent element.
431// Note: see https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce for a visual explanation.
432typedef CLAY_PACKED_ENUM {
433 CLAY_ATTACH_POINT_LEFT_TOP,
434 CLAY_ATTACH_POINT_LEFT_CENTER,
435 CLAY_ATTACH_POINT_LEFT_BOTTOM,
436 CLAY_ATTACH_POINT_CENTER_TOP,
437 CLAY_ATTACH_POINT_CENTER_CENTER,
438 CLAY_ATTACH_POINT_CENTER_BOTTOM,
439 CLAY_ATTACH_POINT_RIGHT_TOP,
440 CLAY_ATTACH_POINT_RIGHT_CENTER,
441 CLAY_ATTACH_POINT_RIGHT_BOTTOM,
442} Clay_FloatingAttachPointType;
443
444// Controls where a floating element is offset relative to its parent element.
445typedef struct Clay_FloatingAttachPoints {
446 Clay_FloatingAttachPointType element; // Controls the origin point on a floating element that attaches to its parent.
447 Clay_FloatingAttachPointType parent; // Controls the origin point on the parent element that the floating element attaches to.
448} Clay_FloatingAttachPoints;
449
450// Controls how mouse pointer events like hover and click are captured or passed through to elements underneath a floating element.
451typedef CLAY_PACKED_ENUM {
452 // (default) "Capture" the pointer event and don't allow events like hover and click to pass through to elements underneath.
453 CLAY_POINTER_CAPTURE_MODE_CAPTURE,
454 // CLAY_POINTER_CAPTURE_MODE_PARENT, TODO pass pointer through to attached parent
455
456 // Transparently pass through pointer events like hover and click to elements underneath the floating element.
457 CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
458} Clay_PointerCaptureMode;
459
460// Controls which element a floating element is "attached" to (i.e. relative offset from).
461typedef CLAY_PACKED_ENUM {
462 // (default) Disables floating for this element.
463 CLAY_ATTACH_TO_NONE,
464 // Attaches this floating element to its parent, positioned based on the .attachPoints and .offset fields.
465 CLAY_ATTACH_TO_PARENT,
466 // Attaches this floating element to an element with a specific ID, specified with the .parentId field. positioned based on the .attachPoints and .offset fields.
467 CLAY_ATTACH_TO_ELEMENT_WITH_ID,
468 // Attaches this floating element to the root of the layout, which combined with the .offset field provides functionality similar to "absolute positioning".
469 CLAY_ATTACH_TO_ROOT,
470} Clay_FloatingAttachToElement;
471
472// Controls whether or not a floating element is clipped to the same clipping rectangle as the element it's attached to.
473typedef CLAY_PACKED_ENUM {
474 // (default) - The floating element does not inherit clipping.
475 CLAY_CLIP_TO_NONE,
476 // The floating element is clipped to the same clipping rectangle as the element it's attached to.
477 CLAY_CLIP_TO_ATTACHED_PARENT
478} Clay_FloatingClipToElement;
479
480// Controls various settings related to "floating" elements, which are elements that "float" above other elements, potentially overlapping their boundaries,
481// and not affecting the layout of sibling or parent elements.
482typedef struct Clay_FloatingElementConfig {
483 // Offsets this floating element by the provided x,y coordinates from its attachPoints.
484 Clay_Vector2 offset;
485 // Expands the boundaries of the outer floating element without affecting its children.
486 Clay_Dimensions expand;
487 // When used in conjunction with .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, attaches this floating element to the element in the hierarchy with the provided ID.
488 // Hint: attach the ID to the other element with .id = CLAY_ID("yourId"), and specify the id the same way, with .parentId = CLAY_ID("yourId").id
489 uint32_t parentId;
490 // Controls the z index of this floating element and all its children. Floating elements are sorted in ascending z order before output.
491 // zIndex is also passed to the renderer for all elements contained within this floating element.
492 int16_t zIndex;
493 // Controls how mouse pointer events like hover and click are captured or passed through to elements underneath / behind a floating element.
494 // Enum is of the form CLAY_ATTACH_POINT_foo_bar. See Clay_FloatingAttachPoints for more details.
495 // Note: see <img src="https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce />
496 // and <img src="https://github.com/user-attachments/assets/ebe75e0d-1904-46b0-982d-418f929d1516 /> for a visual explanation.
497 Clay_FloatingAttachPoints attachPoints;
498 // Controls how mouse pointer events like hover and click are captured or passed through to elements underneath a floating element.
499 // CLAY_POINTER_CAPTURE_MODE_CAPTURE (default) - "Capture" the pointer event and don't allow events like hover and click to pass through to elements underneath.
500 // CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH - Transparently pass through pointer events like hover and click to elements underneath the floating element.
501 Clay_PointerCaptureMode pointerCaptureMode;
502 // Controls which element a floating element is "attached" to (i.e. relative offset from).
503 // CLAY_ATTACH_TO_NONE (default) - Disables floating for this element.
504 // CLAY_ATTACH_TO_PARENT - Attaches this floating element to its parent, positioned based on the .attachPoints and .offset fields.
505 // CLAY_ATTACH_TO_ELEMENT_WITH_ID - Attaches this floating element to an element with a specific ID, specified with the .parentId field. positioned based on the .attachPoints and .offset fields.
506 // CLAY_ATTACH_TO_ROOT - Attaches this floating element to the root of the layout, which combined with the .offset field provides functionality similar to "absolute positioning".
507 Clay_FloatingAttachToElement attachTo;
508 // Controls whether or not a floating element is clipped to the same clipping rectangle as the element it's attached to.
509 // CLAY_CLIP_TO_NONE (default) - The floating element does not inherit clipping.
510 // CLAY_CLIP_TO_ATTACHED_PARENT - The floating element is clipped to the same clipping rectangle as the element it's attached to.
511 Clay_FloatingClipToElement clipTo;
512} Clay_FloatingElementConfig;
513
514CLAY__WRAPPER_STRUCT(Clay_FloatingElementConfig);
515
516// Custom -----------------------------
517
518// Controls various settings related to custom elements.
519typedef struct Clay_CustomElementConfig {
520 // A transparent pointer through which you can pass custom data to the renderer.
521 // Generates CUSTOM render commands.
522 void* customData;
523} Clay_CustomElementConfig;
524
525CLAY__WRAPPER_STRUCT(Clay_CustomElementConfig);
526
527// Scroll -----------------------------
528
529// Controls the axis on which an element switches to "scrolling", which clips the contents and allows scrolling in that direction.
530typedef struct Clay_ClipElementConfig {
531 bool horizontal; // Clip overflowing elements on the X axis.
532 bool vertical; // Clip overflowing elements on the Y axis.
533 Clay_Vector2 childOffset; // Offsets the x,y positions of all child elements. Used primarily for scrolling containers.
534} Clay_ClipElementConfig;
535
536CLAY__WRAPPER_STRUCT(Clay_ClipElementConfig);
537
538// Border -----------------------------
539
540// Controls the widths of individual element borders.
541typedef struct Clay_BorderWidth {
542 uint16_t left;
543 uint16_t right;
544 uint16_t top;
545 uint16_t bottom;
546 // Creates borders between each child element, depending on the .layoutDirection.
547 // e.g. for LEFT_TO_RIGHT, borders will be vertical lines, and for TOP_TO_BOTTOM borders will be horizontal lines.
548 // .betweenChildren borders will result in individual RECTANGLE render commands being generated.
549 uint16_t betweenChildren;
550} Clay_BorderWidth;
551
552// Controls settings related to element borders.
553typedef struct Clay_BorderElementConfig {
554 Clay_Color color; // Controls the color of all borders with width > 0. Conventionally represented as 0-255, but interpretation is up to the renderer.
555 Clay_BorderWidth width; // Controls the widths of individual borders. At least one of these should be > 0 for a BORDER render command to be generated.
556} Clay_BorderElementConfig;
557
558CLAY__WRAPPER_STRUCT(Clay_BorderElementConfig);
559
560// Render Command Data -----------------------------
561
562// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT
563typedef struct Clay_TextRenderData {
564 // A string slice containing the text to be rendered.
565 // Note: this is not guaranteed to be null terminated.
566 Clay_StringSlice stringContents;
567 // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
568 Clay_Color textColor;
569 // An integer representing the font to use to render this text, transparently passed through from the text declaration.
570 uint16_t fontId;
571 uint16_t fontSize;
572 // Specifies the extra whitespace gap in pixels between each character.
573 uint16_t letterSpacing;
574 // The height of the bounding box for this line of text.
575 uint16_t lineHeight;
576} Clay_TextRenderData;
577
578// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE
579typedef struct Clay_RectangleRenderData {
580 // The solid background color to fill this rectangle with. Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
581 Clay_Color backgroundColor;
582 // Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
583 // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
584 Clay_CornerRadius cornerRadius;
585} Clay_RectangleRenderData;
586
587// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE
588typedef struct Clay_ImageRenderData {
589 // The tint color for this image. Note that the default value is 0,0,0,0 and should likely be interpreted
590 // as "untinted".
591 // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
592 Clay_Color backgroundColor;
593 // Controls the "radius", or corner rounding of this image.
594 // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
595 Clay_CornerRadius cornerRadius;
596 // A pointer transparently passed through from the original element definition, typically used to represent image data.
597 void* imageData;
598} Clay_ImageRenderData;
599
600// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM
601typedef struct Clay_CustomRenderData {
602 // Passed through from .backgroundColor in the original element declaration.
603 // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
604 Clay_Color backgroundColor;
605 // Controls the "radius", or corner rounding of this custom element.
606 // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
607 Clay_CornerRadius cornerRadius;
608 // A pointer transparently passed through from the original element definition.
609 void* customData;
610} Clay_CustomRenderData;
611
612// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START || commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_END
613typedef struct Clay_ScrollRenderData {
614 bool horizontal;
615 bool vertical;
616} Clay_ClipRenderData;
617
618// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER
619typedef struct Clay_BorderRenderData {
620 // Controls a shared color for all this element's borders.
621 // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
622 Clay_Color color;
623 // Specifies the "radius", or corner rounding of this border element.
624 // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
625 Clay_CornerRadius cornerRadius;
626 // Controls individual border side widths.
627 Clay_BorderWidth width;
628} Clay_BorderRenderData;
629
630// A struct union containing data specific to this command's .commandType
631typedef union Clay_RenderData {
632 // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE
633 Clay_RectangleRenderData rectangle;
634 // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT
635 Clay_TextRenderData text;
636 // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE
637 Clay_ImageRenderData image;
638 // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM
639 Clay_CustomRenderData custom;
640 // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER
641 Clay_BorderRenderData border;
642 // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START|END
643 Clay_ClipRenderData clip;
644} Clay_RenderData;
645
646// Miscellaneous Structs & Enums ---------------------------------
647
648// Data representing the current internal state of a scrolling element.
649typedef struct Clay_ScrollContainerData {
650 // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.
651 // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.
652 Clay_Vector2 *scrollPosition;
653 // The bounding box of the scroll element.
654 Clay_Dimensions scrollContainerDimensions;
655 // The outer dimensions of the inner scroll container content, including the padding of the parent scroll container.
656 Clay_Dimensions contentDimensions;
657 // The config that was originally passed to the clip element.
658 Clay_ClipElementConfig config;
659 // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.
660 bool found;
661} Clay_ScrollContainerData;
662
663// Bounding box and other data for a specific UI element.
664typedef struct Clay_ElementData {
665 // The rectangle that encloses this UI element, with the position relative to the root of the layout.
666 Clay_BoundingBox boundingBox;
667 // Indicates whether an actual Element matched the provided ID or if the default struct was returned.
668 bool found;
669} Clay_ElementData;
670
671// Used by renderers to determine specific handling for each render command.
672typedef CLAY_PACKED_ENUM {
673 // This command type should be skipped.
674 CLAY_RENDER_COMMAND_TYPE_NONE,
675 // The renderer should draw a solid color rectangle.
676 CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
677 // The renderer should draw a colored border inset into the bounding box.
678 CLAY_RENDER_COMMAND_TYPE_BORDER,
679 // The renderer should draw text.
680 CLAY_RENDER_COMMAND_TYPE_TEXT,
681 // The renderer should draw an image.
682 CLAY_RENDER_COMMAND_TYPE_IMAGE,
683 // The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.
684 CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,
685 // The renderer should finish any previously active clipping, and begin rendering elements in full again.
686 CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,
687 // The renderer should provide a custom implementation for handling this render command based on its .customData
688 CLAY_RENDER_COMMAND_TYPE_CUSTOM,
689} Clay_RenderCommandType;
690
691typedef struct Clay_RenderCommand {
692 // A rectangular box that fully encloses this UI element, with the position relative to the root of the layout.
693 Clay_BoundingBox boundingBox;
694 // A struct union containing data specific to this command's commandType.
695 Clay_RenderData renderData;
696 // A pointer transparently passed through from the original element declaration.
697 void *userData;
698 // The id of this element, transparently passed through from the original element declaration.
699 uint32_t id;
700 // The z order required for drawing this command correctly.
701 // Note: the render command array is already sorted in ascending order, and will produce correct results if drawn in naive order.
702 // This field is intended for use in batching renderers for improved performance.
703 int16_t zIndex;
704 // Specifies how to handle rendering of this command.
705 // CLAY_RENDER_COMMAND_TYPE_RECTANGLE - The renderer should draw a solid color rectangle.
706 // CLAY_RENDER_COMMAND_TYPE_BORDER - The renderer should draw a colored border inset into the bounding box.
707 // CLAY_RENDER_COMMAND_TYPE_TEXT - The renderer should draw text.
708 // CLAY_RENDER_COMMAND_TYPE_IMAGE - The renderer should draw an image.
709 // CLAY_RENDER_COMMAND_TYPE_SCISSOR_START - The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.
710 // CLAY_RENDER_COMMAND_TYPE_SCISSOR_END - The renderer should finish any previously active clipping, and begin rendering elements in full again.
711 // CLAY_RENDER_COMMAND_TYPE_CUSTOM - The renderer should provide a custom implementation for handling this render command based on its .customData
712 Clay_RenderCommandType commandType;
713} Clay_RenderCommand;
714
715// A sized array of render commands.
716typedef struct Clay_RenderCommandArray {
717 // The underlying max capacity of the array, not necessarily all initialized.
718 int32_t capacity;
719 // The number of initialized elements in this array. Used for loops and iteration.
720 int32_t length;
721 // A pointer to the first element in the internal array.
722 Clay_RenderCommand* internalArray;
723} Clay_RenderCommandArray;
724
725// Represents the current state of interaction with clay this frame.
726typedef CLAY_PACKED_ENUM {
727 // A left mouse click, or touch occurred this frame.
728 CLAY_POINTER_DATA_PRESSED_THIS_FRAME,
729 // The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.
730 CLAY_POINTER_DATA_PRESSED,
731 // The left mouse button click or touch was released this frame.
732 CLAY_POINTER_DATA_RELEASED_THIS_FRAME,
733 // The left mouse button click or touch is not currently down / was released at some point in the past.
734 CLAY_POINTER_DATA_RELEASED,
735} Clay_PointerDataInteractionState;
736
737// Information on the current state of pointer interactions this frame.
738typedef struct Clay_PointerData {
739 // The position of the mouse / touch / pointer relative to the root of the layout.
740 Clay_Vector2 position;
741 // Represents the current state of interaction with clay this frame.
742 // CLAY_POINTER_DATA_PRESSED_THIS_FRAME - A left mouse click, or touch occurred this frame.
743 // CLAY_POINTER_DATA_PRESSED - The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.
744 // CLAY_POINTER_DATA_RELEASED_THIS_FRAME - The left mouse button click or touch was released this frame.
745 // CLAY_POINTER_DATA_RELEASED - The left mouse button click or touch is not currently down / was released at some point in the past.
746 Clay_PointerDataInteractionState state;
747} Clay_PointerData;
748
749typedef struct Clay_ElementDeclaration {
750 // Controls various settings that affect the size and position of an element, as well as the sizes and positions of any child elements.
751 Clay_LayoutConfig layout;
752 // Controls the background color of the resulting element.
753 // By convention specified as 0-255, but interpretation is up to the renderer.
754 // If no other config is specified, .backgroundColor will generate a RECTANGLE render command, otherwise it will be passed as a property to IMAGE or CUSTOM render commands.
755 Clay_Color backgroundColor;
756 // Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
757 Clay_CornerRadius cornerRadius;
758 // Controls settings related to aspect ratio scaling.
759 Clay_AspectRatioElementConfig aspectRatio;
760 // Controls settings related to image elements.
761 Clay_ImageElementConfig image;
762 // Controls whether and how an element "floats", which means it layers over the top of other elements in z order, and doesn't affect the position and size of siblings or parent elements.
763 // Note: in order to activate floating, .floating.attachTo must be set to something other than the default value.
764 Clay_FloatingElementConfig floating;
765 // Used to create CUSTOM render commands, usually to render element types not supported by Clay.
766 Clay_CustomElementConfig custom;
767 // Controls whether an element should clip its contents, as well as providing child x,y offset configuration for scrolling.
768 Clay_ClipElementConfig clip;
769 // Controls settings related to element borders, and will generate BORDER render commands.
770 Clay_BorderElementConfig border;
771 // A pointer that will be transparently passed through to resulting render commands.
772 void *userData;
773} Clay_ElementDeclaration;
774
775CLAY__WRAPPER_STRUCT(Clay_ElementDeclaration);
776
777// Represents the type of error clay encountered while computing layout.
778typedef CLAY_PACKED_ENUM {
779 // A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.
780 CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
781 // Clay attempted to allocate its internal data structures but ran out of space.
782 // The arena passed to Clay_Initialize was created with a capacity smaller than that required by Clay_MinMemorySize().
783 CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,
784 // Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxElementCount().
785 CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,
786 // Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxMeasureTextCacheWordCount().
787 CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
788 // Two elements were declared with exactly the same ID within one layout.
789 CLAY_ERROR_TYPE_DUPLICATE_ID,
790 // A floating element was declared using CLAY_ATTACH_TO_ELEMENT_ID and either an invalid .parentId was provided or no element with the provided .parentId was found.
791 CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,
792 // An element was declared that using CLAY_SIZING_PERCENT but the percentage value was over 1. Percentage values are expected to be in the 0-1 range.
793 CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,
794 // Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!
795 CLAY_ERROR_TYPE_INTERNAL_ERROR,
796 // Clay__OpenElement was called more times than Clay__CloseElement, so there were still remaining open elements when the layout ended.
797 CLAY_ERROR_TYPE_UNBALANCED_OPEN_CLOSE,
798} Clay_ErrorType;
799
800// Data to identify the error that clay has encountered.
801typedef struct Clay_ErrorData {
802 // Represents the type of error clay encountered while computing layout.
803 // CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED - A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.
804 // CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED - Clay attempted to allocate its internal data structures but ran out of space. The arena passed to Clay_Initialize was created with a capacity smaller than that required by Clay_MinMemorySize().
805 // CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED - Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxElementCount().
806 // CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED - Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxMeasureTextCacheWordCount().
807 // CLAY_ERROR_TYPE_DUPLICATE_ID - Two elements were declared with exactly the same ID within one layout.
808 // CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND - A floating element was declared using CLAY_ATTACH_TO_ELEMENT_ID and either an invalid .parentId was provided or no element with the provided .parentId was found.
809 // CLAY_ERROR_TYPE_PERCENTAGE_OVER_1 - An element was declared that using CLAY_SIZING_PERCENT but the percentage value was over 1. Percentage values are expected to be in the 0-1 range.
810 // CLAY_ERROR_TYPE_INTERNAL_ERROR - Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!
811 Clay_ErrorType errorType;
812 // A string containing human-readable error text that explains the error in more detail.
813 Clay_String errorText;
814 // A transparent pointer passed through from when the error handler was first provided.
815 void *userData;
816} Clay_ErrorData;
817
818// A wrapper struct around Clay's error handler function.
819typedef struct {
820 // A user provided function to call when Clay encounters an error during layout.
821 void (*errorHandlerFunction)(Clay_ErrorData errorText);
822 // A pointer that will be transparently passed through to the error handler when it is called.
823 void *userData;
824} Clay_ErrorHandler;
825
826// Function Forward Declarations ---------------------------------
827
828// Public API functions ------------------------------------------
829
830// Returns the size, in bytes, of the minimum amount of memory Clay requires to operate at its current settings.
831CLAY_DLL_EXPORT uint32_t Clay_MinMemorySize(void);
832// Creates an arena for clay to use for its internal allocations, given a certain capacity in bytes and a pointer to an allocation of at least that size.
833// Intended to be used with Clay_MinMemorySize in the following way:
834// uint32_t minMemoryRequired = Clay_MinMemorySize();
835// Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(minMemoryRequired, malloc(minMemoryRequired));
836CLAY_DLL_EXPORT Clay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity, void *memory);
837// Sets the state of the "pointer" (i.e. the mouse or touch) in Clay's internal data. Used for detecting and responding to mouse events in the debug view,
838// as well as for Clay_Hovered() and scroll element handling.
839CLAY_DLL_EXPORT void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown);
840// Initialize Clay's internal arena and setup required data before layout can begin. Only needs to be called once.
841// - arena can be created using Clay_CreateArenaWithCapacityAndMemory()
842// - layoutDimensions are the initial bounding dimensions of the layout (i.e. the screen width and height for a full screen layout)
843// - errorHandler is used by Clay to inform you if something has gone wrong in configuration or layout.
844CLAY_DLL_EXPORT Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler);
845// Returns the Context that clay is currently using. Used when using multiple instances of clay simultaneously.
846CLAY_DLL_EXPORT Clay_Context* Clay_GetCurrentContext(void);
847// Sets the context that clay will use to compute the layout.
848// Used to restore a context saved from Clay_GetCurrentContext when using multiple instances of clay simultaneously.
849CLAY_DLL_EXPORT void Clay_SetCurrentContext(Clay_Context* context);
850// Updates the state of Clay's internal scroll data, updating scroll content positions if scrollDelta is non zero, and progressing momentum scrolling.
851// - enableDragScrolling when set to true will enable mobile device like "touch drag" scroll of scroll containers, including momentum scrolling after the touch has ended.
852// - scrollDelta is the amount to scroll this frame on each axis in pixels.
853// - deltaTime is the time in seconds since the last "frame" (scroll update)
854CLAY_DLL_EXPORT void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime);
855// Returns the internally stored scroll offset for the currently open element.
856// Generally intended for use with clip elements to create scrolling containers.
857CLAY_DLL_EXPORT Clay_Vector2 Clay_GetScrollOffset(void);
858// Updates the layout dimensions in response to the window or outer container being resized.
859CLAY_DLL_EXPORT void Clay_SetLayoutDimensions(Clay_Dimensions dimensions);
860// Called before starting any layout declarations.
861CLAY_DLL_EXPORT void Clay_BeginLayout(void);
862// Called when all layout declarations are finished.
863// Computes the layout and generates and returns the array of render commands to draw.
864CLAY_DLL_EXPORT Clay_RenderCommandArray Clay_EndLayout(void);
865// Calculates a hash ID from the given idString.
866// Generally only used for dynamic strings when CLAY_ID("stringLiteral") can't be used.
867CLAY_DLL_EXPORT Clay_ElementId Clay_GetElementId(Clay_String idString);
868// Calculates a hash ID from the given idString and index.
869// - index is used to avoid constructing dynamic ID strings in loops.
870// Generally only used for dynamic strings when CLAY_IDI("stringLiteral", index) can't be used.
871CLAY_DLL_EXPORT Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index);
872// Returns layout data such as the final calculated bounding box for an element with a given ID.
873// The returned Clay_ElementData contains a `found` bool that will be true if an element with the provided ID was found.
874// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
875CLAY_DLL_EXPORT Clay_ElementData Clay_GetElementData(Clay_ElementId id);
876// Returns true if the pointer position provided by Clay_SetPointerState is within the current element's bounding box.
877// Works during element declaration, e.g. CLAY({ .backgroundColor = Clay_Hovered() ? BLUE : RED });
878CLAY_DLL_EXPORT bool Clay_Hovered(void);
879// Bind a callback that will be called when the pointer position provided by Clay_SetPointerState is within the current element's bounding box.
880// - onHoverFunction is a function pointer to a user defined function.
881// - userData is a pointer that will be transparently passed through when the onHoverFunction is called.
882CLAY_DLL_EXPORT void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData), void *userData);
883// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.
884// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
885CLAY_DLL_EXPORT bool Clay_PointerOver(Clay_ElementId elementId);
886// Returns the array of element IDs that the pointer is currently over.
887CLAY_DLL_EXPORT Clay_ElementIdArray Clay_GetPointerOverIds(void);
888// Returns data representing the state of the scrolling element with the provided ID.
889// The returned Clay_ScrollContainerData contains a `found` bool that will be true if a scroll element was found with the provided ID.
890// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.
891// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
892CLAY_DLL_EXPORT Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id);
893// Binds a callback function that Clay will call to determine the dimensions of a given string slice.
894// - measureTextFunction is a user provided function that adheres to the interface Clay_Dimensions (Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
895// - userData is a pointer that will be transparently passed through when the measureTextFunction is called.
896CLAY_DLL_EXPORT void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData);
897// Experimental - Used in cases where Clay needs to integrate with a system that manages its own scrolling containers externally.
898// Please reach out if you plan to use this function, as it may be subject to change.
899CLAY_DLL_EXPORT void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData);
900// A bounds-checked "get" function for the Clay_RenderCommandArray returned from Clay_EndLayout().
901CLAY_DLL_EXPORT Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index);
902// Enables and disables Clay's internal debug tools.
903// This state is retained and does not need to be set each frame.
904CLAY_DLL_EXPORT void Clay_SetDebugModeEnabled(bool enabled);
905// Returns true if Clay's internal debug tools are currently enabled.
906CLAY_DLL_EXPORT bool Clay_IsDebugModeEnabled(void);
907// Enables and disables visibility culling. By default, Clay will not generate render commands for elements whose bounding box is entirely outside the screen.
908CLAY_DLL_EXPORT void Clay_SetCullingEnabled(bool enabled);
909// Returns the maximum number of UI elements supported by Clay's current configuration.
910CLAY_DLL_EXPORT int32_t Clay_GetMaxElementCount(void);
911// Modifies the maximum number of UI elements supported by Clay's current configuration.
912// This may require reallocating additional memory, and re-calling Clay_Initialize();
913CLAY_DLL_EXPORT void Clay_SetMaxElementCount(int32_t maxElementCount);
914// Returns the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
915CLAY_DLL_EXPORT int32_t Clay_GetMaxMeasureTextCacheWordCount(void);
916// Modifies the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
917// This may require reallocating additional memory, and re-calling Clay_Initialize();
918CLAY_DLL_EXPORT void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
919// Resets Clay's internal text measurement cache. Useful if font mappings have changed or fonts have been reloaded.
920CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
921
922// Internal API functions required by macros ----------------------
923
924CLAY_DLL_EXPORT void Clay__OpenElement(void);
925CLAY_DLL_EXPORT void Clay__OpenElementWithId(Clay_ElementId elementId);
926CLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
927CLAY_DLL_EXPORT void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *config);
928CLAY_DLL_EXPORT void Clay__CloseElement(void);
929CLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t seed);
930CLAY_DLL_EXPORT Clay_ElementId Clay__HashStringWithOffset(Clay_String key, uint32_t offset, uint32_t seed);
931CLAY_DLL_EXPORT void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
932CLAY_DLL_EXPORT Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);
933CLAY_DLL_EXPORT uint32_t Clay__GetParentElementId(void);
934
935extern Clay_Color Clay__debugViewHighlightColor;
936extern uint32_t Clay__debugViewWidth;
937
938#ifdef __cplusplus
939}
940#endif
941
942#endif // CLAY_HEADER
943
944// -----------------------------------------
945// IMPLEMENTATION --------------------------
946// -----------------------------------------
947#ifdef CLAY_IMPLEMENTATION
948#undef CLAY_IMPLEMENTATION
949
950#ifndef CLAY__NULL
951#define CLAY__NULL 0
952#endif
953
954#ifndef CLAY__MAXFLOAT
955#define CLAY__MAXFLOAT 3.40282346638528859812e+38F
956#endif
957
958Clay_LayoutConfig CLAY_LAYOUT_DEFAULT = CLAY__DEFAULT_STRUCT;
959
960Clay_Color Clay__Color_DEFAULT = CLAY__DEFAULT_STRUCT;
961Clay_CornerRadius Clay__CornerRadius_DEFAULT = CLAY__DEFAULT_STRUCT;
962Clay_BorderWidth Clay__BorderWidth_DEFAULT = CLAY__DEFAULT_STRUCT;
963
964// The below functions define array bounds checking and convenience functions for a provided type.
965#define CLAY__ARRAY_DEFINE_FUNCTIONS(typeName, arrayName) \
966 \
967typedef struct \
968{ \
969 int32_t length; \
970 typeName *internalArray; \
971} arrayName##Slice; \
972 \
973typeName typeName##_DEFAULT = CLAY__DEFAULT_STRUCT; \
974 \
975arrayName arrayName##_Allocate_Arena(int32_t capacity, Clay_Arena *arena) { \
976 return CLAY__INIT(arrayName){.capacity = capacity, .length = 0, \
977 .internalArray = (typeName *)Clay__Array_Allocate_Arena(capacity, sizeof(typeName), arena)}; \
978} \
979 \
980typeName *arrayName##_Get(arrayName *array, int32_t index) { \
981 return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &typeName##_DEFAULT; \
982} \
983 \
984typeName arrayName##_GetValue(arrayName *array, int32_t index) { \
985 return Clay__Array_RangeCheck(index, array->length) ? array->internalArray[index] : typeName##_DEFAULT; \
986} \
987 \
988typeName *arrayName##_Add(arrayName *array, typeName item) { \
989 if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) { \
990 array->internalArray[array->length++] = item; \
991 return &array->internalArray[array->length - 1]; \
992 } \
993 return &typeName##_DEFAULT; \
994} \
995 \
996typeName *arrayName##Slice_Get(arrayName##Slice *slice, int32_t index) { \
997 return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : &typeName##_DEFAULT; \
998} \
999 \
1000typeName arrayName##_RemoveSwapback(arrayName *array, int32_t index) { \
1001 if (Clay__Array_RangeCheck(index, array->length)) { \
1002 array->length--; \
1003 typeName removed = array->internalArray[index]; \
1004 array->internalArray[index] = array->internalArray[array->length]; \
1005 return removed; \
1006 } \
1007 return typeName##_DEFAULT; \
1008} \
1009 \
1010void arrayName##_Set(arrayName *array, int32_t index, typeName value) { \
1011 if (Clay__Array_RangeCheck(index, array->capacity)) { \
1012 array->internalArray[index] = value; \
1013 array->length = index < array->length ? array->length : index + 1; \
1014 } \
1015} \
1016
1017#define CLAY__ARRAY_DEFINE(typeName, arrayName) \
1018typedef struct \
1019{ \
1020 int32_t capacity; \
1021 int32_t length; \
1022 typeName *internalArray; \
1023} arrayName; \
1024 \
1025CLAY__ARRAY_DEFINE_FUNCTIONS(typeName, arrayName) \
1026
1027Clay_Context *Clay__currentContext;
1028int32_t Clay__defaultMaxElementCount = 8192;
1029int32_t Clay__defaultMaxMeasureTextWordCacheCount = 16384;
1030
1031void Clay__ErrorHandlerFunctionDefault(Clay_ErrorData errorText) {
1032 (void) errorText;
1033}
1034
1035Clay_String CLAY__SPACECHAR = { .length = 1, .chars = " " };
1036Clay_String CLAY__STRING_DEFAULT = { .length = 0, .chars = NULL };
1037
1038typedef struct {
1039 bool maxElementsExceeded;
1040 bool maxRenderCommandsExceeded;
1041 bool maxTextMeasureCacheExceeded;
1042 bool textMeasurementFunctionNotSet;
1043} Clay_BooleanWarnings;
1044
1045typedef struct {
1046 Clay_String baseMessage;
1047 Clay_String dynamicMessage;
1048} Clay__Warning;
1049
1050Clay__Warning CLAY__WARNING_DEFAULT = CLAY__DEFAULT_STRUCT;
1051
1052typedef struct {
1053 int32_t capacity;
1054 int32_t length;
1055 Clay__Warning *internalArray;
1056} Clay__WarningArray;
1057
1058typedef struct {
1059 Clay_Color backgroundColor;
1060 Clay_CornerRadius cornerRadius;
1061 void* userData;
1062} Clay_SharedElementConfig;
1063
1064CLAY__WRAPPER_STRUCT(Clay_SharedElementConfig);
1065
1066Clay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena);
1067Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item);
1068void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena);
1069bool Clay__Array_RangeCheck(int32_t index, int32_t length);
1070bool Clay__Array_AddCapacityCheck(int32_t length, int32_t capacity);
1071
1072CLAY__ARRAY_DEFINE(bool, Clay__boolArray)
1073CLAY__ARRAY_DEFINE(int32_t, Clay__int32_tArray)
1074CLAY__ARRAY_DEFINE(char, Clay__charArray)
1075CLAY__ARRAY_DEFINE_FUNCTIONS(Clay_ElementId, Clay_ElementIdArray)
1076CLAY__ARRAY_DEFINE(Clay_LayoutConfig, Clay__LayoutConfigArray)
1077CLAY__ARRAY_DEFINE(Clay_TextElementConfig, Clay__TextElementConfigArray)
1078CLAY__ARRAY_DEFINE(Clay_AspectRatioElementConfig, Clay__AspectRatioElementConfigArray)
1079CLAY__ARRAY_DEFINE(Clay_ImageElementConfig, Clay__ImageElementConfigArray)
1080CLAY__ARRAY_DEFINE(Clay_FloatingElementConfig, Clay__FloatingElementConfigArray)
1081CLAY__ARRAY_DEFINE(Clay_CustomElementConfig, Clay__CustomElementConfigArray)
1082CLAY__ARRAY_DEFINE(Clay_ClipElementConfig, Clay__ClipElementConfigArray)
1083CLAY__ARRAY_DEFINE(Clay_BorderElementConfig, Clay__BorderElementConfigArray)
1084CLAY__ARRAY_DEFINE(Clay_String, Clay__StringArray)
1085CLAY__ARRAY_DEFINE(Clay_SharedElementConfig, Clay__SharedElementConfigArray)
1086CLAY__ARRAY_DEFINE_FUNCTIONS(Clay_RenderCommand, Clay_RenderCommandArray)
1087
1088typedef CLAY_PACKED_ENUM {
1089 CLAY__ELEMENT_CONFIG_TYPE_NONE,
1090 CLAY__ELEMENT_CONFIG_TYPE_BORDER,
1091 CLAY__ELEMENT_CONFIG_TYPE_FLOATING,
1092 CLAY__ELEMENT_CONFIG_TYPE_CLIP,
1093 CLAY__ELEMENT_CONFIG_TYPE_ASPECT,
1094 CLAY__ELEMENT_CONFIG_TYPE_IMAGE,
1095 CLAY__ELEMENT_CONFIG_TYPE_TEXT,
1096 CLAY__ELEMENT_CONFIG_TYPE_CUSTOM,
1097 CLAY__ELEMENT_CONFIG_TYPE_SHARED,
1098} Clay__ElementConfigType;
1099
1100typedef union {
1101 Clay_TextElementConfig *textElementConfig;
1102 Clay_AspectRatioElementConfig *aspectRatioElementConfig;
1103 Clay_ImageElementConfig *imageElementConfig;
1104 Clay_FloatingElementConfig *floatingElementConfig;
1105 Clay_CustomElementConfig *customElementConfig;
1106 Clay_ClipElementConfig *clipElementConfig;
1107 Clay_BorderElementConfig *borderElementConfig;
1108 Clay_SharedElementConfig *sharedElementConfig;
1109} Clay_ElementConfigUnion;
1110
1111typedef struct {
1112 Clay__ElementConfigType type;
1113 Clay_ElementConfigUnion config;
1114} Clay_ElementConfig;
1115
1116CLAY__ARRAY_DEFINE(Clay_ElementConfig, Clay__ElementConfigArray)
1117
1118typedef struct {
1119 Clay_Dimensions dimensions;
1120 Clay_String line;
1121} Clay__WrappedTextLine;
1122
1123CLAY__ARRAY_DEFINE(Clay__WrappedTextLine, Clay__WrappedTextLineArray)
1124
1125typedef struct {
1126 Clay_String text;
1127 Clay_Dimensions preferredDimensions;
1128 int32_t elementIndex;
1129 Clay__WrappedTextLineArraySlice wrappedLines;
1130} Clay__TextElementData;
1131
1132CLAY__ARRAY_DEFINE(Clay__TextElementData, Clay__TextElementDataArray)
1133
1134typedef struct {
1135 int32_t *elements;
1136 uint16_t length;
1137} Clay__LayoutElementChildren;
1138
1139typedef struct {
1140 union {
1141 Clay__LayoutElementChildren children;
1142 Clay__TextElementData *textElementData;
1143 } childrenOrTextContent;
1144 Clay_Dimensions dimensions;
1145 Clay_Dimensions minDimensions;
1146 Clay_LayoutConfig *layoutConfig;
1147 Clay__ElementConfigArraySlice elementConfigs;
1148 uint32_t id;
1149 uint16_t floatingChildrenCount;
1150} Clay_LayoutElement;
1151
1152CLAY__ARRAY_DEFINE(Clay_LayoutElement, Clay_LayoutElementArray)
1153
1154typedef struct {
1155 Clay_LayoutElement *layoutElement;
1156 Clay_BoundingBox boundingBox;
1157 Clay_Dimensions contentSize;
1158 Clay_Vector2 scrollOrigin;
1159 Clay_Vector2 pointerOrigin;
1160 Clay_Vector2 scrollMomentum;
1161 Clay_Vector2 scrollPosition;
1162 Clay_Vector2 previousDelta;
1163 float momentumTime;
1164 uint32_t elementId;
1165 bool openThisFrame;
1166 bool pointerScrollActive;
1167} Clay__ScrollContainerDataInternal;
1168
1169CLAY__ARRAY_DEFINE(Clay__ScrollContainerDataInternal, Clay__ScrollContainerDataInternalArray)
1170
1171typedef struct {
1172 bool collision;
1173 bool collapsed;
1174} Clay__DebugElementData;
1175
1176CLAY__ARRAY_DEFINE(Clay__DebugElementData, Clay__DebugElementDataArray)
1177
1178typedef struct { // todo get this struct into a single cache line
1179 Clay_BoundingBox boundingBox;
1180 Clay_ElementId elementId;
1181 Clay_LayoutElement* layoutElement;
1182 void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData);
1183 void *hoverFunctionUserData;
1184 int32_t nextIndex;
1185 uint32_t generation;
1186 Clay__DebugElementData *debugData;
1187} Clay_LayoutElementHashMapItem;
1188
1189CLAY__ARRAY_DEFINE(Clay_LayoutElementHashMapItem, Clay__LayoutElementHashMapItemArray)
1190
1191typedef struct {
1192 int32_t startOffset;
1193 int32_t length;
1194 float width;
1195 int32_t next;
1196} Clay__MeasuredWord;
1197
1198CLAY__ARRAY_DEFINE(Clay__MeasuredWord, Clay__MeasuredWordArray)
1199
1200typedef struct {
1201 Clay_Dimensions unwrappedDimensions;
1202 int32_t measuredWordsStartIndex;
1203 float minWidth;
1204 bool containsNewlines;
1205 // Hash map data
1206 uint32_t id;
1207 int32_t nextIndex;
1208 uint32_t generation;
1209} Clay__MeasureTextCacheItem;
1210
1211CLAY__ARRAY_DEFINE(Clay__MeasureTextCacheItem, Clay__MeasureTextCacheItemArray)
1212
1213typedef struct {
1214 Clay_LayoutElement *layoutElement;
1215 Clay_Vector2 position;
1216 Clay_Vector2 nextChildOffset;
1217} Clay__LayoutElementTreeNode;
1218
1219CLAY__ARRAY_DEFINE(Clay__LayoutElementTreeNode, Clay__LayoutElementTreeNodeArray)
1220
1221typedef struct {
1222 int32_t layoutElementIndex;
1223 uint32_t parentId; // This can be zero in the case of the root layout tree
1224 uint32_t clipElementId; // This can be zero if there is no clip element
1225 int16_t zIndex;
1226 Clay_Vector2 pointerOffset; // Only used when scroll containers are managed externally
1227} Clay__LayoutElementTreeRoot;
1228
1229CLAY__ARRAY_DEFINE(Clay__LayoutElementTreeRoot, Clay__LayoutElementTreeRootArray)
1230
1231struct Clay_Context {
1232 int32_t maxElementCount;
1233 int32_t maxMeasureTextCacheWordCount;
1234 bool warningsEnabled;
1235 Clay_ErrorHandler errorHandler;
1236 Clay_BooleanWarnings booleanWarnings;
1237 Clay__WarningArray warnings;
1238
1239 Clay_PointerData pointerInfo;
1240 Clay_Dimensions layoutDimensions;
1241 Clay_ElementId dynamicElementIndexBaseHash;
1242 uint32_t dynamicElementIndex;
1243 bool debugModeEnabled;
1244 bool disableCulling;
1245 bool externalScrollHandlingEnabled;
1246 uint32_t debugSelectedElementId;
1247 uint32_t generation;
1248 uintptr_t arenaResetOffset;
1249 void *measureTextUserData;
1250 void *queryScrollOffsetUserData;
1251 Clay_Arena internalArena;
1252 // Layout Elements / Render Commands
1253 Clay_LayoutElementArray layoutElements;
1254 Clay_RenderCommandArray renderCommands;
1255 Clay__int32_tArray openLayoutElementStack;
1256 Clay__int32_tArray layoutElementChildren;
1257 Clay__int32_tArray layoutElementChildrenBuffer;
1258 Clay__TextElementDataArray textElementData;
1259 Clay__int32_tArray aspectRatioElementIndexes;
1260 Clay__int32_tArray reusableElementIndexBuffer;
1261 Clay__int32_tArray layoutElementClipElementIds;
1262 // Configs
1263 Clay__LayoutConfigArray layoutConfigs;
1264 Clay__ElementConfigArray elementConfigs;
1265 Clay__TextElementConfigArray textElementConfigs;
1266 Clay__AspectRatioElementConfigArray aspectRatioElementConfigs;
1267 Clay__ImageElementConfigArray imageElementConfigs;
1268 Clay__FloatingElementConfigArray floatingElementConfigs;
1269 Clay__ClipElementConfigArray clipElementConfigs;
1270 Clay__CustomElementConfigArray customElementConfigs;
1271 Clay__BorderElementConfigArray borderElementConfigs;
1272 Clay__SharedElementConfigArray sharedElementConfigs;
1273 // Misc Data Structures
1274 Clay__StringArray layoutElementIdStrings;
1275 Clay__WrappedTextLineArray wrappedTextLines;
1276 Clay__LayoutElementTreeNodeArray layoutElementTreeNodeArray1;
1277 Clay__LayoutElementTreeRootArray layoutElementTreeRoots;
1278 Clay__LayoutElementHashMapItemArray layoutElementsHashMapInternal;
1279 Clay__int32_tArray layoutElementsHashMap;
1280 Clay__MeasureTextCacheItemArray measureTextHashMapInternal;
1281 Clay__int32_tArray measureTextHashMapInternalFreeList;
1282 Clay__int32_tArray measureTextHashMap;
1283 Clay__MeasuredWordArray measuredWords;
1284 Clay__int32_tArray measuredWordsFreeList;
1285 Clay__int32_tArray openClipElementStack;
1286 Clay_ElementIdArray pointerOverIds;
1287 Clay__ScrollContainerDataInternalArray scrollContainerDatas;
1288 Clay__boolArray treeNodeVisited;
1289 Clay__charArray dynamicStringData;
1290 Clay__DebugElementDataArray debugElementData;
1291};
1292
1293Clay_Context* Clay__Context_Allocate_Arena(Clay_Arena *arena) {
1294 size_t totalSizeBytes = sizeof(Clay_Context);
1295 if (totalSizeBytes > arena->capacity)
1296 {
1297 return NULL;
1298 }
1299 arena->nextAllocation += totalSizeBytes;
1300 return (Clay_Context*)(arena->memory);
1301}
1302
1303Clay_String Clay__WriteStringToCharBuffer(Clay__charArray *buffer, Clay_String string) {
1304 for (int32_t i = 0; i < string.length; i++) {
1305 buffer->internalArray[buffer->length + i] = string.chars[i];
1306 }
1307 buffer->length += string.length;
1308 return CLAY__INIT(Clay_String) { .length = string.length, .chars = (const char *)(buffer->internalArray + buffer->length - string.length) };
1309}
1310
1311#ifdef CLAY_WASM
1312 __attribute__((import_module("clay"), import_name("measureTextFunction"))) Clay_Dimensions Clay__MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
1313 __attribute__((import_module("clay"), import_name("queryScrollOffsetFunction"))) Clay_Vector2 Clay__QueryScrollOffset(uint32_t elementId, void *userData);
1314#else
1315 Clay_Dimensions (*Clay__MeasureText)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
1316 Clay_Vector2 (*Clay__QueryScrollOffset)(uint32_t elementId, void *userData);
1317#endif
1318
1319Clay_LayoutElement* Clay__GetOpenLayoutElement(void) {
1320 Clay_Context* context = Clay_GetCurrentContext();
1321 return Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1));
1322}
1323
1324uint32_t Clay__GetParentElementId(void) {
1325 Clay_Context* context = Clay_GetCurrentContext();
1326 return Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2))->id;
1327}
1328
1329Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay_GetCurrentContext()->layoutConfigs, config); }
1330Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_TextElementConfig_DEFAULT : Clay__TextElementConfigArray_Add(&Clay_GetCurrentContext()->textElementConfigs, config); }
1331Clay_AspectRatioElementConfig * Clay__StoreAspectRatioElementConfig(Clay_AspectRatioElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_AspectRatioElementConfig_DEFAULT : Clay__AspectRatioElementConfigArray_Add(&Clay_GetCurrentContext()->aspectRatioElementConfigs, config); }
1332Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_ImageElementConfig_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay_GetCurrentContext()->imageElementConfigs, config); }
1333Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_FloatingElementConfig_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay_GetCurrentContext()->floatingElementConfigs, config); }
1334Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_CustomElementConfig_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay_GetCurrentContext()->customElementConfigs, config); }
1335Clay_ClipElementConfig * Clay__StoreClipElementConfig(Clay_ClipElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_ClipElementConfig_DEFAULT : Clay__ClipElementConfigArray_Add(&Clay_GetCurrentContext()->clipElementConfigs, config); }
1336Clay_BorderElementConfig * Clay__StoreBorderElementConfig(Clay_BorderElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_BorderElementConfig_DEFAULT : Clay__BorderElementConfigArray_Add(&Clay_GetCurrentContext()->borderElementConfigs, config); }
1337Clay_SharedElementConfig * Clay__StoreSharedElementConfig(Clay_SharedElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_SharedElementConfig_DEFAULT : Clay__SharedElementConfigArray_Add(&Clay_GetCurrentContext()->sharedElementConfigs, config); }
1338
1339Clay_ElementConfig Clay__AttachElementConfig(Clay_ElementConfigUnion config, Clay__ElementConfigType type) {
1340 Clay_Context* context = Clay_GetCurrentContext();
1341 if (context->booleanWarnings.maxElementsExceeded) {
1342 return CLAY__INIT(Clay_ElementConfig) CLAY__DEFAULT_STRUCT;
1343 }
1344 Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
1345 openLayoutElement->elementConfigs.length++;
1346 return *Clay__ElementConfigArray_Add(&context->elementConfigs, CLAY__INIT(Clay_ElementConfig) { .type = type, .config = config });
1347}
1348
1349Clay_ElementConfigUnion Clay__FindElementConfigWithType(Clay_LayoutElement *element, Clay__ElementConfigType type) {
1350 for (int32_t i = 0; i < element->elementConfigs.length; i++) {
1351 Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&element->elementConfigs, i);
1352 if (config->type == type) {
1353 return config->config;
1354 }
1355 }
1356 return CLAY__INIT(Clay_ElementConfigUnion) { NULL };
1357}
1358
1359Clay_ElementId Clay__HashNumber(const uint32_t offset, const uint32_t seed) {
1360 uint32_t hash = seed;
1361 hash += (offset + 48);
1362 hash += (hash << 10);
1363 hash ^= (hash >> 6);
1364
1365 hash += (hash << 3);
1366 hash ^= (hash >> 11);
1367 hash += (hash << 15);
1368 return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = seed, .stringId = CLAY__STRING_DEFAULT }; // Reserve the hash result of zero as "null id"
1369}
1370
1371Clay_ElementId Clay__HashString(Clay_String key, const uint32_t seed) {
1372 uint32_t hash = seed;
1373
1374 for (int32_t i = 0; i < key.length; i++) {
1375 hash += key.chars[i];
1376 hash += (hash << 10);
1377 hash ^= (hash >> 6);
1378 }
1379
1380 hash += (hash << 3);
1381 hash ^= (hash >> 11);
1382 hash += (hash << 15);
1383 return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = 0, .baseId = hash + 1, .stringId = key }; // Reserve the hash result of zero as "null id"
1384}
1385
1386Clay_ElementId Clay__HashStringWithOffset(Clay_String key, const uint32_t offset, const uint32_t seed) {
1387 uint32_t hash = 0;
1388 uint32_t base = seed;
1389
1390 for (int32_t i = 0; i < key.length; i++) {
1391 base += key.chars[i];
1392 base += (base << 10);
1393 base ^= (base >> 6);
1394 }
1395 hash = base;
1396 hash += offset;
1397 hash += (hash << 10);
1398 hash ^= (hash >> 6);
1399
1400 hash += (hash << 3);
1401 base += (base << 3);
1402 hash ^= (hash >> 11);
1403 base ^= (base >> 11);
1404 hash += (hash << 15);
1405 base += (base << 15);
1406 return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as "null id"
1407}
1408
1409#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
1410static inline __m128i Clay__SIMDRotateLeft(__m128i x, int r) {
1411 return _mm_or_si128(_mm_slli_epi64(x, r), _mm_srli_epi64(x, 64 - r));
1412}
1413
1414static inline void Clay__SIMDARXMix(__m128i* a, __m128i* b) {
1415 *a = _mm_add_epi64(*a, *b);
1416 *b = _mm_xor_si128(Clay__SIMDRotateLeft(*b, 17), *a);
1417}
1418
1419uint64_t Clay__HashData(const uint8_t* data, size_t length) {
1420 // Pinched these constants from the BLAKE implementation
1421 __m128i v0 = _mm_set1_epi64x(0x6a09e667f3bcc908ULL);
1422 __m128i v1 = _mm_set1_epi64x(0xbb67ae8584caa73bULL);
1423 __m128i v2 = _mm_set1_epi64x(0x3c6ef372fe94f82bULL);
1424 __m128i v3 = _mm_set1_epi64x(0xa54ff53a5f1d36f1ULL);
1425
1426 uint8_t overflowBuffer[16] = { 0 }; // Temporary buffer for small inputs
1427
1428 while (length > 0) {
1429 __m128i msg;
1430 if (length >= 16) {
1431 msg = _mm_loadu_si128((const __m128i*)data);
1432 data += 16;
1433 length -= 16;
1434 }
1435 else {
1436 for (size_t i = 0; i < length; i++) {
1437 overflowBuffer[i] = data[i];
1438 }
1439 msg = _mm_loadu_si128((const __m128i*)overflowBuffer);
1440 length = 0;
1441 }
1442
1443 v0 = _mm_xor_si128(v0, msg);
1444 Clay__SIMDARXMix(&v0, &v1);
1445 Clay__SIMDARXMix(&v2, &v3);
1446
1447 v0 = _mm_add_epi64(v0, v2);
1448 v1 = _mm_add_epi64(v1, v3);
1449 }
1450
1451 Clay__SIMDARXMix(&v0, &v1);
1452 Clay__SIMDARXMix(&v2, &v3);
1453 v0 = _mm_add_epi64(v0, v2);
1454 v1 = _mm_add_epi64(v1, v3);
1455 v0 = _mm_add_epi64(v0, v1);
1456
1457 uint64_t result[2];
1458 _mm_storeu_si128((__m128i*)result, v0);
1459
1460 return result[0] ^ result[1];
1461}
1462#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)
1463static inline void Clay__SIMDARXMix(uint64x2_t* a, uint64x2_t* b) {
1464 *a = vaddq_u64(*a, *b);
1465 *b = veorq_u64(vorrq_u64(vshlq_n_u64(*b, 17), vshrq_n_u64(*b, 64 - 17)), *a);
1466}
1467
1468uint64_t Clay__HashData(const uint8_t* data, size_t length) {
1469 // Pinched these constants from the BLAKE implementation
1470 uint64x2_t v0 = vdupq_n_u64(0x6a09e667f3bcc908ULL);
1471 uint64x2_t v1 = vdupq_n_u64(0xbb67ae8584caa73bULL);
1472 uint64x2_t v2 = vdupq_n_u64(0x3c6ef372fe94f82bULL);
1473 uint64x2_t v3 = vdupq_n_u64(0xa54ff53a5f1d36f1ULL);
1474
1475 uint8_t overflowBuffer[8] = { 0 };
1476
1477 while (length > 0) {
1478 uint64x2_t msg;
1479 if (length > 16) {
1480 msg = vld1q_u64((const uint64_t*)data);
1481 data += 16;
1482 length -= 16;
1483 }
1484 else if (length > 8) {
1485 msg = vcombine_u64(vld1_u64((const uint64_t*)data), vdup_n_u64(0));
1486 data += 8;
1487 length -= 8;
1488 }
1489 else {
1490 for (size_t i = 0; i < length; i++) {
1491 overflowBuffer[i] = data[i];
1492 }
1493 uint8x8_t lower = vld1_u8(overflowBuffer);
1494 msg = vreinterpretq_u64_u8(vcombine_u8(lower, vdup_n_u8(0)));
1495 length = 0;
1496 }
1497 v0 = veorq_u64(v0, msg);
1498 Clay__SIMDARXMix(&v0, &v1);
1499 Clay__SIMDARXMix(&v2, &v3);
1500
1501 v0 = vaddq_u64(v0, v2);
1502 v1 = vaddq_u64(v1, v3);
1503 }
1504
1505 Clay__SIMDARXMix(&v0, &v1);
1506 Clay__SIMDARXMix(&v2, &v3);
1507 v0 = vaddq_u64(v0, v2);
1508 v1 = vaddq_u64(v1, v3);
1509 v0 = vaddq_u64(v0, v1);
1510
1511 uint64_t result[2];
1512 vst1q_u64(result, v0);
1513
1514 return result[0] ^ result[1];
1515}
1516#else
1517uint64_t Clay__HashData(const uint8_t* data, size_t length) {
1518 uint64_t hash = 0;
1519
1520 for (size_t i = 0; i < length; i++) {
1521 hash += data[i];
1522 hash += (hash << 10);
1523 hash ^= (hash >> 6);
1524 }
1525 return hash;
1526}
1527#endif
1528
1529uint32_t Clay__HashStringContentsWithConfig(Clay_String *text, Clay_TextElementConfig *config) {
1530 uint32_t hash = 0;
1531 if (text->isStaticallyAllocated) {
1532 hash += (uintptr_t)text->chars;
1533 hash += (hash << 10);
1534 hash ^= (hash >> 6);
1535 hash += text->length;
1536 hash += (hash << 10);
1537 hash ^= (hash >> 6);
1538 } else {
1539 hash = Clay__HashData((const uint8_t *)text->chars, text->length) % UINT32_MAX;
1540 }
1541
1542 hash += config->fontId;
1543 hash += (hash << 10);
1544 hash ^= (hash >> 6);
1545
1546 hash += config->fontSize;
1547 hash += (hash << 10);
1548 hash ^= (hash >> 6);
1549
1550 hash += config->letterSpacing;
1551 hash += (hash << 10);
1552 hash ^= (hash >> 6);
1553
1554 hash += (hash << 3);
1555 hash ^= (hash >> 11);
1556 hash += (hash << 15);
1557 return hash + 1; // Reserve the hash result of zero as "null id"
1558}
1559
1560Clay__MeasuredWord *Clay__AddMeasuredWord(Clay__MeasuredWord word, Clay__MeasuredWord *previousWord) {
1561 Clay_Context* context = Clay_GetCurrentContext();
1562 if (context->measuredWordsFreeList.length > 0) {
1563 uint32_t newItemIndex = Clay__int32_tArray_GetValue(&context->measuredWordsFreeList, (int)context->measuredWordsFreeList.length - 1);
1564 context->measuredWordsFreeList.length--;
1565 Clay__MeasuredWordArray_Set(&context->measuredWords, (int)newItemIndex, word);
1566 previousWord->next = (int32_t)newItemIndex;
1567 return Clay__MeasuredWordArray_Get(&context->measuredWords, (int)newItemIndex);
1568 } else {
1569 previousWord->next = (int32_t)context->measuredWords.length;
1570 return Clay__MeasuredWordArray_Add(&context->measuredWords, word);
1571 }
1572}
1573
1574Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_TextElementConfig *config) {
1575 Clay_Context* context = Clay_GetCurrentContext();
1576 #ifndef CLAY_WASM
1577 if (!Clay__MeasureText) {
1578 if (!context->booleanWarnings.textMeasurementFunctionNotSet) {
1579 context->booleanWarnings.textMeasurementFunctionNotSet = true;
1580 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
1581 .errorType = CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
1582 .errorText = CLAY_STRING("Clay's internal MeasureText function is null. You may have forgotten to call Clay_SetMeasureTextFunction(), or passed a NULL function pointer by mistake."),
1583 .userData = context->errorHandler.userData });
1584 }
1585 return &Clay__MeasureTextCacheItem_DEFAULT;
1586 }
1587 #endif
1588 uint32_t id = Clay__HashStringContentsWithConfig(text, config);
1589 uint32_t hashBucket = id % (context->maxMeasureTextCacheWordCount / 32);
1590 int32_t elementIndexPrevious = 0;
1591 int32_t elementIndex = context->measureTextHashMap.internalArray[hashBucket];
1592 while (elementIndex != 0) {
1593 Clay__MeasureTextCacheItem *hashEntry = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndex);
1594 if (hashEntry->id == id) {
1595 hashEntry->generation = context->generation;
1596 return hashEntry;
1597 }
1598 // This element hasn't been seen in a few frames, delete the hash map item
1599 if (context->generation - hashEntry->generation > 2) {
1600 // Add all the measured words that were included in this measurement to the freelist
1601 int32_t nextWordIndex = hashEntry->measuredWordsStartIndex;
1602 while (nextWordIndex != -1) {
1603 Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&context->measuredWords, nextWordIndex);
1604 Clay__int32_tArray_Add(&context->measuredWordsFreeList, nextWordIndex);
1605 nextWordIndex = measuredWord->next;
1606 }
1607
1608 int32_t nextIndex = hashEntry->nextIndex;
1609 Clay__MeasureTextCacheItemArray_Set(&context->measureTextHashMapInternal, elementIndex, CLAY__INIT(Clay__MeasureTextCacheItem) { .measuredWordsStartIndex = -1 });
1610 Clay__int32_tArray_Add(&context->measureTextHashMapInternalFreeList, elementIndex);
1611 if (elementIndexPrevious == 0) {
1612 context->measureTextHashMap.internalArray[hashBucket] = nextIndex;
1613 } else {
1614 Clay__MeasureTextCacheItem *previousHashEntry = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndexPrevious);
1615 previousHashEntry->nextIndex = nextIndex;
1616 }
1617 elementIndex = nextIndex;
1618 } else {
1619 elementIndexPrevious = elementIndex;
1620 elementIndex = hashEntry->nextIndex;
1621 }
1622 }
1623
1624 int32_t newItemIndex = 0;
1625 Clay__MeasureTextCacheItem newCacheItem = { .measuredWordsStartIndex = -1, .id = id, .generation = context->generation };
1626 Clay__MeasureTextCacheItem *measured = NULL;
1627 if (context->measureTextHashMapInternalFreeList.length > 0) {
1628 newItemIndex = Clay__int32_tArray_GetValue(&context->measureTextHashMapInternalFreeList, context->measureTextHashMapInternalFreeList.length - 1);
1629 context->measureTextHashMapInternalFreeList.length--;
1630 Clay__MeasureTextCacheItemArray_Set(&context->measureTextHashMapInternal, newItemIndex, newCacheItem);
1631 measured = Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, newItemIndex);
1632 } else {
1633 if (context->measureTextHashMapInternal.length == context->measureTextHashMapInternal.capacity - 1) {
1634 if (!context->booleanWarnings.maxTextMeasureCacheExceeded) {
1635 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
1636 .errorType = CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,
1637 .errorText = CLAY_STRING("Clay ran out of capacity while attempting to measure text elements. Try using Clay_SetMaxElementCount() with a higher value."),
1638 .userData = context->errorHandler.userData });
1639 context->booleanWarnings.maxTextMeasureCacheExceeded = true;
1640 }
1641 return &Clay__MeasureTextCacheItem_DEFAULT;
1642 }
1643 measured = Clay__MeasureTextCacheItemArray_Add(&context->measureTextHashMapInternal, newCacheItem);
1644 newItemIndex = context->measureTextHashMapInternal.length - 1;
1645 }
1646
1647 int32_t start = 0;
1648 int32_t end = 0;
1649 float lineWidth = 0;
1650 float measuredWidth = 0;
1651 float measuredHeight = 0;
1652 float spaceWidth = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = 1, .chars = CLAY__SPACECHAR.chars, .baseChars = CLAY__SPACECHAR.chars }, config, context->measureTextUserData).width;
1653 Clay__MeasuredWord tempWord = { .next = -1 };
1654 Clay__MeasuredWord *previousWord = &tempWord;
1655 while (end < text->length) {
1656 if (context->measuredWords.length == context->measuredWords.capacity - 1) {
1657 if (!context->booleanWarnings.maxTextMeasureCacheExceeded) {
1658 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
1659 .errorType = CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
1660 .errorText = CLAY_STRING("Clay has run out of space in it's internal text measurement cache. Try using Clay_SetMaxMeasureTextCacheWordCount() (default 16384, with 1 unit storing 1 measured word)."),
1661 .userData = context->errorHandler.userData });
1662 context->booleanWarnings.maxTextMeasureCacheExceeded = true;
1663 }
1664 return &Clay__MeasureTextCacheItem_DEFAULT;
1665 }
1666 char current = text->chars[end];
1667 if (current == ' ' || current == '\n') {
1668 int32_t length = end - start;
1669 Clay_Dimensions dimensions = CLAY__DEFAULT_STRUCT;
1670 if (length > 0) {
1671 dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) {.length = length, .chars = &text->chars[start], .baseChars = text->chars}, config, context->measureTextUserData);
1672 }
1673 measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
1674 measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
1675 if (current == ' ') {
1676 dimensions.width += spaceWidth;
1677 previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = length + 1, .width = dimensions.width, .next = -1 }, previousWord);
1678 lineWidth += dimensions.width;
1679 }
1680 if (current == '\n') {
1681 if (length > 0) {
1682 previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = length, .width = dimensions.width, .next = -1 }, previousWord);
1683 }
1684 previousWord = Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = end + 1, .length = 0, .width = 0, .next = -1 }, previousWord);
1685 lineWidth += dimensions.width;
1686 measuredWidth = CLAY__MAX(lineWidth, measuredWidth);
1687 measured->containsNewlines = true;
1688 lineWidth = 0;
1689 }
1690 start = end + 1;
1691 }
1692 end++;
1693 }
1694 if (end - start > 0) {
1695 Clay_Dimensions dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = end - start, .chars = &text->chars[start], .baseChars = text->chars }, config, context->measureTextUserData);
1696 Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = end - start, .width = dimensions.width, .next = -1 }, previousWord);
1697 lineWidth += dimensions.width;
1698 measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
1699 measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
1700 }
1701 measuredWidth = CLAY__MAX(lineWidth, measuredWidth) - config->letterSpacing;
1702
1703 measured->measuredWordsStartIndex = tempWord.next;
1704 measured->unwrappedDimensions.width = measuredWidth;
1705 measured->unwrappedDimensions.height = measuredHeight;
1706
1707 if (elementIndexPrevious != 0) {
1708 Clay__MeasureTextCacheItemArray_Get(&context->measureTextHashMapInternal, elementIndexPrevious)->nextIndex = newItemIndex;
1709 } else {
1710 context->measureTextHashMap.internalArray[hashBucket] = newItemIndex;
1711 }
1712 return measured;
1713}
1714
1715bool Clay__PointIsInsideRect(Clay_Vector2 point, Clay_BoundingBox rect) {
1716 return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
1717}
1718
1719Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Clay_LayoutElement* layoutElement) {
1720 Clay_Context* context = Clay_GetCurrentContext();
1721 if (context->layoutElementsHashMapInternal.length == context->layoutElementsHashMapInternal.capacity - 1) {
1722 return NULL;
1723 }
1724 Clay_LayoutElementHashMapItem item = { .elementId = elementId, .layoutElement = layoutElement, .nextIndex = -1, .generation = context->generation + 1 };
1725 uint32_t hashBucket = elementId.id % context->layoutElementsHashMap.capacity;
1726 int32_t hashItemPrevious = -1;
1727 int32_t hashItemIndex = context->layoutElementsHashMap.internalArray[hashBucket];
1728 while (hashItemIndex != -1) { // Just replace collision, not a big deal - leave it up to the end user
1729 Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, hashItemIndex);
1730 if (hashItem->elementId.id == elementId.id) { // Collision - resolve based on generation
1731 item.nextIndex = hashItem->nextIndex;
1732 if (hashItem->generation <= context->generation) { // First collision - assume this is the "same" element
1733 hashItem->elementId = elementId; // Make sure to copy this across. If the stringId reference has changed, we should update the hash item to use the new one.
1734 hashItem->generation = context->generation + 1;
1735 hashItem->layoutElement = layoutElement;
1736 hashItem->debugData->collision = false;
1737 hashItem->onHoverFunction = NULL;
1738 hashItem->hoverFunctionUserData = 0;
1739 } else { // Multiple collisions this frame - two elements have the same ID
1740 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
1741 .errorType = CLAY_ERROR_TYPE_DUPLICATE_ID,
1742 .errorText = CLAY_STRING("An element with this ID was already previously declared during this layout."),
1743 .userData = context->errorHandler.userData });
1744 if (context->debugModeEnabled) {
1745 hashItem->debugData->collision = true;
1746 }
1747 }
1748 return hashItem;
1749 }
1750 hashItemPrevious = hashItemIndex;
1751 hashItemIndex = hashItem->nextIndex;
1752 }
1753 Clay_LayoutElementHashMapItem *hashItem = Clay__LayoutElementHashMapItemArray_Add(&context->layoutElementsHashMapInternal, item);
1754 hashItem->debugData = Clay__DebugElementDataArray_Add(&context->debugElementData, CLAY__INIT(Clay__DebugElementData) CLAY__DEFAULT_STRUCT);
1755 if (hashItemPrevious != -1) {
1756 Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, hashItemPrevious)->nextIndex = (int32_t)context->layoutElementsHashMapInternal.length - 1;
1757 } else {
1758 context->layoutElementsHashMap.internalArray[hashBucket] = (int32_t)context->layoutElementsHashMapInternal.length - 1;
1759 }
1760 return hashItem;
1761}
1762
1763Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) {
1764 Clay_Context* context = Clay_GetCurrentContext();
1765 uint32_t hashBucket = id % context->layoutElementsHashMap.capacity;
1766 int32_t elementIndex = context->layoutElementsHashMap.internalArray[hashBucket];
1767 while (elementIndex != -1) {
1768 Clay_LayoutElementHashMapItem *hashEntry = Clay__LayoutElementHashMapItemArray_Get(&context->layoutElementsHashMapInternal, elementIndex);
1769 if (hashEntry->elementId.id == id) {
1770 return hashEntry;
1771 }
1772 elementIndex = hashEntry->nextIndex;
1773 }
1774 return &Clay_LayoutElementHashMapItem_DEFAULT;
1775}
1776
1777Clay_ElementId Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) {
1778 Clay_Context* context = Clay_GetCurrentContext();
1779 Clay_LayoutElement *parentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));
1780 uint32_t offset = parentElement->childrenOrTextContent.children.length + parentElement->floatingChildrenCount;
1781 Clay_ElementId elementId = Clay__HashNumber(offset, parentElement->id);
1782 openLayoutElement->id = elementId.id;
1783 Clay__AddHashMapItem(elementId, openLayoutElement);
1784 Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
1785 return elementId;
1786}
1787
1788bool Clay__ElementHasConfig(Clay_LayoutElement *layoutElement, Clay__ElementConfigType type) {
1789 for (int32_t i = 0; i < layoutElement->elementConfigs.length; i++) {
1790 if (Clay__ElementConfigArraySlice_Get(&layoutElement->elementConfigs, i)->type == type) {
1791 return true;
1792 }
1793 }
1794 return false;
1795}
1796
1797void Clay__UpdateAspectRatioBox(Clay_LayoutElement *layoutElement) {
1798 for (int32_t j = 0; j < layoutElement->elementConfigs.length; j++) {
1799 Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&layoutElement->elementConfigs, j);
1800 if (config->type == CLAY__ELEMENT_CONFIG_TYPE_ASPECT) {
1801 Clay_AspectRatioElementConfig *aspectConfig = config->config.aspectRatioElementConfig;
1802 if (aspectConfig->aspectRatio == 0) {
1803 break;
1804 }
1805 if (layoutElement->dimensions.width == 0 && layoutElement->dimensions.height != 0) {
1806 layoutElement->dimensions.width = layoutElement->dimensions.height * aspectConfig->aspectRatio;
1807 } else if (layoutElement->dimensions.width != 0 && layoutElement->dimensions.height == 0) {
1808 layoutElement->dimensions.height = layoutElement->dimensions.width * (1 / aspectConfig->aspectRatio);
1809 }
1810 break;
1811 }
1812 }
1813}
1814
1815void Clay__CloseElement(void) {
1816 Clay_Context* context = Clay_GetCurrentContext();
1817 if (context->booleanWarnings.maxElementsExceeded) {
1818 return;
1819 }
1820 Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
1821 Clay_LayoutConfig *layoutConfig = openLayoutElement->layoutConfig;
1822 if (!layoutConfig) {
1823 openLayoutElement->layoutConfig = &Clay_LayoutConfig_DEFAULT;
1824 layoutConfig = &Clay_LayoutConfig_DEFAULT;
1825 }
1826 bool elementHasClipHorizontal = false;
1827 bool elementHasClipVertical = false;
1828 for (int32_t i = 0; i < openLayoutElement->elementConfigs.length; i++) {
1829 Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&openLayoutElement->elementConfigs, i);
1830 if (config->type == CLAY__ELEMENT_CONFIG_TYPE_CLIP) {
1831 elementHasClipHorizontal = config->config.clipElementConfig->horizontal;
1832 elementHasClipVertical = config->config.clipElementConfig->vertical;
1833 context->openClipElementStack.length--;
1834 break;
1835 } else if (config->type == CLAY__ELEMENT_CONFIG_TYPE_FLOATING) {
1836 context->openClipElementStack.length--;
1837 }
1838 }
1839
1840 float leftRightPadding = (float)(layoutConfig->padding.left + layoutConfig->padding.right);
1841 float topBottomPadding = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom);
1842
1843 // Attach children to the current open element
1844 openLayoutElement->childrenOrTextContent.children.elements = &context->layoutElementChildren.internalArray[context->layoutElementChildren.length];
1845 if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
1846 openLayoutElement->dimensions.width = leftRightPadding;
1847 openLayoutElement->minDimensions.width = leftRightPadding;
1848 for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) {
1849 int32_t childIndex = Clay__int32_tArray_GetValue(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i);
1850 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
1851 openLayoutElement->dimensions.width += child->dimensions.width;
1852 openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + topBottomPadding);
1853 // Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents
1854 if (!elementHasClipHorizontal) {
1855 openLayoutElement->minDimensions.width += child->minDimensions.width;
1856 }
1857 if (!elementHasClipVertical) {
1858 openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + topBottomPadding);
1859 }
1860 Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
1861 }
1862 float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
1863 openLayoutElement->dimensions.width += childGap;
1864 if (!elementHasClipHorizontal) {
1865 openLayoutElement->minDimensions.width += childGap;
1866 }
1867 }
1868 else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
1869 openLayoutElement->dimensions.height = topBottomPadding;
1870 openLayoutElement->minDimensions.height = topBottomPadding;
1871 for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) {
1872 int32_t childIndex = Clay__int32_tArray_GetValue(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i);
1873 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
1874 openLayoutElement->dimensions.height += child->dimensions.height;
1875 openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + leftRightPadding);
1876 // Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents
1877 if (!elementHasClipVertical) {
1878 openLayoutElement->minDimensions.height += child->minDimensions.height;
1879 }
1880 if (!elementHasClipHorizontal) {
1881 openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + leftRightPadding);
1882 }
1883 Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
1884 }
1885 float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
1886 openLayoutElement->dimensions.height += childGap;
1887 if (!elementHasClipVertical) {
1888 openLayoutElement->minDimensions.height += childGap;
1889 }
1890 }
1891
1892 context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.children.length;
1893
1894 // Clamp element min and max width to the values configured in the layout
1895 if (layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {
1896 if (layoutConfig->sizing.width.size.minMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier
1897 layoutConfig->sizing.width.size.minMax.max = CLAY__MAXFLOAT;
1898 }
1899 openLayoutElement->dimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.width, layoutConfig->sizing.width.size.minMax.min), layoutConfig->sizing.width.size.minMax.max);
1900 openLayoutElement->minDimensions.width = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.width, layoutConfig->sizing.width.size.minMax.min), layoutConfig->sizing.width.size.minMax.max);
1901 } else {
1902 openLayoutElement->dimensions.width = 0;
1903 }
1904
1905 // Clamp element min and max height to the values configured in the layout
1906 if (layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) {
1907 if (layoutConfig->sizing.height.size.minMax.max <= 0) { // Set the max size if the user didn't specify, makes calculations easier
1908 layoutConfig->sizing.height.size.minMax.max = CLAY__MAXFLOAT;
1909 }
1910 openLayoutElement->dimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->dimensions.height, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);
1911 openLayoutElement->minDimensions.height = CLAY__MIN(CLAY__MAX(openLayoutElement->minDimensions.height, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);
1912 } else {
1913 openLayoutElement->dimensions.height = 0;
1914 }
1915
1916 Clay__UpdateAspectRatioBox(openLayoutElement);
1917
1918 bool elementIsFloating = Clay__ElementHasConfig(openLayoutElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING);
1919
1920 // Close the currently open element
1921 int32_t closingElementIndex = Clay__int32_tArray_RemoveSwapback(&context->openLayoutElementStack, (int)context->openLayoutElementStack.length - 1);
1922
1923 // Get the currently open parent
1924 openLayoutElement = Clay__GetOpenLayoutElement();
1925
1926 if (context->openLayoutElementStack.length > 1) {
1927 if(elementIsFloating) {
1928 openLayoutElement->floatingChildrenCount++;
1929 return;
1930 }
1931 openLayoutElement->childrenOrTextContent.children.length++;
1932 Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, closingElementIndex);
1933 }
1934}
1935
1936bool Clay__MemCmp(const char *s1, const char *s2, int32_t length);
1937#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
1938 bool Clay__MemCmp(const char *s1, const char *s2, int32_t length) {
1939 while (length >= 16) {
1940 __m128i v1 = _mm_loadu_si128((const __m128i *)s1);
1941 __m128i v2 = _mm_loadu_si128((const __m128i *)s2);
1942
1943 if (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) != 0xFFFF) { // If any byte differs
1944 return false;
1945 }
1946
1947 s1 += 16;
1948 s2 += 16;
1949 length -= 16;
1950 }
1951
1952 // Handle remaining bytes
1953 while (length--) {
1954 if (*s1 != *s2) {
1955 return false;
1956 }
1957 s1++;
1958 s2++;
1959 }
1960
1961 return true;
1962 }
1963#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)
1964 bool Clay__MemCmp(const char *s1, const char *s2, int32_t length) {
1965 while (length >= 16) {
1966 uint8x16_t v1 = vld1q_u8((const uint8_t *)s1);
1967 uint8x16_t v2 = vld1q_u8((const uint8_t *)s2);
1968
1969 // Compare vectors
1970 if (vminvq_u32(vreinterpretq_u32_u8(vceqq_u8(v1, v2))) != 0xFFFFFFFF) { // If there's a difference
1971 return false;
1972 }
1973
1974 s1 += 16;
1975 s2 += 16;
1976 length -= 16;
1977 }
1978
1979 // Handle remaining bytes
1980 while (length--) {
1981 if (*s1 != *s2) {
1982 return false;
1983 }
1984 s1++;
1985 s2++;
1986 }
1987
1988 return true;
1989 }
1990#else
1991 bool Clay__MemCmp(const char *s1, const char *s2, int32_t length) {
1992 for (int32_t i = 0; i < length; i++) {
1993 if (s1[i] != s2[i]) {
1994 return false;
1995 }
1996 }
1997 return true;
1998 }
1999#endif
2000
2001void Clay__OpenElement(void) {
2002 Clay_Context* context = Clay_GetCurrentContext();
2003 if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {
2004 context->booleanWarnings.maxElementsExceeded = true;
2005 return;
2006 }
2007 Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;
2008 Clay_LayoutElement* openLayoutElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);
2009 Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1);
2010 Clay__GenerateIdForAnonymousElement(openLayoutElement);
2011 if (context->openClipElementStack.length > 0) {
2012 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));
2013 } else {
2014 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);
2015 }
2016}
2017
2018void Clay__OpenElementWithId(Clay_ElementId elementId) {
2019 Clay_Context* context = Clay_GetCurrentContext();
2020 if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {
2021 context->booleanWarnings.maxElementsExceeded = true;
2022 return;
2023 }
2024 Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;
2025 layoutElement.id = elementId.id;
2026 Clay_LayoutElement * openLayoutElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);
2027 Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1);
2028 Clay__AddHashMapItem(elementId, openLayoutElement);
2029 Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
2030 if (context->openClipElementStack.length > 0) {
2031 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));
2032 } else {
2033 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);
2034 }
2035}
2036
2037void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig) {
2038 Clay_Context* context = Clay_GetCurrentContext();
2039 if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {
2040 context->booleanWarnings.maxElementsExceeded = true;
2041 return;
2042 }
2043 Clay_LayoutElement *parentElement = Clay__GetOpenLayoutElement();
2044
2045 Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;
2046 Clay_LayoutElement *textElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);
2047 if (context->openClipElementStack.length > 0) {
2048 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));
2049 } else {
2050 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);
2051 }
2052
2053 Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, context->layoutElements.length - 1);
2054 Clay__MeasureTextCacheItem *textMeasured = Clay__MeasureTextCached(&text, textConfig);
2055 Clay_ElementId elementId = Clay__HashNumber(parentElement->childrenOrTextContent.children.length + parentElement->floatingChildrenCount, parentElement->id);
2056 textElement->id = elementId.id;
2057 Clay__AddHashMapItem(elementId, textElement);
2058 Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
2059 Clay_Dimensions textDimensions = { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? (float)textConfig->lineHeight : textMeasured->unwrappedDimensions.height };
2060 textElement->dimensions = textDimensions;
2061 textElement->minDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->minWidth, .height = textDimensions.height };
2062 textElement->childrenOrTextContent.textElementData = Clay__TextElementDataArray_Add(&context->textElementData, CLAY__INIT(Clay__TextElementData) { .text = text, .preferredDimensions = textMeasured->unwrappedDimensions, .elementIndex = context->layoutElements.length - 1 });
2063 textElement->elementConfigs = CLAY__INIT(Clay__ElementConfigArraySlice) {
2064 .length = 1,
2065 .internalArray = Clay__ElementConfigArray_Add(&context->elementConfigs, CLAY__INIT(Clay_ElementConfig) { .type = CLAY__ELEMENT_CONFIG_TYPE_TEXT, .config = { .textElementConfig = textConfig }})
2066 };
2067 textElement->layoutConfig = &CLAY_LAYOUT_DEFAULT;
2068 parentElement->childrenOrTextContent.children.length++;
2069}
2070
2071void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
2072 Clay_Context* context = Clay_GetCurrentContext();
2073 Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
2074 openLayoutElement->layoutConfig = Clay__StoreLayoutConfig(declaration->layout);
2075 if ((declaration->layout.sizing.width.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.width.size.percent > 1) || (declaration->layout.sizing.height.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.height.size.percent > 1)) {
2076 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
2077 .errorType = CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,
2078 .errorText = CLAY_STRING("An element was configured with CLAY_SIZING_PERCENT, but the provided percentage value was over 1.0. Clay expects a value between 0 and 1, i.e. 20% is 0.2."),
2079 .userData = context->errorHandler.userData });
2080 }
2081
2082 openLayoutElement->elementConfigs.internalArray = &context->elementConfigs.internalArray[context->elementConfigs.length];
2083 Clay_SharedElementConfig *sharedConfig = NULL;
2084 if (declaration->backgroundColor.a > 0) {
2085 sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .backgroundColor = declaration->backgroundColor });
2086 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
2087 }
2088 if (!Clay__MemCmp((char *)(&declaration->cornerRadius), (char *)(&Clay__CornerRadius_DEFAULT), sizeof(Clay_CornerRadius))) {
2089 if (sharedConfig) {
2090 sharedConfig->cornerRadius = declaration->cornerRadius;
2091 } else {
2092 sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .cornerRadius = declaration->cornerRadius });
2093 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
2094 }
2095 }
2096 if (declaration->userData != 0) {
2097 if (sharedConfig) {
2098 sharedConfig->userData = declaration->userData;
2099 } else {
2100 sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .userData = declaration->userData });
2101 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
2102 }
2103 }
2104 if (declaration->image.imageData) {
2105 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration->image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);
2106 }
2107 if (declaration->aspectRatio.aspectRatio > 0) {
2108 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .aspectRatioElementConfig = Clay__StoreAspectRatioElementConfig(declaration->aspectRatio) }, CLAY__ELEMENT_CONFIG_TYPE_ASPECT);
2109 Clay__int32_tArray_Add(&context->aspectRatioElementIndexes, context->layoutElements.length - 1);
2110 }
2111 if (declaration->floating.attachTo != CLAY_ATTACH_TO_NONE) {
2112 Clay_FloatingElementConfig floatingConfig = declaration->floating;
2113 // This looks dodgy but because of the auto generated root element the depth of the tree will always be at least 2 here
2114 Clay_LayoutElement *hierarchicalParent = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));
2115 if (hierarchicalParent) {
2116 uint32_t clipElementId = 0;
2117 if (declaration->floating.attachTo == CLAY_ATTACH_TO_PARENT) {
2118 // Attach to the element's direct hierarchical parent
2119 floatingConfig.parentId = hierarchicalParent->id;
2120 if (context->openClipElementStack.length > 0) {
2121 clipElementId = Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1);
2122 }
2123 } else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {
2124 Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig.parentId);
2125 if (parentItem == &Clay_LayoutElementHashMapItem_DEFAULT) {
2126 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
2127 .errorType = CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,
2128 .errorText = CLAY_STRING("A floating element was declared with a parentId, but no element with that ID was found."),
2129 .userData = context->errorHandler.userData });
2130 } else {
2131 clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(parentItem->layoutElement - context->layoutElements.internalArray));
2132 }
2133 } else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ROOT) {
2134 floatingConfig.parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0).id;
2135 }
2136 if (declaration->floating.clipTo == CLAY_CLIP_TO_NONE) {
2137 clipElementId = 0;
2138 }
2139 int32_t currentElementIndex = Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1);
2140 Clay__int32_tArray_Set(&context->layoutElementClipElementIds, currentElementIndex, clipElementId);
2141 Clay__int32_tArray_Add(&context->openClipElementStack, clipElementId);
2142 Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) {
2143 .layoutElementIndex = Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1),
2144 .parentId = floatingConfig.parentId,
2145 .clipElementId = clipElementId,
2146 .zIndex = floatingConfig.zIndex,
2147 });
2148 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .floatingElementConfig = Clay__StoreFloatingElementConfig(floatingConfig) }, CLAY__ELEMENT_CONFIG_TYPE_FLOATING);
2149 }
2150 }
2151 if (declaration->custom.customData) {
2152 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration->custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);
2153 }
2154
2155 if (declaration->clip.horizontal | declaration->clip.vertical) {
2156 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .clipElementConfig = Clay__StoreClipElementConfig(declaration->clip) }, CLAY__ELEMENT_CONFIG_TYPE_CLIP);
2157 Clay__int32_tArray_Add(&context->openClipElementStack, (int)openLayoutElement->id);
2158 // Retrieve or create cached data to track scroll position across frames
2159 Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL;
2160 for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {
2161 Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
2162 if (openLayoutElement->id == mapping->elementId) {
2163 scrollOffset = mapping;
2164 scrollOffset->layoutElement = openLayoutElement;
2165 scrollOffset->openThisFrame = true;
2166 }
2167 }
2168 if (!scrollOffset) {
2169 scrollOffset = Clay__ScrollContainerDataInternalArray_Add(&context->scrollContainerDatas, CLAY__INIT(Clay__ScrollContainerDataInternal){.layoutElement = openLayoutElement, .scrollOrigin = {-1,-1}, .elementId = openLayoutElement->id, .openThisFrame = true});
2170 }
2171 if (context->externalScrollHandlingEnabled) {
2172 scrollOffset->scrollPosition = Clay__QueryScrollOffset(scrollOffset->elementId, context->queryScrollOffsetUserData);
2173 }
2174 }
2175 if (!Clay__MemCmp((char *)(&declaration->border.width), (char *)(&Clay__BorderWidth_DEFAULT), sizeof(Clay_BorderWidth))) {
2176 Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(declaration->border) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER);
2177 }
2178}
2179
2180void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
2181 Clay__ConfigureOpenElementPtr(&declaration);
2182}
2183
2184void Clay__InitializeEphemeralMemory(Clay_Context* context) {
2185 int32_t maxElementCount = context->maxElementCount;
2186 // Ephemeral Memory - reset every frame
2187 Clay_Arena *arena = &context->internalArena;
2188 arena->nextAllocation = context->arenaResetOffset;
2189
2190 context->layoutElementChildrenBuffer = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2191 context->layoutElements = Clay_LayoutElementArray_Allocate_Arena(maxElementCount, arena);
2192 context->warnings = Clay__WarningArray_Allocate_Arena(100, arena);
2193
2194 context->layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(maxElementCount, arena);
2195 context->elementConfigs = Clay__ElementConfigArray_Allocate_Arena(maxElementCount, arena);
2196 context->textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(maxElementCount, arena);
2197 context->aspectRatioElementConfigs = Clay__AspectRatioElementConfigArray_Allocate_Arena(maxElementCount, arena);
2198 context->imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(maxElementCount, arena);
2199 context->floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(maxElementCount, arena);
2200 context->clipElementConfigs = Clay__ClipElementConfigArray_Allocate_Arena(maxElementCount, arena);
2201 context->customElementConfigs = Clay__CustomElementConfigArray_Allocate_Arena(maxElementCount, arena);
2202 context->borderElementConfigs = Clay__BorderElementConfigArray_Allocate_Arena(maxElementCount, arena);
2203 context->sharedElementConfigs = Clay__SharedElementConfigArray_Allocate_Arena(maxElementCount, arena);
2204
2205 context->layoutElementIdStrings = Clay__StringArray_Allocate_Arena(maxElementCount, arena);
2206 context->wrappedTextLines = Clay__WrappedTextLineArray_Allocate_Arena(maxElementCount, arena);
2207 context->layoutElementTreeNodeArray1 = Clay__LayoutElementTreeNodeArray_Allocate_Arena(maxElementCount, arena);
2208 context->layoutElementTreeRoots = Clay__LayoutElementTreeRootArray_Allocate_Arena(maxElementCount, arena);
2209 context->layoutElementChildren = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2210 context->openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2211 context->textElementData = Clay__TextElementDataArray_Allocate_Arena(maxElementCount, arena);
2212 context->aspectRatioElementIndexes = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2213 context->renderCommands = Clay_RenderCommandArray_Allocate_Arena(maxElementCount, arena);
2214 context->treeNodeVisited = Clay__boolArray_Allocate_Arena(maxElementCount, arena);
2215 context->treeNodeVisited.length = context->treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list
2216 context->openClipElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2217 context->reusableElementIndexBuffer = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2218 context->layoutElementClipElementIds = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2219 context->dynamicStringData = Clay__charArray_Allocate_Arena(maxElementCount, arena);
2220}
2221
2222void Clay__InitializePersistentMemory(Clay_Context* context) {
2223 // Persistent memory - initialized once and not reset
2224 int32_t maxElementCount = context->maxElementCount;
2225 int32_t maxMeasureTextCacheWordCount = context->maxMeasureTextCacheWordCount;
2226 Clay_Arena *arena = &context->internalArena;
2227
2228 context->scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(100, arena);
2229 context->layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(maxElementCount, arena);
2230 context->layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2231 context->measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(maxElementCount, arena);
2232 context->measureTextHashMapInternalFreeList = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2233 context->measuredWordsFreeList = Clay__int32_tArray_Allocate_Arena(maxMeasureTextCacheWordCount, arena);
2234 context->measureTextHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
2235 context->measuredWords = Clay__MeasuredWordArray_Allocate_Arena(maxMeasureTextCacheWordCount, arena);
2236 context->pointerOverIds = Clay_ElementIdArray_Allocate_Arena(maxElementCount, arena);
2237 context->debugElementData = Clay__DebugElementDataArray_Allocate_Arena(maxElementCount, arena);
2238 context->arenaResetOffset = arena->nextAllocation;
2239}
2240
2241const float CLAY__EPSILON = 0.01;
2242
2243bool Clay__FloatEqual(float left, float right) {
2244 float subtracted = left - right;
2245 return subtracted < CLAY__EPSILON && subtracted > -CLAY__EPSILON;
2246}
2247
2248void Clay__SizeContainersAlongAxis(bool xAxis) {
2249 Clay_Context* context = Clay_GetCurrentContext();
2250 Clay__int32_tArray bfsBuffer = context->layoutElementChildrenBuffer;
2251 Clay__int32_tArray resizableContainerBuffer = context->openLayoutElementStack;
2252 for (int32_t rootIndex = 0; rootIndex < context->layoutElementTreeRoots.length; ++rootIndex) {
2253 bfsBuffer.length = 0;
2254 Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);
2255 Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex);
2256 Clay__int32_tArray_Add(&bfsBuffer, (int32_t)root->layoutElementIndex);
2257
2258 // Size floating containers to their parents
2259 if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING)) {
2260 Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig;
2261 Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId);
2262 if (parentItem && parentItem != &Clay_LayoutElementHashMapItem_DEFAULT) {
2263 Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement;
2264 switch (rootElement->layoutConfig->sizing.width.type) {
2265 case CLAY__SIZING_TYPE_GROW: {
2266 rootElement->dimensions.width = parentLayoutElement->dimensions.width;
2267 break;
2268 }
2269 case CLAY__SIZING_TYPE_PERCENT: {
2270 rootElement->dimensions.width = parentLayoutElement->dimensions.width * rootElement->layoutConfig->sizing.width.size.percent;
2271 break;
2272 }
2273 default: break;
2274 }
2275 switch (rootElement->layoutConfig->sizing.height.type) {
2276 case CLAY__SIZING_TYPE_GROW: {
2277 rootElement->dimensions.height = parentLayoutElement->dimensions.height;
2278 break;
2279 }
2280 case CLAY__SIZING_TYPE_PERCENT: {
2281 rootElement->dimensions.height = parentLayoutElement->dimensions.height * rootElement->layoutConfig->sizing.height.size.percent;
2282 break;
2283 }
2284 default: break;
2285 }
2286 }
2287 }
2288
2289 if (rootElement->layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {
2290 rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.size.minMax.min), rootElement->layoutConfig->sizing.width.size.minMax.max);
2291 }
2292 if (rootElement->layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) {
2293 rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.size.minMax.min), rootElement->layoutConfig->sizing.height.size.minMax.max);
2294 }
2295
2296 for (int32_t i = 0; i < bfsBuffer.length; ++i) {
2297 int32_t parentIndex = Clay__int32_tArray_GetValue(&bfsBuffer, i);
2298 Clay_LayoutElement *parent = Clay_LayoutElementArray_Get(&context->layoutElements, parentIndex);
2299 Clay_LayoutConfig *parentStyleConfig = parent->layoutConfig;
2300 int32_t growContainerCount = 0;
2301 float parentSize = xAxis ? parent->dimensions.width : parent->dimensions.height;
2302 float parentPadding = (float)(xAxis ? (parent->layoutConfig->padding.left + parent->layoutConfig->padding.right) : (parent->layoutConfig->padding.top + parent->layoutConfig->padding.bottom));
2303 float innerContentSize = 0, totalPaddingAndChildGaps = parentPadding;
2304 bool sizingAlongAxis = (xAxis && parentStyleConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) || (!xAxis && parentStyleConfig->layoutDirection == CLAY_TOP_TO_BOTTOM);
2305 resizableContainerBuffer.length = 0;
2306 float parentChildGap = parentStyleConfig->childGap;
2307
2308 for (int32_t childOffset = 0; childOffset < parent->childrenOrTextContent.children.length; childOffset++) {
2309 int32_t childElementIndex = parent->childrenOrTextContent.children.elements[childOffset];
2310 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, childElementIndex);
2311 Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
2312 float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height;
2313
2314 if (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && childElement->childrenOrTextContent.children.length > 0) {
2315 Clay__int32_tArray_Add(&bfsBuffer, childElementIndex);
2316 }
2317
2318 if (childSizing.type != CLAY__SIZING_TYPE_PERCENT
2319 && childSizing.type != CLAY__SIZING_TYPE_FIXED
2320 && (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS)) // todo too many loops
2321// && (xAxis || !Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT))
2322 ) {
2323 Clay__int32_tArray_Add(&resizableContainerBuffer, childElementIndex);
2324 }
2325
2326 if (sizingAlongAxis) {
2327 innerContentSize += (childSizing.type == CLAY__SIZING_TYPE_PERCENT ? 0 : childSize);
2328 if (childSizing.type == CLAY__SIZING_TYPE_GROW) {
2329 growContainerCount++;
2330 }
2331 if (childOffset > 0) {
2332 innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child
2333 totalPaddingAndChildGaps += parentChildGap;
2334 }
2335 } else {
2336 innerContentSize = CLAY__MAX(childSize, innerContentSize);
2337 }
2338 }
2339
2340 // Expand percentage containers to size
2341 for (int32_t childOffset = 0; childOffset < parent->childrenOrTextContent.children.length; childOffset++) {
2342 int32_t childElementIndex = parent->childrenOrTextContent.children.elements[childOffset];
2343 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, childElementIndex);
2344 Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
2345 float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
2346 if (childSizing.type == CLAY__SIZING_TYPE_PERCENT) {
2347 *childSize = (parentSize - totalPaddingAndChildGaps) * childSizing.size.percent;
2348 if (sizingAlongAxis) {
2349 innerContentSize += *childSize;
2350 }
2351 Clay__UpdateAspectRatioBox(childElement);
2352 }
2353 }
2354
2355 if (sizingAlongAxis) {
2356 float sizeToDistribute = parentSize - parentPadding - innerContentSize;
2357 // The content is too large, compress the children as much as possible
2358 if (sizeToDistribute < 0) {
2359 // If the parent clips content in this axis direction, don't compress children, just leave them alone
2360 Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
2361 if (clipElementConfig) {
2362 if (((xAxis && clipElementConfig->horizontal) || (!xAxis && clipElementConfig->vertical))) {
2363 continue;
2364 }
2365 }
2366 // Scrolling containers preferentially compress before others
2367 while (sizeToDistribute < -CLAY__EPSILON && resizableContainerBuffer.length > 0) {
2368 float largest = 0;
2369 float secondLargest = 0;
2370 float widthToAdd = sizeToDistribute;
2371 for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {
2372 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));
2373 float childSize = xAxis ? child->dimensions.width : child->dimensions.height;
2374 if (Clay__FloatEqual(childSize, largest)) { continue; }
2375 if (childSize > largest) {
2376 secondLargest = largest;
2377 largest = childSize;
2378 }
2379 if (childSize < largest) {
2380 secondLargest = CLAY__MAX(secondLargest, childSize);
2381 widthToAdd = secondLargest - largest;
2382 }
2383 }
2384
2385 widthToAdd = CLAY__MAX(widthToAdd, sizeToDistribute / resizableContainerBuffer.length);
2386
2387 for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {
2388 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));
2389 float *childSize = xAxis ? &child->dimensions.width : &child->dimensions.height;
2390 float minSize = xAxis ? child->minDimensions.width : child->minDimensions.height;
2391 float previousWidth = *childSize;
2392 if (Clay__FloatEqual(*childSize, largest)) {
2393 *childSize += widthToAdd;
2394 if (*childSize <= minSize) {
2395 *childSize = minSize;
2396 Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childIndex--);
2397 }
2398 sizeToDistribute -= (*childSize - previousWidth);
2399 }
2400 }
2401 }
2402 // The content is too small, allow SIZING_GROW containers to expand
2403 } else if (sizeToDistribute > 0 && growContainerCount > 0) {
2404 for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {
2405 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));
2406 Clay__SizingType childSizing = xAxis ? child->layoutConfig->sizing.width.type : child->layoutConfig->sizing.height.type;
2407 if (childSizing != CLAY__SIZING_TYPE_GROW) {
2408 Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childIndex--);
2409 }
2410 }
2411 while (sizeToDistribute > CLAY__EPSILON && resizableContainerBuffer.length > 0) {
2412 float smallest = CLAY__MAXFLOAT;
2413 float secondSmallest = CLAY__MAXFLOAT;
2414 float widthToAdd = sizeToDistribute;
2415 for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {
2416 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));
2417 float childSize = xAxis ? child->dimensions.width : child->dimensions.height;
2418 if (Clay__FloatEqual(childSize, smallest)) { continue; }
2419 if (childSize < smallest) {
2420 secondSmallest = smallest;
2421 smallest = childSize;
2422 }
2423 if (childSize > smallest) {
2424 secondSmallest = CLAY__MIN(secondSmallest, childSize);
2425 widthToAdd = secondSmallest - smallest;
2426 }
2427 }
2428
2429 widthToAdd = CLAY__MIN(widthToAdd, sizeToDistribute / resizableContainerBuffer.length);
2430
2431 for (int childIndex = 0; childIndex < resizableContainerBuffer.length; childIndex++) {
2432 Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childIndex));
2433 float *childSize = xAxis ? &child->dimensions.width : &child->dimensions.height;
2434 float maxSize = xAxis ? child->layoutConfig->sizing.width.size.minMax.max : child->layoutConfig->sizing.height.size.minMax.max;
2435 float previousWidth = *childSize;
2436 if (Clay__FloatEqual(*childSize, smallest)) {
2437 *childSize += widthToAdd;
2438 if (*childSize >= maxSize) {
2439 *childSize = maxSize;
2440 Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childIndex--);
2441 }
2442 sizeToDistribute -= (*childSize - previousWidth);
2443 }
2444 }
2445 }
2446 }
2447 // Sizing along the non layout axis ("off axis")
2448 } else {
2449 for (int32_t childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) {
2450 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childOffset));
2451 Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
2452 float minSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height;
2453 float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
2454
2455 float maxSize = parentSize - parentPadding;
2456 // If we're laying out the children of a scroll panel, grow containers expand to the size of the inner content, not the outer container
2457 if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP)) {
2458 Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
2459 if (((xAxis && clipElementConfig->horizontal) || (!xAxis && clipElementConfig->vertical))) {
2460 maxSize = CLAY__MAX(maxSize, innerContentSize);
2461 }
2462 }
2463 if (childSizing.type == CLAY__SIZING_TYPE_GROW) {
2464 *childSize = CLAY__MIN(maxSize, childSizing.size.minMax.max);
2465 }
2466 *childSize = CLAY__MAX(minSize, CLAY__MIN(*childSize, maxSize));
2467 }
2468 }
2469 }
2470 }
2471}
2472
2473Clay_String Clay__IntToString(int32_t integer) {
2474 if (integer == 0) {
2475 return CLAY__INIT(Clay_String) { .length = 1, .chars = "0" };
2476 }
2477 Clay_Context* context = Clay_GetCurrentContext();
2478 char *chars = (char *)(context->dynamicStringData.internalArray + context->dynamicStringData.length);
2479 int32_t length = 0;
2480 int32_t sign = integer;
2481
2482 if (integer < 0) {
2483 integer = -integer;
2484 }
2485 while (integer > 0) {
2486 chars[length++] = (char)(integer % 10 + '0');
2487 integer /= 10;
2488 }
2489
2490 if (sign < 0) {
2491 chars[length++] = '-';
2492 }
2493
2494 // Reverse the string to get the correct order
2495 for (int32_t j = 0, k = length - 1; j < k; j++, k--) {
2496 char temp = chars[j];
2497 chars[j] = chars[k];
2498 chars[k] = temp;
2499 }
2500 context->dynamicStringData.length += length;
2501 return CLAY__INIT(Clay_String) { .length = length, .chars = chars };
2502}
2503
2504void Clay__AddRenderCommand(Clay_RenderCommand renderCommand) {
2505 Clay_Context* context = Clay_GetCurrentContext();
2506 if (context->renderCommands.length < context->renderCommands.capacity - 1) {
2507 Clay_RenderCommandArray_Add(&context->renderCommands, renderCommand);
2508 } else {
2509 if (!context->booleanWarnings.maxRenderCommandsExceeded) {
2510 context->booleanWarnings.maxRenderCommandsExceeded = true;
2511 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
2512 .errorType = CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,
2513 .errorText = CLAY_STRING("Clay ran out of capacity while attempting to create render commands. This is usually caused by a large amount of wrapping text elements while close to the max element capacity. Try using Clay_SetMaxElementCount() with a higher value."),
2514 .userData = context->errorHandler.userData });
2515 }
2516 }
2517}
2518
2519bool Clay__ElementIsOffscreen(Clay_BoundingBox *boundingBox) {
2520 Clay_Context* context = Clay_GetCurrentContext();
2521 if (context->disableCulling) {
2522 return false;
2523 }
2524
2525 return (boundingBox->x > (float)context->layoutDimensions.width) ||
2526 (boundingBox->y > (float)context->layoutDimensions.height) ||
2527 (boundingBox->x + boundingBox->width < 0) ||
2528 (boundingBox->y + boundingBox->height < 0);
2529}
2530
2531void Clay__CalculateFinalLayout(void) {
2532 Clay_Context* context = Clay_GetCurrentContext();
2533 // Calculate sizing along the X axis
2534 Clay__SizeContainersAlongAxis(true);
2535
2536 // Wrap text
2537 for (int32_t textElementIndex = 0; textElementIndex < context->textElementData.length; ++textElementIndex) {
2538 Clay__TextElementData *textElementData = Clay__TextElementDataArray_Get(&context->textElementData, textElementIndex);
2539 textElementData->wrappedLines = CLAY__INIT(Clay__WrappedTextLineArraySlice) { .length = 0, .internalArray = &context->wrappedTextLines.internalArray[context->wrappedTextLines.length] };
2540 Clay_LayoutElement *containerElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)textElementData->elementIndex);
2541 Clay_TextElementConfig *textConfig = Clay__FindElementConfigWithType(containerElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig;
2542 Clay__MeasureTextCacheItem *measureTextCacheItem = Clay__MeasureTextCached(&textElementData->text, textConfig);
2543 float lineWidth = 0;
2544 float lineHeight = textConfig->lineHeight > 0 ? (float)textConfig->lineHeight : textElementData->preferredDimensions.height;
2545 int32_t lineLengthChars = 0;
2546 int32_t lineStartOffset = 0;
2547 if (!measureTextCacheItem->containsNewlines && textElementData->preferredDimensions.width <= containerElement->dimensions.width) {
2548 Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { containerElement->dimensions, textElementData->text });
2549 textElementData->wrappedLines.length++;
2550 continue;
2551 }
2552 float spaceWidth = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = 1, .chars = CLAY__SPACECHAR.chars, .baseChars = CLAY__SPACECHAR.chars }, textConfig, context->measureTextUserData).width;
2553 int32_t wordIndex = measureTextCacheItem->measuredWordsStartIndex;
2554 while (wordIndex != -1) {
2555 if (context->wrappedTextLines.length > context->wrappedTextLines.capacity - 1) {
2556 break;
2557 }
2558 Clay__MeasuredWord *measuredWord = Clay__MeasuredWordArray_Get(&context->measuredWords, wordIndex);
2559 // Only word on the line is too large, just render it anyway
2560 if (lineLengthChars == 0 && lineWidth + measuredWord->width > containerElement->dimensions.width) {
2561 Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { measuredWord->width, lineHeight }, { .length = measuredWord->length, .chars = &textElementData->text.chars[measuredWord->startOffset] } });
2562 textElementData->wrappedLines.length++;
2563 wordIndex = measuredWord->next;
2564 lineStartOffset = measuredWord->startOffset + measuredWord->length;
2565 }
2566 // measuredWord->length == 0 means a newline character
2567 else if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) {
2568 // Wrapped text lines list has overflowed, just render out the line
2569 bool finalCharIsSpace = textElementData->text.chars[CLAY__MAX(lineStartOffset + lineLengthChars - 1, 0)] == ' ';
2570 Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth + (finalCharIsSpace ? -spaceWidth : 0), lineHeight }, { .length = lineLengthChars + (finalCharIsSpace ? -1 : 0), .chars = &textElementData->text.chars[lineStartOffset] } });
2571 textElementData->wrappedLines.length++;
2572 if (lineLengthChars == 0 || measuredWord->length == 0) {
2573 wordIndex = measuredWord->next;
2574 }
2575 lineWidth = 0;
2576 lineLengthChars = 0;
2577 lineStartOffset = measuredWord->startOffset;
2578 } else {
2579 lineWidth += measuredWord->width + textConfig->letterSpacing;
2580 lineLengthChars += measuredWord->length;
2581 wordIndex = measuredWord->next;
2582 }
2583 }
2584 if (lineLengthChars > 0) {
2585 Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth - textConfig->letterSpacing, lineHeight }, {.length = lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } });
2586 textElementData->wrappedLines.length++;
2587 }
2588 containerElement->dimensions.height = lineHeight * (float)textElementData->wrappedLines.length;
2589 }
2590
2591 // Scale vertical heights according to aspect ratio
2592 for (int32_t i = 0; i < context->aspectRatioElementIndexes.length; ++i) {
2593 Clay_LayoutElement* aspectElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->aspectRatioElementIndexes, i));
2594 Clay_AspectRatioElementConfig *config = Clay__FindElementConfigWithType(aspectElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
2595 aspectElement->dimensions.height = (1 / config->aspectRatio) * aspectElement->dimensions.width;
2596 aspectElement->layoutConfig->sizing.height.size.minMax.max = aspectElement->dimensions.height;
2597 }
2598
2599 // Propagate effect of text wrapping, aspect scaling etc. on height of parents
2600 Clay__LayoutElementTreeNodeArray dfsBuffer = context->layoutElementTreeNodeArray1;
2601 dfsBuffer.length = 0;
2602 for (int32_t i = 0; i < context->layoutElementTreeRoots.length; ++i) {
2603 Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i);
2604 context->treeNodeVisited.internalArray[dfsBuffer.length] = false;
2605 Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex) });
2606 }
2607 while (dfsBuffer.length > 0) {
2608 Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);
2609 Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement;
2610 if (!context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
2611 context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
2612 // If the element has no children or is the container for a text element, don't bother inspecting it
2613 if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->childrenOrTextContent.children.length == 0) {
2614 dfsBuffer.length--;
2615 continue;
2616 }
2617 // Add the children to the DFS buffer (needs to be pushed in reverse so that stack traversal is in correct layout order)
2618 for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; i++) {
2619 context->treeNodeVisited.internalArray[dfsBuffer.length] = false;
2620 Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]) });
2621 }
2622 continue;
2623 }
2624 dfsBuffer.length--;
2625
2626 // DFS node has been visited, this is on the way back up to the root
2627 Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig;
2628 if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
2629 // Resize any parent containers that have grown in height along their non layout axis
2630 for (int32_t j = 0; j < currentElement->childrenOrTextContent.children.length; ++j) {
2631 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[j]);
2632 float childHeightWithPadding = CLAY__MAX(childElement->dimensions.height + layoutConfig->padding.top + layoutConfig->padding.bottom, currentElement->dimensions.height);
2633 currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(childHeightWithPadding, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);
2634 }
2635 } else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
2636 // Resizing along the layout axis
2637 float contentHeight = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom);
2638 for (int32_t j = 0; j < currentElement->childrenOrTextContent.children.length; ++j) {
2639 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[j]);
2640 contentHeight += childElement->dimensions.height;
2641 }
2642 contentHeight += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
2643 currentElement->dimensions.height = CLAY__MIN(CLAY__MAX(contentHeight, layoutConfig->sizing.height.size.minMax.min), layoutConfig->sizing.height.size.minMax.max);
2644 }
2645 }
2646
2647 // Calculate sizing along the Y axis
2648 Clay__SizeContainersAlongAxis(false);
2649
2650 // Scale horizontal widths according to aspect ratio
2651 for (int32_t i = 0; i < context->aspectRatioElementIndexes.length; ++i) {
2652 Clay_LayoutElement* aspectElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->aspectRatioElementIndexes, i));
2653 Clay_AspectRatioElementConfig *config = Clay__FindElementConfigWithType(aspectElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
2654 aspectElement->dimensions.width = config->aspectRatio * aspectElement->dimensions.height;
2655 }
2656
2657 // Sort tree roots by z-index
2658 int32_t sortMax = context->layoutElementTreeRoots.length - 1;
2659 while (sortMax > 0) { // todo dumb bubble sort
2660 for (int32_t i = 0; i < sortMax; ++i) {
2661 Clay__LayoutElementTreeRoot current = *Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i);
2662 Clay__LayoutElementTreeRoot next = *Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, i + 1);
2663 if (next.zIndex < current.zIndex) {
2664 Clay__LayoutElementTreeRootArray_Set(&context->layoutElementTreeRoots, i, next);
2665 Clay__LayoutElementTreeRootArray_Set(&context->layoutElementTreeRoots, i + 1, current);
2666 }
2667 }
2668 sortMax--;
2669 }
2670
2671 // Calculate final positions and generate render commands
2672 context->renderCommands.length = 0;
2673 dfsBuffer.length = 0;
2674 for (int32_t rootIndex = 0; rootIndex < context->layoutElementTreeRoots.length; ++rootIndex) {
2675 dfsBuffer.length = 0;
2676 Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);
2677 Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)root->layoutElementIndex);
2678 Clay_Vector2 rootPosition = CLAY__DEFAULT_STRUCT;
2679 Clay_LayoutElementHashMapItem *parentHashMapItem = Clay__GetHashMapItem(root->parentId);
2680 // Position root floating containers
2681 if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING) && parentHashMapItem) {
2682 Clay_FloatingElementConfig *config = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig;
2683 Clay_Dimensions rootDimensions = rootElement->dimensions;
2684 Clay_BoundingBox parentBoundingBox = parentHashMapItem->boundingBox;
2685 // Set X position
2686 Clay_Vector2 targetAttachPosition = CLAY__DEFAULT_STRUCT;
2687 switch (config->attachPoints.parent) {
2688 case CLAY_ATTACH_POINT_LEFT_TOP:
2689 case CLAY_ATTACH_POINT_LEFT_CENTER:
2690 case CLAY_ATTACH_POINT_LEFT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x; break;
2691 case CLAY_ATTACH_POINT_CENTER_TOP:
2692 case CLAY_ATTACH_POINT_CENTER_CENTER:
2693 case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + (parentBoundingBox.width / 2); break;
2694 case CLAY_ATTACH_POINT_RIGHT_TOP:
2695 case CLAY_ATTACH_POINT_RIGHT_CENTER:
2696 case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x = parentBoundingBox.x + parentBoundingBox.width; break;
2697 }
2698 switch (config->attachPoints.element) {
2699 case CLAY_ATTACH_POINT_LEFT_TOP:
2700 case CLAY_ATTACH_POINT_LEFT_CENTER:
2701 case CLAY_ATTACH_POINT_LEFT_BOTTOM: break;
2702 case CLAY_ATTACH_POINT_CENTER_TOP:
2703 case CLAY_ATTACH_POINT_CENTER_CENTER:
2704 case CLAY_ATTACH_POINT_CENTER_BOTTOM: targetAttachPosition.x -= (rootDimensions.width / 2); break;
2705 case CLAY_ATTACH_POINT_RIGHT_TOP:
2706 case CLAY_ATTACH_POINT_RIGHT_CENTER:
2707 case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.x -= rootDimensions.width; break;
2708 }
2709 switch (config->attachPoints.parent) { // I know I could merge the x and y switch statements, but this is easier to read
2710 case CLAY_ATTACH_POINT_LEFT_TOP:
2711 case CLAY_ATTACH_POINT_RIGHT_TOP:
2712 case CLAY_ATTACH_POINT_CENTER_TOP: targetAttachPosition.y = parentBoundingBox.y; break;
2713 case CLAY_ATTACH_POINT_LEFT_CENTER:
2714 case CLAY_ATTACH_POINT_CENTER_CENTER:
2715 case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y = parentBoundingBox.y + (parentBoundingBox.height / 2); break;
2716 case CLAY_ATTACH_POINT_LEFT_BOTTOM:
2717 case CLAY_ATTACH_POINT_CENTER_BOTTOM:
2718 case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y = parentBoundingBox.y + parentBoundingBox.height; break;
2719 }
2720 switch (config->attachPoints.element) {
2721 case CLAY_ATTACH_POINT_LEFT_TOP:
2722 case CLAY_ATTACH_POINT_RIGHT_TOP:
2723 case CLAY_ATTACH_POINT_CENTER_TOP: break;
2724 case CLAY_ATTACH_POINT_LEFT_CENTER:
2725 case CLAY_ATTACH_POINT_CENTER_CENTER:
2726 case CLAY_ATTACH_POINT_RIGHT_CENTER: targetAttachPosition.y -= (rootDimensions.height / 2); break;
2727 case CLAY_ATTACH_POINT_LEFT_BOTTOM:
2728 case CLAY_ATTACH_POINT_CENTER_BOTTOM:
2729 case CLAY_ATTACH_POINT_RIGHT_BOTTOM: targetAttachPosition.y -= rootDimensions.height; break;
2730 }
2731 targetAttachPosition.x += config->offset.x;
2732 targetAttachPosition.y += config->offset.y;
2733 rootPosition = targetAttachPosition;
2734 }
2735 if (root->clipElementId) {
2736 Clay_LayoutElementHashMapItem *clipHashMapItem = Clay__GetHashMapItem(root->clipElementId);
2737 if (clipHashMapItem) {
2738 // Floating elements that are attached to scrolling contents won't be correctly positioned if external scroll handling is enabled, fix here
2739 if (context->externalScrollHandlingEnabled) {
2740 Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(clipHashMapItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
2741 if (clipConfig->horizontal) {
2742 rootPosition.x += clipConfig->childOffset.x;
2743 }
2744 if (clipConfig->vertical) {
2745 rootPosition.y += clipConfig->childOffset.y;
2746 }
2747 }
2748 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
2749 .boundingBox = clipHashMapItem->boundingBox,
2750 .userData = 0,
2751 .id = Clay__HashNumber(rootElement->id, rootElement->childrenOrTextContent.children.length + 10).id, // TODO need a better strategy for managing derived ids
2752 .zIndex = root->zIndex,
2753 .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,
2754 });
2755 }
2756 }
2757 Clay__LayoutElementTreeNodeArray_Add(&dfsBuffer, CLAY__INIT(Clay__LayoutElementTreeNode) { .layoutElement = rootElement, .position = rootPosition, .nextChildOffset = { .x = (float)rootElement->layoutConfig->padding.left, .y = (float)rootElement->layoutConfig->padding.top } });
2758
2759 context->treeNodeVisited.internalArray[0] = false;
2760 while (dfsBuffer.length > 0) {
2761 Clay__LayoutElementTreeNode *currentElementTreeNode = Clay__LayoutElementTreeNodeArray_Get(&dfsBuffer, (int)dfsBuffer.length - 1);
2762 Clay_LayoutElement *currentElement = currentElementTreeNode->layoutElement;
2763 Clay_LayoutConfig *layoutConfig = currentElement->layoutConfig;
2764 Clay_Vector2 scrollOffset = CLAY__DEFAULT_STRUCT;
2765
2766 // This will only be run a single time for each element in downwards DFS order
2767 if (!context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
2768 context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
2769
2770 Clay_BoundingBox currentElementBoundingBox = { currentElementTreeNode->position.x, currentElementTreeNode->position.y, currentElement->dimensions.width, currentElement->dimensions.height };
2771 if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING)) {
2772 Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig;
2773 Clay_Dimensions expand = floatingElementConfig->expand;
2774 currentElementBoundingBox.x -= expand.width;
2775 currentElementBoundingBox.width += expand.width * 2;
2776 currentElementBoundingBox.y -= expand.height;
2777 currentElementBoundingBox.height += expand.height * 2;
2778 }
2779
2780 Clay__ScrollContainerDataInternal *scrollContainerData = CLAY__NULL;
2781 // Apply scroll offsets to container
2782 if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP)) {
2783 Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
2784
2785 // 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
2786 for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {
2787 Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
2788 if (mapping->layoutElement == currentElement) {
2789 scrollContainerData = mapping;
2790 mapping->boundingBox = currentElementBoundingBox;
2791 scrollOffset = clipConfig->childOffset;
2792 if (context->externalScrollHandlingEnabled) {
2793 scrollOffset = CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;
2794 }
2795 break;
2796 }
2797 }
2798 }
2799
2800 Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(currentElement->id);
2801 if (hashMapItem) {
2802 hashMapItem->boundingBox = currentElementBoundingBox;
2803 }
2804
2805 int32_t sortedConfigIndexes[20];
2806 for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) {
2807 sortedConfigIndexes[elementConfigIndex] = elementConfigIndex;
2808 }
2809 sortMax = currentElement->elementConfigs.length - 1;
2810 while (sortMax > 0) { // todo dumb bubble sort
2811 for (int32_t i = 0; i < sortMax; ++i) {
2812 int32_t current = sortedConfigIndexes[i];
2813 int32_t next = sortedConfigIndexes[i + 1];
2814 Clay__ElementConfigType currentType = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, current)->type;
2815 Clay__ElementConfigType nextType = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, next)->type;
2816 if (nextType == CLAY__ELEMENT_CONFIG_TYPE_CLIP || currentType == CLAY__ELEMENT_CONFIG_TYPE_BORDER) {
2817 sortedConfigIndexes[i] = next;
2818 sortedConfigIndexes[i + 1] = current;
2819 }
2820 }
2821 sortMax--;
2822 }
2823
2824 bool emitRectangle = false;
2825 // Create the render commands for this element
2826 Clay_SharedElementConfig *sharedConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SHARED).sharedElementConfig;
2827 if (sharedConfig && sharedConfig->backgroundColor.a > 0) {
2828 emitRectangle = true;
2829 }
2830 else if (!sharedConfig) {
2831 emitRectangle = false;
2832 sharedConfig = &Clay_SharedElementConfig_DEFAULT;
2833 }
2834 for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) {
2835 Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, sortedConfigIndexes[elementConfigIndex]);
2836 Clay_RenderCommand renderCommand = {
2837 .boundingBox = currentElementBoundingBox,
2838 .userData = sharedConfig->userData,
2839 .id = currentElement->id,
2840 };
2841
2842 bool offscreen = Clay__ElementIsOffscreen(&currentElementBoundingBox);
2843 // Culling - Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow
2844 bool shouldRender = !offscreen;
2845 switch (elementConfig->type) {
2846 case CLAY__ELEMENT_CONFIG_TYPE_ASPECT:
2847 case CLAY__ELEMENT_CONFIG_TYPE_FLOATING:
2848 case CLAY__ELEMENT_CONFIG_TYPE_SHARED:
2849 case CLAY__ELEMENT_CONFIG_TYPE_BORDER: {
2850 shouldRender = false;
2851 break;
2852 }
2853 case CLAY__ELEMENT_CONFIG_TYPE_CLIP: {
2854 renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START;
2855 renderCommand.renderData = CLAY__INIT(Clay_RenderData) {
2856 .clip = {
2857 .horizontal = elementConfig->config.clipElementConfig->horizontal,
2858 .vertical = elementConfig->config.clipElementConfig->vertical,
2859 }
2860 };
2861 break;
2862 }
2863 case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: {
2864 renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE;
2865 renderCommand.renderData = CLAY__INIT(Clay_RenderData) {
2866 .image = {
2867 .backgroundColor = sharedConfig->backgroundColor,
2868 .cornerRadius = sharedConfig->cornerRadius,
2869 .imageData = elementConfig->config.imageElementConfig->imageData,
2870 }
2871 };
2872 emitRectangle = false;
2873 break;
2874 }
2875 case CLAY__ELEMENT_CONFIG_TYPE_TEXT: {
2876 if (!shouldRender) {
2877 break;
2878 }
2879 shouldRender = false;
2880 Clay_ElementConfigUnion configUnion = elementConfig->config;
2881 Clay_TextElementConfig *textElementConfig = configUnion.textElementConfig;
2882 float naturalLineHeight = currentElement->childrenOrTextContent.textElementData->preferredDimensions.height;
2883 float finalLineHeight = textElementConfig->lineHeight > 0 ? (float)textElementConfig->lineHeight : naturalLineHeight;
2884 float lineHeightOffset = (finalLineHeight - naturalLineHeight) / 2;
2885 float yPosition = lineHeightOffset;
2886 for (int32_t lineIndex = 0; lineIndex < currentElement->childrenOrTextContent.textElementData->wrappedLines.length; ++lineIndex) {
2887 Clay__WrappedTextLine *wrappedLine = Clay__WrappedTextLineArraySlice_Get(&currentElement->childrenOrTextContent.textElementData->wrappedLines, lineIndex);
2888 if (wrappedLine->line.length == 0) {
2889 yPosition += finalLineHeight;
2890 continue;
2891 }
2892 float offset = (currentElementBoundingBox.width - wrappedLine->dimensions.width);
2893 if (textElementConfig->textAlignment == CLAY_TEXT_ALIGN_LEFT) {
2894 offset = 0;
2895 }
2896 if (textElementConfig->textAlignment == CLAY_TEXT_ALIGN_CENTER) {
2897 offset /= 2;
2898 }
2899 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
2900 .boundingBox = { currentElementBoundingBox.x + offset, currentElementBoundingBox.y + yPosition, wrappedLine->dimensions.width, wrappedLine->dimensions.height },
2901 .renderData = { .text = {
2902 .stringContents = CLAY__INIT(Clay_StringSlice) { .length = wrappedLine->line.length, .chars = wrappedLine->line.chars, .baseChars = currentElement->childrenOrTextContent.textElementData->text.chars },
2903 .textColor = textElementConfig->textColor,
2904 .fontId = textElementConfig->fontId,
2905 .fontSize = textElementConfig->fontSize,
2906 .letterSpacing = textElementConfig->letterSpacing,
2907 .lineHeight = textElementConfig->lineHeight,
2908 }},
2909 .userData = textElementConfig->userData,
2910 .id = Clay__HashNumber(lineIndex, currentElement->id).id,
2911 .zIndex = root->zIndex,
2912 .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT,
2913 });
2914 yPosition += finalLineHeight;
2915
2916 if (!context->disableCulling && (currentElementBoundingBox.y + yPosition > context->layoutDimensions.height)) {
2917 break;
2918 }
2919 }
2920 break;
2921 }
2922 case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: {
2923 renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM;
2924 renderCommand.renderData = CLAY__INIT(Clay_RenderData) {
2925 .custom = {
2926 .backgroundColor = sharedConfig->backgroundColor,
2927 .cornerRadius = sharedConfig->cornerRadius,
2928 .customData = elementConfig->config.customElementConfig->customData,
2929 }
2930 };
2931 emitRectangle = false;
2932 break;
2933 }
2934 default: break;
2935 }
2936 if (shouldRender) {
2937 Clay__AddRenderCommand(renderCommand);
2938 }
2939 if (offscreen) {
2940 // 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?
2941 // Unfortunately, a FLOATING_CONTAINER may be defined that attaches to a child or grandchild of this element, which is large enough to still
2942 // 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)
2943 }
2944 }
2945
2946 if (emitRectangle) {
2947 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
2948 .boundingBox = currentElementBoundingBox,
2949 .renderData = { .rectangle = {
2950 .backgroundColor = sharedConfig->backgroundColor,
2951 .cornerRadius = sharedConfig->cornerRadius,
2952 }},
2953 .userData = sharedConfig->userData,
2954 .id = currentElement->id,
2955 .zIndex = root->zIndex,
2956 .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
2957 });
2958 }
2959
2960 // Setup initial on-axis alignment
2961 if (!Clay__ElementHasConfig(currentElementTreeNode->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {
2962 Clay_Dimensions contentSize = {0,0};
2963 if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
2964 for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
2965 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
2966 contentSize.width += childElement->dimensions.width;
2967 contentSize.height = CLAY__MAX(contentSize.height, childElement->dimensions.height);
2968 }
2969 contentSize.width += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
2970 float extraSpace = currentElement->dimensions.width - (float)(layoutConfig->padding.left + layoutConfig->padding.right) - contentSize.width;
2971 switch (layoutConfig->childAlignment.x) {
2972 case CLAY_ALIGN_X_LEFT: extraSpace = 0; break;
2973 case CLAY_ALIGN_X_CENTER: extraSpace /= 2; break;
2974 default: break;
2975 }
2976 currentElementTreeNode->nextChildOffset.x += extraSpace;
2977 extraSpace = CLAY__MAX(0, extraSpace);
2978 } else {
2979 for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
2980 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
2981 contentSize.width = CLAY__MAX(contentSize.width, childElement->dimensions.width);
2982 contentSize.height += childElement->dimensions.height;
2983 }
2984 contentSize.height += (float)(CLAY__MAX(currentElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
2985 float extraSpace = currentElement->dimensions.height - (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) - contentSize.height;
2986 switch (layoutConfig->childAlignment.y) {
2987 case CLAY_ALIGN_Y_TOP: extraSpace = 0; break;
2988 case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break;
2989 default: break;
2990 }
2991 extraSpace = CLAY__MAX(0, extraSpace);
2992 currentElementTreeNode->nextChildOffset.y += extraSpace;
2993 }
2994
2995 if (scrollContainerData) {
2996 scrollContainerData->contentSize = CLAY__INIT(Clay_Dimensions) { contentSize.width + (float)(layoutConfig->padding.left + layoutConfig->padding.right), contentSize.height + (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) };
2997 }
2998 }
2999 }
3000 else {
3001 // DFS is returning upwards backwards
3002 bool closeClipElement = false;
3003 Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
3004 if (clipConfig) {
3005 closeClipElement = true;
3006 for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {
3007 Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
3008 if (mapping->layoutElement == currentElement) {
3009 scrollOffset = clipConfig->childOffset;
3010 if (context->externalScrollHandlingEnabled) {
3011 scrollOffset = CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;
3012 }
3013 break;
3014 }
3015 }
3016 }
3017
3018 if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER)) {
3019 Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id);
3020 Clay_BoundingBox currentElementBoundingBox = currentElementData->boundingBox;
3021
3022 // Culling - Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow
3023 if (!Clay__ElementIsOffscreen(&currentElementBoundingBox)) {
3024 Clay_SharedElementConfig *sharedConfig = Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SHARED) ? Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_SHARED).sharedElementConfig : &Clay_SharedElementConfig_DEFAULT;
3025 Clay_BorderElementConfig *borderConfig = Clay__FindElementConfigWithType(currentElement, CLAY__ELEMENT_CONFIG_TYPE_BORDER).borderElementConfig;
3026 Clay_RenderCommand renderCommand = {
3027 .boundingBox = currentElementBoundingBox,
3028 .renderData = { .border = {
3029 .color = borderConfig->color,
3030 .cornerRadius = sharedConfig->cornerRadius,
3031 .width = borderConfig->width
3032 }},
3033 .userData = sharedConfig->userData,
3034 .id = Clay__HashNumber(currentElement->id, currentElement->childrenOrTextContent.children.length).id,
3035 .commandType = CLAY_RENDER_COMMAND_TYPE_BORDER,
3036 };
3037 Clay__AddRenderCommand(renderCommand);
3038 if (borderConfig->width.betweenChildren > 0 && borderConfig->color.a > 0) {
3039 float halfGap = layoutConfig->childGap / 2;
3040 Clay_Vector2 borderOffset = { (float)layoutConfig->padding.left - halfGap, (float)layoutConfig->padding.top - halfGap };
3041 if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
3042 for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
3043 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
3044 if (i > 0) {
3045 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
3046 .boundingBox = { currentElementBoundingBox.x + borderOffset.x + scrollOffset.x, currentElementBoundingBox.y + scrollOffset.y, (float)borderConfig->width.betweenChildren, currentElement->dimensions.height },
3047 .renderData = { .rectangle = {
3048 .backgroundColor = borderConfig->color,
3049 } },
3050 .userData = sharedConfig->userData,
3051 .id = Clay__HashNumber(currentElement->id, currentElement->childrenOrTextContent.children.length + 1 + i).id,
3052 .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
3053 });
3054 }
3055 borderOffset.x += (childElement->dimensions.width + (float)layoutConfig->childGap);
3056 }
3057 } else {
3058 for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
3059 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
3060 if (i > 0) {
3061 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
3062 .boundingBox = { currentElementBoundingBox.x + scrollOffset.x, currentElementBoundingBox.y + borderOffset.y + scrollOffset.y, currentElement->dimensions.width, (float)borderConfig->width.betweenChildren },
3063 .renderData = { .rectangle = {
3064 .backgroundColor = borderConfig->color,
3065 } },
3066 .userData = sharedConfig->userData,
3067 .id = Clay__HashNumber(currentElement->id, currentElement->childrenOrTextContent.children.length + 1 + i).id,
3068 .commandType = CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
3069 });
3070 }
3071 borderOffset.y += (childElement->dimensions.height + (float)layoutConfig->childGap);
3072 }
3073 }
3074 }
3075 }
3076 }
3077 // This exists because the scissor needs to end _after_ borders between elements
3078 if (closeClipElement) {
3079 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
3080 .id = Clay__HashNumber(currentElement->id, rootElement->childrenOrTextContent.children.length + 11).id,
3081 .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,
3082 });
3083 }
3084
3085 dfsBuffer.length--;
3086 continue;
3087 }
3088
3089 // Add children to the DFS buffer
3090 if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {
3091 dfsBuffer.length += currentElement->childrenOrTextContent.children.length;
3092 for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
3093 Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
3094 // Alignment along non layout axis
3095 if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
3096 currentElementTreeNode->nextChildOffset.y = currentElement->layoutConfig->padding.top;
3097 float whiteSpaceAroundChild = currentElement->dimensions.height - (float)(layoutConfig->padding.top + layoutConfig->padding.bottom) - childElement->dimensions.height;
3098 switch (layoutConfig->childAlignment.y) {
3099 case CLAY_ALIGN_Y_TOP: break;
3100 case CLAY_ALIGN_Y_CENTER: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild / 2; break;
3101 case CLAY_ALIGN_Y_BOTTOM: currentElementTreeNode->nextChildOffset.y += whiteSpaceAroundChild; break;
3102 }
3103 } else {
3104 currentElementTreeNode->nextChildOffset.x = currentElement->layoutConfig->padding.left;
3105 float whiteSpaceAroundChild = currentElement->dimensions.width - (float)(layoutConfig->padding.left + layoutConfig->padding.right) - childElement->dimensions.width;
3106 switch (layoutConfig->childAlignment.x) {
3107 case CLAY_ALIGN_X_LEFT: break;
3108 case CLAY_ALIGN_X_CENTER: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild / 2; break;
3109 case CLAY_ALIGN_X_RIGHT: currentElementTreeNode->nextChildOffset.x += whiteSpaceAroundChild; break;
3110 }
3111 }
3112
3113 Clay_Vector2 childPosition = {
3114 currentElementTreeNode->position.x + currentElementTreeNode->nextChildOffset.x + scrollOffset.x,
3115 currentElementTreeNode->position.y + currentElementTreeNode->nextChildOffset.y + scrollOffset.y,
3116 };
3117
3118 // DFS buffer elements need to be added in reverse because stack traversal happens backwards
3119 uint32_t newNodeIndex = dfsBuffer.length - 1 - i;
3120 dfsBuffer.internalArray[newNodeIndex] = CLAY__INIT(Clay__LayoutElementTreeNode) {
3121 .layoutElement = childElement,
3122 .position = { childPosition.x, childPosition.y },
3123 .nextChildOffset = { .x = (float)childElement->layoutConfig->padding.left, .y = (float)childElement->layoutConfig->padding.top },
3124 };
3125 context->treeNodeVisited.internalArray[newNodeIndex] = false;
3126
3127 // Update parent offsets
3128 if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
3129 currentElementTreeNode->nextChildOffset.x += childElement->dimensions.width + (float)layoutConfig->childGap;
3130 } else {
3131 currentElementTreeNode->nextChildOffset.y += childElement->dimensions.height + (float)layoutConfig->childGap;
3132 }
3133 }
3134 }
3135 }
3136
3137 if (root->clipElementId) {
3138 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { .id = Clay__HashNumber(rootElement->id, rootElement->childrenOrTextContent.children.length + 11).id, .commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END });
3139 }
3140 }
3141}
3142
3143CLAY_WASM_EXPORT("Clay_GetPointerOverIds")
3144CLAY_DLL_EXPORT Clay_ElementIdArray Clay_GetPointerOverIds(void) {
3145 return Clay_GetCurrentContext()->pointerOverIds;
3146}
3147
3148#pragma region DebugTools
3149Clay_Color CLAY__DEBUGVIEW_COLOR_1 = {58, 56, 52, 255};
3150Clay_Color CLAY__DEBUGVIEW_COLOR_2 = {62, 60, 58, 255};
3151Clay_Color CLAY__DEBUGVIEW_COLOR_3 = {141, 133, 135, 255};
3152Clay_Color CLAY__DEBUGVIEW_COLOR_4 = {238, 226, 231, 255};
3153Clay_Color CLAY__DEBUGVIEW_COLOR_SELECTED_ROW = {102, 80, 78, 255};
3154const int32_t CLAY__DEBUGVIEW_ROW_HEIGHT = 30;
3155const int32_t CLAY__DEBUGVIEW_OUTER_PADDING = 10;
3156const int32_t CLAY__DEBUGVIEW_INDENT_WIDTH = 16;
3157Clay_TextElementConfig Clay__DebugView_TextNameConfig = {.textColor = {238, 226, 231, 255}, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE };
3158Clay_LayoutConfig Clay__DebugView_ScrollViewItemLayoutConfig = CLAY__DEFAULT_STRUCT;
3159
3160typedef struct {
3161 Clay_String label;
3162 Clay_Color color;
3163} Clay__DebugElementConfigTypeLabelConfig;
3164
3165Clay__DebugElementConfigTypeLabelConfig Clay__DebugGetElementConfigTypeLabel(Clay__ElementConfigType type) {
3166 switch (type) {
3167 case CLAY__ELEMENT_CONFIG_TYPE_SHARED: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Shared"), {243,134,48,255} };
3168 case CLAY__ELEMENT_CONFIG_TYPE_TEXT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Text"), {105,210,231,255} };
3169 case CLAY__ELEMENT_CONFIG_TYPE_ASPECT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Aspect"), {101,149,194,255} };
3170 case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Image"), {121,189,154,255} };
3171 case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Floating"), {250,105,0,255} };
3172 case CLAY__ELEMENT_CONFIG_TYPE_CLIP: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) {CLAY_STRING("Scroll"), {242, 196, 90, 255} };
3173 case CLAY__ELEMENT_CONFIG_TYPE_BORDER: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) {CLAY_STRING("Border"), {108, 91, 123, 255} };
3174 case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Custom"), {11,72,107,255} };
3175 default: break;
3176 }
3177 return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Error"), {0,0,0,255} };
3178}
3179
3180typedef struct {
3181 int32_t rowCount;
3182 int32_t selectedElementRowIndex;
3183} Clay__RenderDebugLayoutData;
3184
3185// Returns row count
3186Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialRootsLength, int32_t highlightedRowIndex) {
3187 Clay_Context* context = Clay_GetCurrentContext();
3188 Clay__int32_tArray dfsBuffer = context->reusableElementIndexBuffer;
3189 Clay__DebugView_ScrollViewItemLayoutConfig = CLAY__INIT(Clay_LayoutConfig) { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT) }, .childGap = 6, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }};
3190 Clay__RenderDebugLayoutData layoutData = CLAY__DEFAULT_STRUCT;
3191
3192 uint32_t highlightedElementId = 0;
3193
3194 for (int32_t rootIndex = 0; rootIndex < initialRootsLength; ++rootIndex) {
3195 dfsBuffer.length = 0;
3196 Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);
3197 Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex);
3198 context->treeNodeVisited.internalArray[0] = false;
3199 if (rootIndex > 0) {
3200 CLAY(CLAY_IDI("Clay__DebugView_EmptyRowOuter", rootIndex), { .layout = { .sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2, 0, 0, 0} } }) {
3201 CLAY(CLAY_IDI("Clay__DebugView_EmptyRow", rootIndex), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }}, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .top = 1 } } }) {}
3202 }
3203 layoutData.rowCount++;
3204 }
3205 while (dfsBuffer.length > 0) {
3206 int32_t currentElementIndex = Clay__int32_tArray_GetValue(&dfsBuffer, (int)dfsBuffer.length - 1);
3207 Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, (int)currentElementIndex);
3208 if (context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
3209 if (!Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) && currentElement->childrenOrTextContent.children.length > 0) {
3210 Clay__CloseElement();
3211 Clay__CloseElement();
3212 Clay__CloseElement();
3213 }
3214 dfsBuffer.length--;
3215 continue;
3216 }
3217
3218 if (highlightedRowIndex == layoutData.rowCount) {
3219 if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
3220 context->debugSelectedElementId = currentElement->id;
3221 }
3222 highlightedElementId = currentElement->id;
3223 }
3224
3225 context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
3226 Clay_LayoutElementHashMapItem *currentElementData = Clay__GetHashMapItem(currentElement->id);
3227 bool offscreen = Clay__ElementIsOffscreen(&currentElementData->boundingBox);
3228 if (context->debugSelectedElementId == currentElement->id) {
3229 layoutData.selectedElementRowIndex = layoutData.rowCount;
3230 }
3231 CLAY(CLAY_IDI("Clay__DebugView_ElementOuter", currentElement->id), { .layout = Clay__DebugView_ScrollViewItemLayoutConfig }) {
3232 // Collapse icon / button
3233 if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->childrenOrTextContent.children.length == 0)) {
3234 CLAY(CLAY_IDI("Clay__DebugView_CollapseElement", currentElement->id), {
3235 .layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} },
3236 .cornerRadius = CLAY_CORNER_RADIUS(4),
3237 .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = {1, 1, 1, 1, 0} },
3238 }) {
3239 CLAY_TEXT((currentElementData && currentElementData->debugData->collapsed) ? CLAY_STRING("+") : CLAY_STRING("-"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
3240 }
3241 } else { // Square dot for empty containers
3242 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER } } }) {
3243 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_FIXED(8), CLAY_SIZING_FIXED(8)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3, .cornerRadius = CLAY_CORNER_RADIUS(2) }) {}
3244 }
3245 }
3246 // Collisions and offscreen info
3247 if (currentElementData) {
3248 if (currentElementData->debugData->collision) {
3249 CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 }}, .border = { .color = {177, 147, 8, 255}, .width = {1, 1, 1, 1, 0} } }) {
3250 CLAY_TEXT(CLAY_STRING("Duplicate ID"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }));
3251 }
3252 }
3253 if (offscreen) {
3254 CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { 1, 1, 1, 1, 0} } }) {
3255 CLAY_TEXT(CLAY_STRING("Offscreen"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }));
3256 }
3257 }
3258 }
3259 Clay_String idString = context->layoutElementIdStrings.internalArray[currentElementIndex];
3260 if (idString.length > 0) {
3261 CLAY_TEXT(idString, offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig);
3262 }
3263 for (int32_t elementConfigIndex = 0; elementConfigIndex < currentElement->elementConfigs.length; ++elementConfigIndex) {
3264 Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&currentElement->elementConfigs, elementConfigIndex);
3265 if (elementConfig->type == CLAY__ELEMENT_CONFIG_TYPE_SHARED) {
3266 Clay_Color labelColor = {243,134,48,90};
3267 labelColor.a = 90;
3268 Clay_Color backgroundColor = elementConfig->config.sharedElementConfig->backgroundColor;
3269 Clay_CornerRadius radius = elementConfig->config.sharedElementConfig->cornerRadius;
3270 if (backgroundColor.a > 0) {
3271 CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0} } }) {
3272 CLAY_TEXT(CLAY_STRING("Color"), CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
3273 }
3274 }
3275 if (radius.bottomLeft > 0) {
3276 CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0 } } }) {
3277 CLAY_TEXT(CLAY_STRING("Radius"), CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
3278 }
3279 }
3280 continue;
3281 }
3282 Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(elementConfig->type);
3283 Clay_Color backgroundColor = config.color;
3284 backgroundColor.a = 90;
3285 CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {
3286 CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
3287 }
3288 }
3289 }
3290
3291 // Render the text contents below the element as a non-interactive row
3292 if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {
3293 layoutData.rowCount++;
3294 Clay__TextElementData *textElementData = currentElement->childrenOrTextContent.textElementData;
3295 Clay_TextElementConfig *rawTextConfig = offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig;
3296 CLAY_AUTO_ID({ .layout = { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {
3297 CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16) } } }) {}
3298 CLAY_TEXT(CLAY_STRING("\""), rawTextConfig);
3299 CLAY_TEXT(textElementData->text.length > 40 ? (CLAY__INIT(Clay_String) { .length = 40, .chars = textElementData->text.chars }) : textElementData->text, rawTextConfig);
3300 if (textElementData->text.length > 40) {
3301 CLAY_TEXT(CLAY_STRING("..."), rawTextConfig);
3302 }
3303 CLAY_TEXT(CLAY_STRING("\""), rawTextConfig);
3304 }
3305 } else if (currentElement->childrenOrTextContent.children.length > 0) {
3306 Clay__OpenElement();
3307 Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) { .layout = { .padding = { .left = 8 } } });
3308 Clay__OpenElement();
3309 Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) { .layout = { .padding = { .left = CLAY__DEBUGVIEW_INDENT_WIDTH }}, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .left = 1 } }});
3310 Clay__OpenElement();
3311 Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } });
3312 }
3313
3314 layoutData.rowCount++;
3315 if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (currentElementData && currentElementData->debugData->collapsed))) {
3316 for (int32_t i = currentElement->childrenOrTextContent.children.length - 1; i >= 0; --i) {
3317 Clay__int32_tArray_Add(&dfsBuffer, currentElement->childrenOrTextContent.children.elements[i]);
3318 context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked
3319 }
3320 }
3321 }
3322 }
3323
3324 if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
3325 Clay_ElementId collapseButtonId = Clay__HashString(CLAY_STRING("Clay__DebugView_CollapseElement"), 0);
3326 for (int32_t i = (int)context->pointerOverIds.length - 1; i >= 0; i--) {
3327 Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);
3328 if (elementId->baseId == collapseButtonId.baseId) {
3329 Clay_LayoutElementHashMapItem *highlightedItem = Clay__GetHashMapItem(elementId->offset);
3330 highlightedItem->debugData->collapsed = !highlightedItem->debugData->collapsed;
3331 break;
3332 }
3333 }
3334 }
3335
3336 if (highlightedElementId) {
3337 CLAY(CLAY_ID("Clay__DebugView_ElementHighlight"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .parentId = highlightedElementId, .zIndex = 32767, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID } }) {
3338 CLAY(CLAY_ID("Clay__DebugView_ElementHighlightRectangle"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .backgroundColor = Clay__debugViewHighlightColor }) {}
3339 }
3340 }
3341 return layoutData;
3342}
3343
3344void Clay__RenderDebugLayoutSizing(Clay_SizingAxis sizing, Clay_TextElementConfig *infoTextConfig) {
3345 Clay_String sizingLabel = CLAY_STRING("GROW");
3346 if (sizing.type == CLAY__SIZING_TYPE_FIT) {
3347 sizingLabel = CLAY_STRING("FIT");
3348 } else if (sizing.type == CLAY__SIZING_TYPE_PERCENT) {
3349 sizingLabel = CLAY_STRING("PERCENT");
3350 } else if (sizing.type == CLAY__SIZING_TYPE_FIXED) {
3351 sizingLabel = CLAY_STRING("FIXED");
3352 }
3353 CLAY_TEXT(sizingLabel, infoTextConfig);
3354 if (sizing.type == CLAY__SIZING_TYPE_GROW || sizing.type == CLAY__SIZING_TYPE_FIT || sizing.type == CLAY__SIZING_TYPE_FIXED) {
3355 CLAY_TEXT(CLAY_STRING("("), infoTextConfig);
3356 if (sizing.size.minMax.min != 0) {
3357 CLAY_TEXT(CLAY_STRING("min: "), infoTextConfig);
3358 CLAY_TEXT(Clay__IntToString(sizing.size.minMax.min), infoTextConfig);
3359 if (sizing.size.minMax.max != CLAY__MAXFLOAT) {
3360 CLAY_TEXT(CLAY_STRING(", "), infoTextConfig);
3361 }
3362 }
3363 if (sizing.size.minMax.max != CLAY__MAXFLOAT) {
3364 CLAY_TEXT(CLAY_STRING("max: "), infoTextConfig);
3365 CLAY_TEXT(Clay__IntToString(sizing.size.minMax.max), infoTextConfig);
3366 }
3367 CLAY_TEXT(CLAY_STRING(")"), infoTextConfig);
3368 } else if (sizing.type == CLAY__SIZING_TYPE_PERCENT) {
3369 CLAY_TEXT(CLAY_STRING("("), infoTextConfig);
3370 CLAY_TEXT(Clay__IntToString(sizing.size.percent * 100), infoTextConfig);
3371 CLAY_TEXT(CLAY_STRING("%)"), infoTextConfig);
3372 }
3373}
3374
3375void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__ElementConfigType type) {
3376 Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(type);
3377 Clay_Color backgroundColor = config.color;
3378 backgroundColor.a = 90;
3379 CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(CLAY__DEBUGVIEW_OUTER_PADDING), .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {
3380 CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {
3381 CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
3382 }
3383 CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
3384 CLAY_TEXT(elementId, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }));
3385 }
3386}
3387
3388void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) {
3389 CLAY_AUTO_ID({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
3390 CLAY_TEXT(CLAY_STRING("{ r: "), textConfig);
3391 CLAY_TEXT(Clay__IntToString(color.r), textConfig);
3392 CLAY_TEXT(CLAY_STRING(", g: "), textConfig);
3393 CLAY_TEXT(Clay__IntToString(color.g), textConfig);
3394 CLAY_TEXT(CLAY_STRING(", b: "), textConfig);
3395 CLAY_TEXT(Clay__IntToString(color.b), textConfig);
3396 CLAY_TEXT(CLAY_STRING(", a: "), textConfig);
3397 CLAY_TEXT(Clay__IntToString(color.a), textConfig);
3398 CLAY_TEXT(CLAY_STRING(" }"), textConfig);
3399 CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_FIXED(10) } } }) {}
3400 CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)} }, .backgroundColor = color, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = CLAY__DEBUGVIEW_COLOR_4, .width = { 1, 1, 1, 1, 0 } } }) {}
3401 }
3402}
3403
3404void Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) {
3405 CLAY_AUTO_ID({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
3406 CLAY_TEXT(CLAY_STRING("{ topLeft: "), textConfig);
3407 CLAY_TEXT(Clay__IntToString(cornerRadius.topLeft), textConfig);
3408 CLAY_TEXT(CLAY_STRING(", topRight: "), textConfig);
3409 CLAY_TEXT(Clay__IntToString(cornerRadius.topRight), textConfig);
3410 CLAY_TEXT(CLAY_STRING(", bottomLeft: "), textConfig);
3411 CLAY_TEXT(Clay__IntToString(cornerRadius.bottomLeft), textConfig);
3412 CLAY_TEXT(CLAY_STRING(", bottomRight: "), textConfig);
3413 CLAY_TEXT(Clay__IntToString(cornerRadius.bottomRight), textConfig);
3414 CLAY_TEXT(CLAY_STRING(" }"), textConfig);
3415 }
3416}
3417
3418void HandleDebugViewCloseButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData) {
3419 Clay_Context* context = Clay_GetCurrentContext();
3420 (void) elementId; (void) pointerInfo; (void) userData;
3421 if (pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
3422 context->debugModeEnabled = false;
3423 }
3424}
3425
3426void Clay__RenderDebugView(void) {
3427 Clay_Context* context = Clay_GetCurrentContext();
3428 Clay_ElementId closeButtonId = Clay__HashString(CLAY_STRING("Clay__DebugViewTopHeaderCloseButtonOuter"), 0);
3429 if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
3430 for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {
3431 Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);
3432 if (elementId->id == closeButtonId.id) {
3433 context->debugModeEnabled = false;
3434 return;
3435 }
3436 }
3437 }
3438
3439 uint32_t initialRootsLength = context->layoutElementTreeRoots.length;
3440 uint32_t initialElementsLength = context->layoutElements.length;
3441 Clay_TextElementConfig *infoTextConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });
3442 Clay_TextElementConfig *infoTitleConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });
3443 Clay_ElementId scrollId = Clay__HashString(CLAY_STRING("Clay__DebugViewOuterScrollPane"), 0);
3444 float scrollYOffset = 0;
3445 bool pointerInDebugView = context->pointerInfo.position.y < context->layoutDimensions.height - 300;
3446 for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) {
3447 Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
3448 if (scrollContainerData->elementId == scrollId.id) {
3449 if (!context->externalScrollHandlingEnabled) {
3450 scrollYOffset = scrollContainerData->scrollPosition.y;
3451 } else {
3452 pointerInDebugView = context->pointerInfo.position.y + scrollContainerData->scrollPosition.y < context->layoutDimensions.height - 300;
3453 }
3454 break;
3455 }
3456 }
3457 int32_t highlightedRow = pointerInDebugView
3458 ? (int32_t)((context->pointerInfo.position.y - scrollYOffset) / (float)CLAY__DEBUGVIEW_ROW_HEIGHT) - 1
3459 : -1;
3460 if (context->pointerInfo.position.x < context->layoutDimensions.width - (float)Clay__debugViewWidth) {
3461 highlightedRow = -1;
3462 }
3463 Clay__RenderDebugLayoutData layoutData = CLAY__DEFAULT_STRUCT;
3464 CLAY(CLAY_ID("Clay__DebugView"), {
3465 .layout = { .sizing = { CLAY_SIZING_FIXED((float)Clay__debugViewWidth) , CLAY_SIZING_FIXED(context->layoutDimensions.height) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },
3466 .floating = { .zIndex = 32765, .attachPoints = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_CENTER }, .attachTo = CLAY_ATTACH_TO_ROOT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT },
3467 .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .bottom = 1 } }
3468 }) {
3469 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2 }) {
3470 CLAY_TEXT(CLAY_STRING("Clay Debug Tools"), infoTextConfig);
3471 CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
3472 // Close button
3473 CLAY_AUTO_ID({
3474 .layout = { .sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10)}, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} },
3475 .backgroundColor = {217,91,67,80},
3476 .cornerRadius = CLAY_CORNER_RADIUS(4),
3477 .border = { .color = { 217,91,67,255 }, .width = { 1, 1, 1, 1, 0 } },
3478 }) {
3479 Clay_OnHover(HandleDebugViewCloseButtonInteraction, 0);
3480 CLAY_TEXT(CLAY_STRING("x"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
3481 }
3482 }
3483 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 } ) {}
3484 CLAY(scrollId, { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
3485 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1 }) {
3486 Clay_ElementId panelContentsId = Clay__HashString(CLAY_STRING("Clay__DebugViewPaneOuter"), 0);
3487 // Element list
3488 CLAY(panelContentsId, { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .zIndex = 32766, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_PARENT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT } }) {
3489 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = { CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3490 layoutData = Clay__RenderDebugLayoutElementsList((int32_t)initialRootsLength, highlightedRow);
3491 }
3492 }
3493 float contentWidth = Clay__GetHashMapItem(panelContentsId.id)->layoutElement->dimensions.width;
3494 CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(contentWidth) }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {}
3495 for (int32_t i = 0; i < layoutData.rowCount; i++) {
3496 Clay_Color rowColor = (i & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1;
3497 if (i == layoutData.selectedElementRowIndex) {
3498 rowColor = CLAY__DEBUGVIEW_COLOR_SELECTED_ROW;
3499 }
3500 if (i == highlightedRow) {
3501 rowColor.r *= 1.25f;
3502 rowColor.g *= 1.25f;
3503 rowColor.b *= 1.25f;
3504 }
3505 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = rowColor } ) {}
3506 }
3507 }
3508 }
3509 CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 }) {}
3510 if (context->debugSelectedElementId != 0) {
3511 Clay_LayoutElementHashMapItem *selectedItem = Clay__GetHashMapItem(context->debugSelectedElementId);
3512 CLAY_AUTO_ID({
3513 .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .layoutDirection = CLAY_TOP_TO_BOTTOM },
3514 .backgroundColor = CLAY__DEBUGVIEW_COLOR_2 ,
3515 .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
3516 .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .betweenChildren = 1 } }
3517 }) {
3518 CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
3519 CLAY_TEXT(CLAY_STRING("Layout Config"), infoTextConfig);
3520 CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
3521 if (selectedItem->elementId.stringId.length != 0) {
3522 CLAY_TEXT(selectedItem->elementId.stringId, infoTitleConfig);
3523 if (selectedItem->elementId.offset != 0) {
3524 CLAY_TEXT(CLAY_STRING(" ("), infoTitleConfig);
3525 CLAY_TEXT(Clay__IntToString(selectedItem->elementId.offset), infoTitleConfig);
3526 CLAY_TEXT(CLAY_STRING(")"), infoTitleConfig);
3527 }
3528 }
3529 }
3530 Clay_Padding attributeConfigPadding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 8, 8};
3531 // Clay_LayoutConfig debug info
3532 CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3533 // .boundingBox
3534 CLAY_TEXT(CLAY_STRING("Bounding Box"), infoTitleConfig);
3535 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3536 CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig);
3537 CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.x), infoTextConfig);
3538 CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig);
3539 CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.y), infoTextConfig);
3540 CLAY_TEXT(CLAY_STRING(", width: "), infoTextConfig);
3541 CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.width), infoTextConfig);
3542 CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig);
3543 CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.height), infoTextConfig);
3544 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3545 }
3546 // .layoutDirection
3547 CLAY_TEXT(CLAY_STRING("Layout Direction"), infoTitleConfig);
3548 Clay_LayoutConfig *layoutConfig = selectedItem->layoutElement->layoutConfig;
3549 CLAY_TEXT(layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM ? CLAY_STRING("TOP_TO_BOTTOM") : CLAY_STRING("LEFT_TO_RIGHT"), infoTextConfig);
3550 // .sizing
3551 CLAY_TEXT(CLAY_STRING("Sizing"), infoTitleConfig);
3552 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3553 CLAY_TEXT(CLAY_STRING("width: "), infoTextConfig);
3554 Clay__RenderDebugLayoutSizing(layoutConfig->sizing.width, infoTextConfig);
3555 }
3556 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3557 CLAY_TEXT(CLAY_STRING("height: "), infoTextConfig);
3558 Clay__RenderDebugLayoutSizing(layoutConfig->sizing.height, infoTextConfig);
3559 }
3560 // .padding
3561 CLAY_TEXT(CLAY_STRING("Padding"), infoTitleConfig);
3562 CLAY(CLAY_ID("Clay__DebugViewElementInfoPadding"), { }) {
3563 CLAY_TEXT(CLAY_STRING("{ left: "), infoTextConfig);
3564 CLAY_TEXT(Clay__IntToString(layoutConfig->padding.left), infoTextConfig);
3565 CLAY_TEXT(CLAY_STRING(", right: "), infoTextConfig);
3566 CLAY_TEXT(Clay__IntToString(layoutConfig->padding.right), infoTextConfig);
3567 CLAY_TEXT(CLAY_STRING(", top: "), infoTextConfig);
3568 CLAY_TEXT(Clay__IntToString(layoutConfig->padding.top), infoTextConfig);
3569 CLAY_TEXT(CLAY_STRING(", bottom: "), infoTextConfig);
3570 CLAY_TEXT(Clay__IntToString(layoutConfig->padding.bottom), infoTextConfig);
3571 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3572 }
3573 // .childGap
3574 CLAY_TEXT(CLAY_STRING("Child Gap"), infoTitleConfig);
3575 CLAY_TEXT(Clay__IntToString(layoutConfig->childGap), infoTextConfig);
3576 // .childAlignment
3577 CLAY_TEXT(CLAY_STRING("Child Alignment"), infoTitleConfig);
3578 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3579 CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig);
3580 Clay_String alignX = CLAY_STRING("LEFT");
3581 if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_CENTER) {
3582 alignX = CLAY_STRING("CENTER");
3583 } else if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_RIGHT) {
3584 alignX = CLAY_STRING("RIGHT");
3585 }
3586 CLAY_TEXT(alignX, infoTextConfig);
3587 CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig);
3588 Clay_String alignY = CLAY_STRING("TOP");
3589 if (layoutConfig->childAlignment.y == CLAY_ALIGN_Y_CENTER) {
3590 alignY = CLAY_STRING("CENTER");
3591 } else if (layoutConfig->childAlignment.y == CLAY_ALIGN_Y_BOTTOM) {
3592 alignY = CLAY_STRING("BOTTOM");
3593 }
3594 CLAY_TEXT(alignY, infoTextConfig);
3595 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3596 }
3597 }
3598 for (int32_t elementConfigIndex = 0; elementConfigIndex < selectedItem->layoutElement->elementConfigs.length; ++elementConfigIndex) {
3599 Clay_ElementConfig *elementConfig = Clay__ElementConfigArraySlice_Get(&selectedItem->layoutElement->elementConfigs, elementConfigIndex);
3600 Clay__RenderDebugViewElementConfigHeader(selectedItem->elementId.stringId, elementConfig->type);
3601 switch (elementConfig->type) {
3602 case CLAY__ELEMENT_CONFIG_TYPE_SHARED: {
3603 Clay_SharedElementConfig *sharedConfig = elementConfig->config.sharedElementConfig;
3604 CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
3605 // .backgroundColor
3606 CLAY_TEXT(CLAY_STRING("Background Color"), infoTitleConfig);
3607 Clay__RenderDebugViewColor(sharedConfig->backgroundColor, infoTextConfig);
3608 // .cornerRadius
3609 CLAY_TEXT(CLAY_STRING("Corner Radius"), infoTitleConfig);
3610 Clay__RenderDebugViewCornerRadius(sharedConfig->cornerRadius, infoTextConfig);
3611 }
3612 break;
3613 }
3614 case CLAY__ELEMENT_CONFIG_TYPE_TEXT: {
3615 Clay_TextElementConfig *textConfig = elementConfig->config.textElementConfig;
3616 CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3617 // .fontSize
3618 CLAY_TEXT(CLAY_STRING("Font Size"), infoTitleConfig);
3619 CLAY_TEXT(Clay__IntToString(textConfig->fontSize), infoTextConfig);
3620 // .fontId
3621 CLAY_TEXT(CLAY_STRING("Font ID"), infoTitleConfig);
3622 CLAY_TEXT(Clay__IntToString(textConfig->fontId), infoTextConfig);
3623 // .lineHeight
3624 CLAY_TEXT(CLAY_STRING("Line Height"), infoTitleConfig);
3625 CLAY_TEXT(textConfig->lineHeight == 0 ? CLAY_STRING("auto") : Clay__IntToString(textConfig->lineHeight), infoTextConfig);
3626 // .letterSpacing
3627 CLAY_TEXT(CLAY_STRING("Letter Spacing"), infoTitleConfig);
3628 CLAY_TEXT(Clay__IntToString(textConfig->letterSpacing), infoTextConfig);
3629 // .wrapMode
3630 CLAY_TEXT(CLAY_STRING("Wrap Mode"), infoTitleConfig);
3631 Clay_String wrapMode = CLAY_STRING("WORDS");
3632 if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE) {
3633 wrapMode = CLAY_STRING("NONE");
3634 } else if (textConfig->wrapMode == CLAY_TEXT_WRAP_NEWLINES) {
3635 wrapMode = CLAY_STRING("NEWLINES");
3636 }
3637 CLAY_TEXT(wrapMode, infoTextConfig);
3638 // .textAlignment
3639 CLAY_TEXT(CLAY_STRING("Text Alignment"), infoTitleConfig);
3640 Clay_String textAlignment = CLAY_STRING("LEFT");
3641 if (textConfig->textAlignment == CLAY_TEXT_ALIGN_CENTER) {
3642 textAlignment = CLAY_STRING("CENTER");
3643 } else if (textConfig->textAlignment == CLAY_TEXT_ALIGN_RIGHT) {
3644 textAlignment = CLAY_STRING("RIGHT");
3645 }
3646 CLAY_TEXT(textAlignment, infoTextConfig);
3647 // .textColor
3648 CLAY_TEXT(CLAY_STRING("Text Color"), infoTitleConfig);
3649 Clay__RenderDebugViewColor(textConfig->textColor, infoTextConfig);
3650 }
3651 break;
3652 }
3653 case CLAY__ELEMENT_CONFIG_TYPE_ASPECT: {
3654 Clay_AspectRatioElementConfig *aspectRatioConfig = elementConfig->config.aspectRatioElementConfig;
3655 CLAY(CLAY_ID("Clay__DebugViewElementInfoAspectRatioBody"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3656 CLAY_TEXT(CLAY_STRING("Aspect Ratio"), infoTitleConfig);
3657 // Aspect Ratio
3658 CLAY(CLAY_ID("Clay__DebugViewElementInfoAspectRatio"), { }) {
3659 CLAY_TEXT(Clay__IntToString(aspectRatioConfig->aspectRatio), infoTextConfig);
3660 CLAY_TEXT(CLAY_STRING("."), infoTextConfig);
3661 float frac = aspectRatioConfig->aspectRatio - (int)(aspectRatioConfig->aspectRatio);
3662 frac *= 100;
3663 if ((int)frac < 10) {
3664 CLAY_TEXT(CLAY_STRING("0"), infoTextConfig);
3665 }
3666 CLAY_TEXT(Clay__IntToString(frac), infoTextConfig);
3667 }
3668 }
3669 break;
3670 }
3671 case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: {
3672 Clay_ImageElementConfig *imageConfig = elementConfig->config.imageElementConfig;
3673 Clay_AspectRatioElementConfig aspectConfig = { 1 };
3674 if (Clay__ElementHasConfig(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT)) {
3675 aspectConfig = *Clay__FindElementConfigWithType(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
3676 }
3677 CLAY(CLAY_ID("Clay__DebugViewElementInfoImageBody"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3678 // Image Preview
3679 CLAY_TEXT(CLAY_STRING("Preview"), infoTitleConfig);
3680 CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(64, 128), .height = CLAY_SIZING_GROW(64, 128) }}, .aspectRatio = aspectConfig, .image = *imageConfig }) {}
3681 }
3682 break;
3683 }
3684 case CLAY__ELEMENT_CONFIG_TYPE_CLIP: {
3685 Clay_ClipElementConfig *clipConfig = elementConfig->config.clipElementConfig;
3686 CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3687 // .vertical
3688 CLAY_TEXT(CLAY_STRING("Vertical"), infoTitleConfig);
3689 CLAY_TEXT(clipConfig->vertical ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig);
3690 // .horizontal
3691 CLAY_TEXT(CLAY_STRING("Horizontal"), infoTitleConfig);
3692 CLAY_TEXT(clipConfig->horizontal ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig);
3693 }
3694 break;
3695 }
3696 case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: {
3697 Clay_FloatingElementConfig *floatingConfig = elementConfig->config.floatingElementConfig;
3698 CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3699 // .offset
3700 CLAY_TEXT(CLAY_STRING("Offset"), infoTitleConfig);
3701 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3702 CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig);
3703 CLAY_TEXT(Clay__IntToString(floatingConfig->offset.x), infoTextConfig);
3704 CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig);
3705 CLAY_TEXT(Clay__IntToString(floatingConfig->offset.y), infoTextConfig);
3706 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3707 }
3708 // .expand
3709 CLAY_TEXT(CLAY_STRING("Expand"), infoTitleConfig);
3710 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3711 CLAY_TEXT(CLAY_STRING("{ width: "), infoTextConfig);
3712 CLAY_TEXT(Clay__IntToString(floatingConfig->expand.width), infoTextConfig);
3713 CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig);
3714 CLAY_TEXT(Clay__IntToString(floatingConfig->expand.height), infoTextConfig);
3715 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3716 }
3717 // .zIndex
3718 CLAY_TEXT(CLAY_STRING("z-index"), infoTitleConfig);
3719 CLAY_TEXT(Clay__IntToString(floatingConfig->zIndex), infoTextConfig);
3720 // .parentId
3721 CLAY_TEXT(CLAY_STRING("Parent"), infoTitleConfig);
3722 Clay_LayoutElementHashMapItem *hashItem = Clay__GetHashMapItem(floatingConfig->parentId);
3723 CLAY_TEXT(hashItem->elementId.stringId, infoTextConfig);
3724 // .attachPoints
3725 CLAY_TEXT(CLAY_STRING("Attach Points"), infoTitleConfig);
3726 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3727 CLAY_TEXT(CLAY_STRING("{ element: "), infoTextConfig);
3728 Clay_String attachPointElement = CLAY_STRING("LEFT_TOP");
3729 if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_LEFT_CENTER) {
3730 attachPointElement = CLAY_STRING("LEFT_CENTER");
3731 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_LEFT_BOTTOM) {
3732 attachPointElement = CLAY_STRING("LEFT_BOTTOM");
3733 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_CENTER_TOP) {
3734 attachPointElement = CLAY_STRING("CENTER_TOP");
3735 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_CENTER_CENTER) {
3736 attachPointElement = CLAY_STRING("CENTER_CENTER");
3737 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_CENTER_BOTTOM) {
3738 attachPointElement = CLAY_STRING("CENTER_BOTTOM");
3739 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_RIGHT_TOP) {
3740 attachPointElement = CLAY_STRING("RIGHT_TOP");
3741 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_RIGHT_CENTER) {
3742 attachPointElement = CLAY_STRING("RIGHT_CENTER");
3743 } else if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_RIGHT_BOTTOM) {
3744 attachPointElement = CLAY_STRING("RIGHT_BOTTOM");
3745 }
3746 CLAY_TEXT(attachPointElement, infoTextConfig);
3747 Clay_String attachPointParent = CLAY_STRING("LEFT_TOP");
3748 if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_LEFT_CENTER) {
3749 attachPointParent = CLAY_STRING("LEFT_CENTER");
3750 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_LEFT_BOTTOM) {
3751 attachPointParent = CLAY_STRING("LEFT_BOTTOM");
3752 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_CENTER_TOP) {
3753 attachPointParent = CLAY_STRING("CENTER_TOP");
3754 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_CENTER_CENTER) {
3755 attachPointParent = CLAY_STRING("CENTER_CENTER");
3756 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_CENTER_BOTTOM) {
3757 attachPointParent = CLAY_STRING("CENTER_BOTTOM");
3758 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_RIGHT_TOP) {
3759 attachPointParent = CLAY_STRING("RIGHT_TOP");
3760 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_RIGHT_CENTER) {
3761 attachPointParent = CLAY_STRING("RIGHT_CENTER");
3762 } else if (floatingConfig->attachPoints.parent == CLAY_ATTACH_POINT_RIGHT_BOTTOM) {
3763 attachPointParent = CLAY_STRING("RIGHT_BOTTOM");
3764 }
3765 CLAY_TEXT(CLAY_STRING(", parent: "), infoTextConfig);
3766 CLAY_TEXT(attachPointParent, infoTextConfig);
3767 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3768 }
3769 // .pointerCaptureMode
3770 CLAY_TEXT(CLAY_STRING("Pointer Capture Mode"), infoTitleConfig);
3771 Clay_String pointerCaptureMode = CLAY_STRING("NONE");
3772 if (floatingConfig->pointerCaptureMode == CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH) {
3773 pointerCaptureMode = CLAY_STRING("PASSTHROUGH");
3774 }
3775 CLAY_TEXT(pointerCaptureMode, infoTextConfig);
3776 // .attachTo
3777 CLAY_TEXT(CLAY_STRING("Attach To"), infoTitleConfig);
3778 Clay_String attachTo = CLAY_STRING("NONE");
3779 if (floatingConfig->attachTo == CLAY_ATTACH_TO_PARENT) {
3780 attachTo = CLAY_STRING("PARENT");
3781 } else if (floatingConfig->attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {
3782 attachTo = CLAY_STRING("ELEMENT_WITH_ID");
3783 } else if (floatingConfig->attachTo == CLAY_ATTACH_TO_ROOT) {
3784 attachTo = CLAY_STRING("ROOT");
3785 }
3786 CLAY_TEXT(attachTo, infoTextConfig);
3787 // .clipTo
3788 CLAY_TEXT(CLAY_STRING("Clip To"), infoTitleConfig);
3789 Clay_String clipTo = CLAY_STRING("ATTACHED_PARENT");
3790 if (floatingConfig->clipTo == CLAY_CLIP_TO_NONE) {
3791 clipTo = CLAY_STRING("NONE");
3792 }
3793 CLAY_TEXT(clipTo, infoTextConfig);
3794 }
3795 break;
3796 }
3797 case CLAY__ELEMENT_CONFIG_TYPE_BORDER: {
3798 Clay_BorderElementConfig *borderConfig = elementConfig->config.borderElementConfig;
3799 CLAY(CLAY_ID("Clay__DebugViewElementInfoBorderBody"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
3800 CLAY_TEXT(CLAY_STRING("Border Widths"), infoTitleConfig);
3801 CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
3802 CLAY_TEXT(CLAY_STRING("{ left: "), infoTextConfig);
3803 CLAY_TEXT(Clay__IntToString(borderConfig->width.left), infoTextConfig);
3804 CLAY_TEXT(CLAY_STRING(", right: "), infoTextConfig);
3805 CLAY_TEXT(Clay__IntToString(borderConfig->width.right), infoTextConfig);
3806 CLAY_TEXT(CLAY_STRING(", top: "), infoTextConfig);
3807 CLAY_TEXT(Clay__IntToString(borderConfig->width.top), infoTextConfig);
3808 CLAY_TEXT(CLAY_STRING(", bottom: "), infoTextConfig);
3809 CLAY_TEXT(Clay__IntToString(borderConfig->width.bottom), infoTextConfig);
3810 CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
3811 }
3812 // .textColor
3813 CLAY_TEXT(CLAY_STRING("Border Color"), infoTitleConfig);
3814 Clay__RenderDebugViewColor(borderConfig->color, infoTextConfig);
3815 }
3816 break;
3817 }
3818 case CLAY__ELEMENT_CONFIG_TYPE_CUSTOM:
3819 default: break;
3820 }
3821 }
3822 }
3823 } else {
3824 CLAY(CLAY_ID("Clay__DebugViewWarningsScrollPane"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .childGap = 6, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
3825 Clay_TextElementConfig *warningConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });
3826 CLAY(CLAY_ID("Clay__DebugViewWarningItemHeader"), { .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
3827 CLAY_TEXT(CLAY_STRING("Warnings"), warningConfig);
3828 }
3829 CLAY(CLAY_ID("Clay__DebugViewWarningsTopBorder"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = {200, 200, 200, 255} }) {}
3830 int32_t previousWarningsLength = context->warnings.length;
3831 for (int32_t i = 0; i < previousWarningsLength; i++) {
3832 Clay__Warning warning = context->warnings.internalArray[i];
3833 CLAY(CLAY_IDI("Clay__DebugViewWarningItem", i), { .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
3834 CLAY_TEXT(warning.baseMessage, warningConfig);
3835 if (warning.dynamicMessage.length > 0) {
3836 CLAY_TEXT(warning.dynamicMessage, warningConfig);
3837 }
3838 }
3839 }
3840 }
3841 }
3842 }
3843}
3844#pragma endregion
3845
3846uint32_t Clay__debugViewWidth = 400;
3847Clay_Color Clay__debugViewHighlightColor = { 168, 66, 28, 100 };
3848
3849Clay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena) {
3850 size_t totalSizeBytes = capacity * sizeof(Clay_String);
3851 Clay__WarningArray array = {.capacity = capacity, .length = 0};
3852 uintptr_t nextAllocOffset = arena->nextAllocation + (64 - (arena->nextAllocation % 64));
3853 if (nextAllocOffset + totalSizeBytes <= arena->capacity) {
3854 array.internalArray = (Clay__Warning*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);
3855 arena->nextAllocation = nextAllocOffset + totalSizeBytes;
3856 }
3857 else {
3858 Clay__currentContext->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
3859 .errorType = CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,
3860 .errorText = CLAY_STRING("Clay attempted to allocate memory in its arena, but ran out of capacity. Try increasing the capacity of the arena passed to Clay_Initialize()"),
3861 .userData = Clay__currentContext->errorHandler.userData });
3862 }
3863 return array;
3864}
3865
3866Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item)
3867{
3868 if (array->length < array->capacity) {
3869 array->internalArray[array->length++] = item;
3870 return &array->internalArray[array->length - 1];
3871 }
3872 return &CLAY__WARNING_DEFAULT;
3873}
3874
3875void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena)
3876{
3877 size_t totalSizeBytes = capacity * itemSize;
3878 uintptr_t nextAllocOffset = arena->nextAllocation + ((64 - (arena->nextAllocation % 64)) & 63);
3879 if (nextAllocOffset + totalSizeBytes <= arena->capacity) {
3880 arena->nextAllocation = nextAllocOffset + totalSizeBytes;
3881 return (void*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);
3882 }
3883 else {
3884 Clay__currentContext->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
3885 .errorType = CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,
3886 .errorText = CLAY_STRING("Clay attempted to allocate memory in its arena, but ran out of capacity. Try increasing the capacity of the arena passed to Clay_Initialize()"),
3887 .userData = Clay__currentContext->errorHandler.userData });
3888 }
3889 return CLAY__NULL;
3890}
3891
3892bool Clay__Array_RangeCheck(int32_t index, int32_t length)
3893{
3894 if (index < length && index >= 0) {
3895 return true;
3896 }
3897 Clay_Context* context = Clay_GetCurrentContext();
3898 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
3899 .errorType = CLAY_ERROR_TYPE_INTERNAL_ERROR,
3900 .errorText = CLAY_STRING("Clay attempted to make an out of bounds array access. This is an internal error and is likely a bug."),
3901 .userData = context->errorHandler.userData });
3902 return false;
3903}
3904
3905bool Clay__Array_AddCapacityCheck(int32_t length, int32_t capacity)
3906{
3907 if (length < capacity) {
3908 return true;
3909 }
3910 Clay_Context* context = Clay_GetCurrentContext();
3911 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
3912 .errorType = CLAY_ERROR_TYPE_INTERNAL_ERROR,
3913 .errorText = CLAY_STRING("Clay attempted to make an out of bounds array access. This is an internal error and is likely a bug."),
3914 .userData = context->errorHandler.userData });
3915 return false;
3916}
3917
3918// PUBLIC API FROM HERE ---------------------------------------
3919
3920CLAY_WASM_EXPORT("Clay_MinMemorySize")
3921uint32_t Clay_MinMemorySize(void) {
3922 Clay_Context fakeContext = {
3923 .maxElementCount = Clay__defaultMaxElementCount,
3924 .maxMeasureTextCacheWordCount = Clay__defaultMaxMeasureTextWordCacheCount,
3925 .internalArena = {
3926 .capacity = SIZE_MAX,
3927 .memory = NULL,
3928 }
3929 };
3930 Clay_Context* currentContext = Clay_GetCurrentContext();
3931 if (currentContext) {
3932 fakeContext.maxElementCount = currentContext->maxElementCount;
3933 fakeContext.maxMeasureTextCacheWordCount = currentContext->maxMeasureTextCacheWordCount;
3934 }
3935 // Reserve space in the arena for the context, important for calculating min memory size correctly
3936 Clay__Context_Allocate_Arena(&fakeContext.internalArena);
3937 Clay__InitializePersistentMemory(&fakeContext);
3938 Clay__InitializeEphemeralMemory(&fakeContext);
3939 return (uint32_t)fakeContext.internalArena.nextAllocation + 128;
3940}
3941
3942CLAY_WASM_EXPORT("Clay_CreateArenaWithCapacityAndMemory")
3943Clay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity, void *memory) {
3944 Clay_Arena arena = {
3945 .capacity = capacity,
3946 .memory = (char *)memory
3947 };
3948 return arena;
3949}
3950
3951#ifndef CLAY_WASM
3952void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData) {
3953 Clay_Context* context = Clay_GetCurrentContext();
3954 Clay__MeasureText = measureTextFunction;
3955 context->measureTextUserData = userData;
3956}
3957void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData) {
3958 Clay_Context* context = Clay_GetCurrentContext();
3959 Clay__QueryScrollOffset = queryScrollOffsetFunction;
3960 context->queryScrollOffsetUserData = userData;
3961}
3962#endif
3963
3964CLAY_WASM_EXPORT("Clay_SetLayoutDimensions")
3965void Clay_SetLayoutDimensions(Clay_Dimensions dimensions) {
3966 Clay_GetCurrentContext()->layoutDimensions = dimensions;
3967}
3968
3969CLAY_WASM_EXPORT("Clay_SetPointerState")
3970void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
3971 Clay_Context* context = Clay_GetCurrentContext();
3972 if (context->booleanWarnings.maxElementsExceeded) {
3973 return;
3974 }
3975 context->pointerInfo.position = position;
3976 context->pointerOverIds.length = 0;
3977 Clay__int32_tArray dfsBuffer = context->layoutElementChildrenBuffer;
3978 for (int32_t rootIndex = context->layoutElementTreeRoots.length - 1; rootIndex >= 0; --rootIndex) {
3979 dfsBuffer.length = 0;
3980 Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&context->layoutElementTreeRoots, rootIndex);
3981 Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex);
3982 context->treeNodeVisited.internalArray[0] = false;
3983 bool found = false;
3984 while (dfsBuffer.length > 0) {
3985 if (context->treeNodeVisited.internalArray[dfsBuffer.length - 1]) {
3986 dfsBuffer.length--;
3987 continue;
3988 }
3989 context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
3990 Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&dfsBuffer, (int)dfsBuffer.length - 1));
3991 Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO think of a way around this, maybe the fact that it's essentially a binary tree limits the cost, but the worst case is not great
3992 int32_t clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(currentElement - context->layoutElements.internalArray));
3993 Clay_LayoutElementHashMapItem *clipItem = Clay__GetHashMapItem(clipElementId);
3994 if (mapItem) {
3995 Clay_BoundingBox elementBox = mapItem->boundingBox;
3996 elementBox.x -= root->pointerOffset.x;
3997 elementBox.y -= root->pointerOffset.y;
3998 if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)) || context->externalScrollHandlingEnabled)) {
3999 if (mapItem->onHoverFunction) {
4000 mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData);
4001 }
4002 Clay_ElementIdArray_Add(&context->pointerOverIds, mapItem->elementId);
4003 found = true;
4004 }
4005 if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {
4006 dfsBuffer.length--;
4007 continue;
4008 }
4009 for (int32_t i = currentElement->childrenOrTextContent.children.length - 1; i >= 0; --i) {
4010 Clay__int32_tArray_Add(&dfsBuffer, currentElement->childrenOrTextContent.children.elements[i]);
4011 context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = false; // TODO needs to be ranged checked
4012 }
4013 } else {
4014 dfsBuffer.length--;
4015 }
4016 }
4017
4018 Clay_LayoutElement *rootElement = Clay_LayoutElementArray_Get(&context->layoutElements, root->layoutElementIndex);
4019 if (found && Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING) &&
4020 Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING).floatingElementConfig->pointerCaptureMode == CLAY_POINTER_CAPTURE_MODE_CAPTURE) {
4021 break;
4022 }
4023 }
4024
4025 if (isPointerDown) {
4026 if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
4027 context->pointerInfo.state = CLAY_POINTER_DATA_PRESSED;
4028 } else if (context->pointerInfo.state != CLAY_POINTER_DATA_PRESSED) {
4029 context->pointerInfo.state = CLAY_POINTER_DATA_PRESSED_THIS_FRAME;
4030 }
4031 } else {
4032 if (context->pointerInfo.state == CLAY_POINTER_DATA_RELEASED_THIS_FRAME) {
4033 context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED;
4034 } else if (context->pointerInfo.state != CLAY_POINTER_DATA_RELEASED) {
4035 context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED_THIS_FRAME;
4036 }
4037 }
4038}
4039
4040CLAY_WASM_EXPORT("Clay_Initialize")
4041Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler) {
4042 // Cacheline align memory passed in
4043 uintptr_t baseOffset = 64 - ((uintptr_t)arena.memory % 64);
4044 baseOffset = baseOffset == 64 ? 0 : baseOffset;
4045 arena.memory += baseOffset;
4046 Clay_Context *context = Clay__Context_Allocate_Arena(&arena);
4047 if (context == NULL) return NULL;
4048 // DEFAULTS
4049 Clay_Context *oldContext = Clay_GetCurrentContext();
4050 *context = CLAY__INIT(Clay_Context) {
4051 .maxElementCount = oldContext ? oldContext->maxElementCount : Clay__defaultMaxElementCount,
4052 .maxMeasureTextCacheWordCount = oldContext ? oldContext->maxMeasureTextCacheWordCount : Clay__defaultMaxMeasureTextWordCacheCount,
4053 .errorHandler = errorHandler.errorHandlerFunction ? errorHandler : CLAY__INIT(Clay_ErrorHandler) { Clay__ErrorHandlerFunctionDefault, 0 },
4054 .layoutDimensions = layoutDimensions,
4055 .internalArena = arena,
4056 };
4057 Clay_SetCurrentContext(context);
4058 Clay__InitializePersistentMemory(context);
4059 Clay__InitializeEphemeralMemory(context);
4060 for (int32_t i = 0; i < context->layoutElementsHashMap.capacity; ++i) {
4061 context->layoutElementsHashMap.internalArray[i] = -1;
4062 }
4063 for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) {
4064 context->measureTextHashMap.internalArray[i] = 0;
4065 }
4066 context->measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element"
4067 context->layoutDimensions = layoutDimensions;
4068 return context;
4069}
4070
4071CLAY_WASM_EXPORT("Clay_GetCurrentContext")
4072Clay_Context* Clay_GetCurrentContext(void) {
4073 return Clay__currentContext;
4074}
4075
4076CLAY_WASM_EXPORT("Clay_SetCurrentContext")
4077void Clay_SetCurrentContext(Clay_Context* context) {
4078 Clay__currentContext = context;
4079}
4080
4081CLAY_WASM_EXPORT("Clay_GetScrollOffset")
4082Clay_Vector2 Clay_GetScrollOffset(void) {
4083 Clay_Context* context = Clay_GetCurrentContext();
4084 if (context->booleanWarnings.maxElementsExceeded) {
4085 return CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;
4086 }
4087 Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
4088 // If the element has no id attached at this point, we need to generate one
4089 if (openLayoutElement->id == 0) {
4090 Clay__GenerateIdForAnonymousElement(openLayoutElement);
4091 }
4092 for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {
4093 Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
4094 if (mapping->layoutElement == openLayoutElement) {
4095 return mapping->scrollPosition;
4096 }
4097 }
4098 return CLAY__INIT(Clay_Vector2) CLAY__DEFAULT_STRUCT;
4099}
4100
4101CLAY_WASM_EXPORT("Clay_UpdateScrollContainers")
4102void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime) {
4103 Clay_Context* context = Clay_GetCurrentContext();
4104 bool isPointerActive = enableDragScrolling && (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED || context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME);
4105 // Don't apply scroll events to ancestors of the inner element
4106 int32_t highestPriorityElementIndex = -1;
4107 Clay__ScrollContainerDataInternal *highestPriorityScrollData = CLAY__NULL;
4108 for (int32_t i = 0; i < context->scrollContainerDatas.length; i++) {
4109 Clay__ScrollContainerDataInternal *scrollData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
4110 if (!scrollData->openThisFrame) {
4111 Clay__ScrollContainerDataInternalArray_RemoveSwapback(&context->scrollContainerDatas, i);
4112 continue;
4113 }
4114 scrollData->openThisFrame = false;
4115 Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId);
4116 // Element isn't rendered this frame but scroll offset has been retained
4117 if (!hashMapItem) {
4118 Clay__ScrollContainerDataInternalArray_RemoveSwapback(&context->scrollContainerDatas, i);
4119 continue;
4120 }
4121
4122 // Touch / click is released
4123 if (!isPointerActive && scrollData->pointerScrollActive) {
4124 float xDiff = scrollData->scrollPosition.x - scrollData->scrollOrigin.x;
4125 if (xDiff < -10 || xDiff > 10) {
4126 scrollData->scrollMomentum.x = (scrollData->scrollPosition.x - scrollData->scrollOrigin.x) / (scrollData->momentumTime * 25);
4127 }
4128 float yDiff = scrollData->scrollPosition.y - scrollData->scrollOrigin.y;
4129 if (yDiff < -10 || yDiff > 10) {
4130 scrollData->scrollMomentum.y = (scrollData->scrollPosition.y - scrollData->scrollOrigin.y) / (scrollData->momentumTime * 25);
4131 }
4132 scrollData->pointerScrollActive = false;
4133
4134 scrollData->pointerOrigin = CLAY__INIT(Clay_Vector2){0,0};
4135 scrollData->scrollOrigin = CLAY__INIT(Clay_Vector2){0,0};
4136 scrollData->momentumTime = 0;
4137 }
4138
4139 // Apply existing momentum
4140 scrollData->scrollPosition.x += scrollData->scrollMomentum.x;
4141 scrollData->scrollMomentum.x *= 0.95f;
4142 bool scrollOccurred = scrollDelta.x != 0 || scrollDelta.y != 0;
4143 if ((scrollData->scrollMomentum.x > -0.1f && scrollData->scrollMomentum.x < 0.1f) || scrollOccurred) {
4144 scrollData->scrollMomentum.x = 0;
4145 }
4146 scrollData->scrollPosition.x = CLAY__MIN(CLAY__MAX(scrollData->scrollPosition.x, -(CLAY__MAX(scrollData->contentSize.width - scrollData->layoutElement->dimensions.width, 0))), 0);
4147
4148 scrollData->scrollPosition.y += scrollData->scrollMomentum.y;
4149 scrollData->scrollMomentum.y *= 0.95f;
4150 if ((scrollData->scrollMomentum.y > -0.1f && scrollData->scrollMomentum.y < 0.1f) || scrollOccurred) {
4151 scrollData->scrollMomentum.y = 0;
4152 }
4153 scrollData->scrollPosition.y = CLAY__MIN(CLAY__MAX(scrollData->scrollPosition.y, -(CLAY__MAX(scrollData->contentSize.height - scrollData->layoutElement->dimensions.height, 0))), 0);
4154
4155 for (int32_t j = 0; j < context->pointerOverIds.length; ++j) { // TODO n & m are small here but this being n*m gives me the creeps
4156 if (scrollData->layoutElement->id == Clay_ElementIdArray_Get(&context->pointerOverIds, j)->id) {
4157 highestPriorityElementIndex = j;
4158 highestPriorityScrollData = scrollData;
4159 }
4160 }
4161 }
4162
4163 if (highestPriorityElementIndex > -1 && highestPriorityScrollData) {
4164 Clay_LayoutElement *scrollElement = highestPriorityScrollData->layoutElement;
4165 Clay_ClipElementConfig *clipConfig = Clay__FindElementConfigWithType(scrollElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
4166 bool canScrollVertically = clipConfig->vertical && highestPriorityScrollData->contentSize.height > scrollElement->dimensions.height;
4167 bool canScrollHorizontally = clipConfig->horizontal && highestPriorityScrollData->contentSize.width > scrollElement->dimensions.width;
4168 // Handle wheel scroll
4169 if (canScrollVertically) {
4170 highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollPosition.y + scrollDelta.y * 10;
4171 }
4172 if (canScrollHorizontally) {
4173 highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollPosition.x + scrollDelta.x * 10;
4174 }
4175 // Handle click / touch scroll
4176 if (isPointerActive) {
4177 highestPriorityScrollData->scrollMomentum = CLAY__INIT(Clay_Vector2)CLAY__DEFAULT_STRUCT;
4178 if (!highestPriorityScrollData->pointerScrollActive) {
4179 highestPriorityScrollData->pointerOrigin = context->pointerInfo.position;
4180 highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition;
4181 highestPriorityScrollData->pointerScrollActive = true;
4182 } else {
4183 float scrollDeltaX = 0, scrollDeltaY = 0;
4184 if (canScrollHorizontally) {
4185 float oldXScrollPosition = highestPriorityScrollData->scrollPosition.x;
4186 highestPriorityScrollData->scrollPosition.x = highestPriorityScrollData->scrollOrigin.x + (context->pointerInfo.position.x - highestPriorityScrollData->pointerOrigin.x);
4187 highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - highestPriorityScrollData->boundingBox.width));
4188 scrollDeltaX = highestPriorityScrollData->scrollPosition.x - oldXScrollPosition;
4189 }
4190 if (canScrollVertically) {
4191 float oldYScrollPosition = highestPriorityScrollData->scrollPosition.y;
4192 highestPriorityScrollData->scrollPosition.y = highestPriorityScrollData->scrollOrigin.y + (context->pointerInfo.position.y - highestPriorityScrollData->pointerOrigin.y);
4193 highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - highestPriorityScrollData->boundingBox.height));
4194 scrollDeltaY = highestPriorityScrollData->scrollPosition.y - oldYScrollPosition;
4195 }
4196 if (scrollDeltaX > -0.1f && scrollDeltaX < 0.1f && scrollDeltaY > -0.1f && scrollDeltaY < 0.1f && highestPriorityScrollData->momentumTime > 0.15f) {
4197 highestPriorityScrollData->momentumTime = 0;
4198 highestPriorityScrollData->pointerOrigin = context->pointerInfo.position;
4199 highestPriorityScrollData->scrollOrigin = highestPriorityScrollData->scrollPosition;
4200 } else {
4201 highestPriorityScrollData->momentumTime += deltaTime;
4202 }
4203 }
4204 }
4205 // Clamp any changes to scroll position to the maximum size of the contents
4206 if (canScrollVertically) {
4207 highestPriorityScrollData->scrollPosition.y = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.y, 0), -(highestPriorityScrollData->contentSize.height - scrollElement->dimensions.height));
4208 }
4209 if (canScrollHorizontally) {
4210 highestPriorityScrollData->scrollPosition.x = CLAY__MAX(CLAY__MIN(highestPriorityScrollData->scrollPosition.x, 0), -(highestPriorityScrollData->contentSize.width - scrollElement->dimensions.width));
4211 }
4212 }
4213}
4214
4215CLAY_WASM_EXPORT("Clay_BeginLayout")
4216void Clay_BeginLayout(void) {
4217 Clay_Context* context = Clay_GetCurrentContext();
4218 Clay__InitializeEphemeralMemory(context);
4219 context->generation++;
4220 context->dynamicElementIndex = 0;
4221 // Set up the root container that covers the entire window
4222 Clay_Dimensions rootDimensions = {context->layoutDimensions.width, context->layoutDimensions.height};
4223 if (context->debugModeEnabled) {
4224 rootDimensions.width -= (float)Clay__debugViewWidth;
4225 }
4226 context->booleanWarnings = CLAY__INIT(Clay_BooleanWarnings) CLAY__DEFAULT_STRUCT;
4227 Clay__OpenElementWithId(CLAY_ID("Clay__RootContainer"));
4228 Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) {
4229 .layout = { .sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)} }
4230 });
4231 Clay__int32_tArray_Add(&context->openLayoutElementStack, 0);
4232 Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { .layoutElementIndex = 0 });
4233}
4234
4235CLAY_WASM_EXPORT("Clay_EndLayout")
4236Clay_RenderCommandArray Clay_EndLayout(void) {
4237 Clay_Context* context = Clay_GetCurrentContext();
4238 Clay__CloseElement();
4239 bool elementsExceededBeforeDebugView = context->booleanWarnings.maxElementsExceeded;
4240 if (context->debugModeEnabled && !elementsExceededBeforeDebugView) {
4241 context->warningsEnabled = false;
4242 Clay__RenderDebugView();
4243 context->warningsEnabled = true;
4244 }
4245 if (context->booleanWarnings.maxElementsExceeded) {
4246 Clay_String message;
4247 if (!elementsExceededBeforeDebugView) {
4248 message = CLAY_STRING("Clay Error: Layout elements exceeded Clay__maxElementCount after adding the debug-view to the layout.");
4249 } else {
4250 message = CLAY_STRING("Clay Error: Layout elements exceeded Clay__maxElementCount");
4251 }
4252 Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand ) {
4253 .boundingBox = { context->layoutDimensions.width / 2 - 59 * 4, context->layoutDimensions.height / 2, 0, 0 },
4254 .renderData = { .text = { .stringContents = CLAY__INIT(Clay_StringSlice) { .length = message.length, .chars = message.chars, .baseChars = message.chars }, .textColor = {255, 0, 0, 255}, .fontSize = 16 } },
4255 .commandType = CLAY_RENDER_COMMAND_TYPE_TEXT
4256 });
4257 }
4258 if (context->openLayoutElementStack.length > 1) {
4259 context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
4260 .errorType = CLAY_ERROR_TYPE_UNBALANCED_OPEN_CLOSE,
4261 .errorText = CLAY_STRING("There were still open layout elements when EndLayout was called. This results from an unequal number of calls to Clay__OpenElement and Clay__CloseElement."),
4262 .userData = context->errorHandler.userData });
4263 }
4264 Clay__CalculateFinalLayout();
4265 return context->renderCommands;
4266}
4267
4268CLAY_WASM_EXPORT("Clay_GetElementId")
4269Clay_ElementId Clay_GetElementId(Clay_String idString) {
4270 return Clay__HashString(idString, 0);
4271}
4272
4273CLAY_WASM_EXPORT("Clay_GetElementIdWithIndex")
4274Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index) {
4275 return Clay__HashStringWithOffset(idString, index, 0);
4276}
4277
4278bool Clay_Hovered(void) {
4279 Clay_Context* context = Clay_GetCurrentContext();
4280 if (context->booleanWarnings.maxElementsExceeded) {
4281 return false;
4282 }
4283 Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
4284 // If the element has no id attached at this point, we need to generate one
4285 if (openLayoutElement->id == 0) {
4286 Clay__GenerateIdForAnonymousElement(openLayoutElement);
4287 }
4288 for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {
4289 if (Clay_ElementIdArray_Get(&context->pointerOverIds, i)->id == openLayoutElement->id) {
4290 return true;
4291 }
4292 }
4293 return false;
4294}
4295
4296void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerInfo, void *userData), void *userData) {
4297 Clay_Context* context = Clay_GetCurrentContext();
4298 if (context->booleanWarnings.maxElementsExceeded) {
4299 return;
4300 }
4301 Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
4302 if (openLayoutElement->id == 0) {
4303 Clay__GenerateIdForAnonymousElement(openLayoutElement);
4304 }
4305 Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(openLayoutElement->id);
4306 hashMapItem->onHoverFunction = onHoverFunction;
4307 hashMapItem->hoverFunctionUserData = userData;
4308}
4309
4310CLAY_WASM_EXPORT("Clay_PointerOver")
4311bool Clay_PointerOver(Clay_ElementId elementId) { // TODO return priority for separating multiple results
4312 Clay_Context* context = Clay_GetCurrentContext();
4313 for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {
4314 if (Clay_ElementIdArray_Get(&context->pointerOverIds, i)->id == elementId.id) {
4315 return true;
4316 }
4317 }
4318 return false;
4319}
4320
4321CLAY_WASM_EXPORT("Clay_GetScrollContainerData")
4322Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id) {
4323 Clay_Context* context = Clay_GetCurrentContext();
4324 for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) {
4325 Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&context->scrollContainerDatas, i);
4326 if (scrollContainerData->elementId == id.id) {
4327 Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(scrollContainerData->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
4328 if (!clipElementConfig) { // This can happen on the first frame before a scroll container is declared
4329 return CLAY__INIT(Clay_ScrollContainerData) CLAY__DEFAULT_STRUCT;
4330 }
4331 return CLAY__INIT(Clay_ScrollContainerData) {
4332 .scrollPosition = &scrollContainerData->scrollPosition,
4333 .scrollContainerDimensions = { scrollContainerData->boundingBox.width, scrollContainerData->boundingBox.height },
4334 .contentDimensions = scrollContainerData->contentSize,
4335 .config = *clipElementConfig,
4336 .found = true
4337 };
4338 }
4339 }
4340 return CLAY__INIT(Clay_ScrollContainerData) CLAY__DEFAULT_STRUCT;
4341}
4342
4343CLAY_WASM_EXPORT("Clay_GetElementData")
4344Clay_ElementData Clay_GetElementData(Clay_ElementId id){
4345 Clay_LayoutElementHashMapItem * item = Clay__GetHashMapItem(id.id);
4346 if(item == &Clay_LayoutElementHashMapItem_DEFAULT) {
4347 return CLAY__INIT(Clay_ElementData) CLAY__DEFAULT_STRUCT;
4348 }
4349
4350 return CLAY__INIT(Clay_ElementData){
4351 .boundingBox = item->boundingBox,
4352 .found = true
4353 };
4354}
4355
4356CLAY_WASM_EXPORT("Clay_SetDebugModeEnabled")
4357void Clay_SetDebugModeEnabled(bool enabled) {
4358 Clay_Context* context = Clay_GetCurrentContext();
4359 context->debugModeEnabled = enabled;
4360}
4361
4362CLAY_WASM_EXPORT("Clay_IsDebugModeEnabled")
4363bool Clay_IsDebugModeEnabled(void) {
4364 Clay_Context* context = Clay_GetCurrentContext();
4365 return context->debugModeEnabled;
4366}
4367
4368CLAY_WASM_EXPORT("Clay_SetCullingEnabled")
4369void Clay_SetCullingEnabled(bool enabled) {
4370 Clay_Context* context = Clay_GetCurrentContext();
4371 context->disableCulling = !enabled;
4372}
4373
4374CLAY_WASM_EXPORT("Clay_SetExternalScrollHandlingEnabled")
4375void Clay_SetExternalScrollHandlingEnabled(bool enabled) {
4376 Clay_Context* context = Clay_GetCurrentContext();
4377 context->externalScrollHandlingEnabled = enabled;
4378}
4379
4380CLAY_WASM_EXPORT("Clay_GetMaxElementCount")
4381int32_t Clay_GetMaxElementCount(void) {
4382 Clay_Context* context = Clay_GetCurrentContext();
4383 return context->maxElementCount;
4384}
4385
4386CLAY_WASM_EXPORT("Clay_SetMaxElementCount")
4387void Clay_SetMaxElementCount(int32_t maxElementCount) {
4388 Clay_Context* context = Clay_GetCurrentContext();
4389 if (context) {
4390 context->maxElementCount = maxElementCount;
4391 } else {
4392 Clay__defaultMaxElementCount = maxElementCount; // TODO: Fix this
4393 Clay__defaultMaxMeasureTextWordCacheCount = maxElementCount * 2;
4394 }
4395}
4396
4397CLAY_WASM_EXPORT("Clay_GetMaxMeasureTextCacheWordCount")
4398int32_t Clay_GetMaxMeasureTextCacheWordCount(void) {
4399 Clay_Context* context = Clay_GetCurrentContext();
4400 return context->maxMeasureTextCacheWordCount;
4401}
4402
4403CLAY_WASM_EXPORT("Clay_SetMaxMeasureTextCacheWordCount")
4404void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount) {
4405 Clay_Context* context = Clay_GetCurrentContext();
4406 if (context) {
4407 Clay__currentContext->maxMeasureTextCacheWordCount = maxMeasureTextCacheWordCount;
4408 } else {
4409 Clay__defaultMaxMeasureTextWordCacheCount = maxMeasureTextCacheWordCount; // TODO: Fix this
4410 }
4411}
4412
4413CLAY_WASM_EXPORT("Clay_ResetMeasureTextCache")
4414void Clay_ResetMeasureTextCache(void) {
4415 Clay_Context* context = Clay_GetCurrentContext();
4416 context->measureTextHashMapInternal.length = 0;
4417 context->measureTextHashMapInternalFreeList.length = 0;
4418 context->measureTextHashMap.length = 0;
4419 context->measuredWords.length = 0;
4420 context->measuredWordsFreeList.length = 0;
4421
4422 for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) {
4423 context->measureTextHashMap.internalArray[i] = 0;
4424 }
4425 context->measureTextHashMapInternal.length = 1; // Reserve the 0 value to mean "no next element"
4426}
4427
4428#endif // CLAY_IMPLEMENTATION
4429
4430/*
4431LICENSE
4432zlib/libpng license
4433
4434Copyright (c) 2024 Nic Barker
4435
4436This software is provided 'as-is', without any express or implied warranty.
4437In no event will the authors be held liable for any damages arising from the
4438use of this software.
4439
4440Permission is granted to anyone to use this software for any purpose,
4441including commercial applications, and to alter it and redistribute it
4442freely, subject to the following restrictions:
4443
4444 1. The origin of this software must not be misrepresented; you must not
4445 claim that you wrote the original software. If you use this software in a
4446 product, an acknowledgment in the product documentation would be
4447 appreciated but is not required.
4448
4449 2. Altered source versions must be plainly marked as such, and must not
4450 be misrepresented as being the original software.
4451
4452 3. This notice may not be removed or altered from any source
4453 distribution.
4454*/
diff --git a/src/encryption.c b/src/encryption.c
index 322a2cd..6db2c88 100644
--- a/src/encryption.c
+++ b/src/encryption.c
@@ -455,7 +455,7 @@ int _cryptscan__process_scandir(const char * const folder, taskqueue *toscan, ct
455 task *tmptsk = NULL; 455 task *tmptsk = NULL;
456 struct _cryptscan_args *args = NULL; char tflag = 0; 456 struct _cryptscan_args *args = NULL; char tflag = 0;
457 for(int i = 0; i < entries; i++) { 457 for(int i = 0; i < entries; i++) {
458 args = calloc(1, sizeof(*args)); 458 args = VXGG_CALLOC(1, sizeof(*args));
459 if(!args) { 459 if(!args) {
460 if(___VXGG___VERBOSE_ERRORS___) WARN(errno, "<_cryptscan__process_scandir> Warning: Could not create arg holder for task",); 460 if(___VXGG___VERBOSE_ERRORS___) WARN(errno, "<_cryptscan__process_scandir> Warning: Could not create arg holder for task",);
461 } 461 }
diff --git a/src/main.c b/src/main.c
index c9cc873..0b68a44 100755
--- a/src/main.c
+++ b/src/main.c
@@ -11,6 +11,9 @@
11 11
12#define _GNU_SOURCE 1 12#define _GNU_SOURCE 1
13 13
14#define CLAY_IMPLEMENTATION
15#include "clay.h"
16
14#include "shared.c" 17#include "shared.c"
15#include "encryption.c" 18#include "encryption.c"
16#include "threadpool.c" 19#include "threadpool.c"
@@ -18,8 +21,16 @@
18#include <errno.h> 21#include <errno.h>
19#include <error.h> 22#include <error.h>
20 23
24void handleClayErrors(Clay_ErrorData ed) {
25
26}
27
21int main() { 28int main() {
22 error(-1, ENOTSUP, "lol"); 29 uint64_t totalMem = Clay_MinMemorySize();
30 Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMem, VXGG_CALLOC(totalMem, 1));
31 Clay_Initialize(arena, (Clay_Dimensions){0, 0} /* TODO: Figure out how to get screen dims */, (Clay_ErrorHandler){handleClayErrors});
32
33 // TODO: Figure out how to use clay
23 34
24 return 0; 35 return 0;
25} \ No newline at end of file 36} \ No newline at end of file
diff --git a/src/shared.c b/src/shared.c
index 1dd0309..5e425f0 100644
--- a/src/shared.c
+++ b/src/shared.c
@@ -12,6 +12,30 @@
12#ifndef __VXGG_REWRITE___SHARED_C___3880294315821___ 12#ifndef __VXGG_REWRITE___SHARED_C___3880294315821___
13#define __VXGG_REWRITE___SHARED_C___3880294315821___ 1 13#define __VXGG_REWRITE___SHARED_C___3880294315821___ 1
14 14
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18#include <errno.h>
19#include <error.h>
20
21
22#define VXGG_MALLOC(size) malloc((size))
23#define VXGG_CALLOC(nmemb, size) calloc((nmemb), (size))
24#ifdef DEBUG
25#undef VXGG_MALLOC
26#undef VXGG_CALLOC
27
28void* VXGG_ALLOC(size_t size) {
29 void *mem = malloc(size);
30 if(!mem) return NULL;
31 memset(mem, 0x57, size); // VX -> {21, 23} % 16 -> 0x57
32 return mem;
33}
34
35#define VXGG_MALLOC(size) VXGG_ALLOC((size))
36#define VXGG_CALLOC(nmemb, size) VXGG_ALLOC((nmemb) * (size))
37#endif
38
15#define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0])) 39#define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0]))
16 40
17#define ERRRET(errval, retval) do {\ 41#define ERRRET(errval, retval) do {\
@@ -40,17 +64,11 @@
40#define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0) 64#define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0)
41//! Spit out a warning using `error` 65//! Spit out a warning using `error`
42#define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0) 66#define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0)
43 67// TODO: gcc is yelling at me about these functions implicitly falling through. Not sure why
44 68
45typedef int (*gcallback)(void*); //!< Generic callback signature 69typedef int (*gcallback)(void*); //!< Generic callback signature
46typedef void (*fcallback)(void*); //!< free()-like callback signature 70typedef void (*fcallback)(void*); //!< free()-like callback signature
47 71
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51#include <errno.h>
52#include <error.h>
53
54/** 72/**
55 * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer 73 * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer
56 * 74 *
diff --git a/src/threadpool.c b/src/threadpool.c
index 7a2f93a..8c6b243 100644
--- a/src/threadpool.c
+++ b/src/threadpool.c
@@ -76,7 +76,7 @@ typedef struct ctqueue {
76task * task_new(gcallback callback, fcallback freecb, void *data) { 76task * task_new(gcallback callback, fcallback freecb, void *data) {
77 if(callback == NULL) ERRRET(EINVAL, NULL); 77 if(callback == NULL) ERRRET(EINVAL, NULL);
78 78
79 task *tsk = calloc(1, sizeof(*tsk)); 79 task *tsk = VXGG_CALLOC(1, sizeof(*tsk));
80 if(!tsk) return NULL; 80 if(!tsk) return NULL;
81 81
82 tsk->callback = callback; 82 tsk->callback = callback;
@@ -129,7 +129,7 @@ int task_fired(task *tsk) {
129tqnode * tqnode_new(tqnode *next, tqnode *prev, task *tsk) { 129tqnode * tqnode_new(tqnode *next, tqnode *prev, task *tsk) {
130 if(!tsk) ERRRET(EINVAL, NULL); 130 if(!tsk) ERRRET(EINVAL, NULL);
131 131
132 tqnode *node = calloc(1, sizeof(*node)); 132 tqnode *node = VXGG_CALLOC(1, sizeof(*node));
133 if(!node) return NULL; 133 if(!node) return NULL;
134 134
135 node->next = next; 135 node->next = next;
@@ -166,7 +166,7 @@ void tqnode_free(void *tqn) {
166 * @retval (taskqueue*)[NULL, taskqueue*] Returns a new taskqueue object. Returns `null` and sets errno on error 166 * @retval (taskqueue*)[NULL, taskqueue*] Returns a new taskqueue object. Returns `null` and sets errno on error
167 */ 167 */
168taskqueue * taskqueue_new(void) { 168taskqueue * taskqueue_new(void) {
169 taskqueue *tq = calloc(1, sizeof(*tq)); 169 taskqueue *tq = VXGG_CALLOC(1, sizeof(*tq));
170 if(!tq) return NULL; 170 if(!tq) return NULL;
171 171
172 tq->start = NULL; 172 tq->start = NULL;
@@ -349,7 +349,7 @@ static void cndd_helper(cnd_t *cond) {
349ctqueue * ctqueue_init(int nthreads) { 349ctqueue * ctqueue_init(int nthreads) {
350 if(nthreads <= 0) ERRRET(EINVAL, NULL); 350 if(nthreads <= 0) ERRRET(EINVAL, NULL);
351 351
352 ctqueue *ctq = calloc(1, sizeof(*ctq)); 352 ctqueue *ctq = VXGG_CALLOC(1, sizeof(*ctq));
353 if(!ctq) return NULL; 353 if(!ctq) return NULL;
354 354
355 ctq->canceled = 0; 355 ctq->canceled = 0;
@@ -361,7 +361,7 @@ ctqueue * ctqueue_init(int nthreads) {
361 if(mtx_init(&ctq->mutex, mtx_plain) != thrd_success) goto ERR_ctqueue_init; 361 if(mtx_init(&ctq->mutex, mtx_plain) != thrd_success) goto ERR_ctqueue_init;
362 if(cnd_init(&ctq->cond) != thrd_success) goto ERR_ctqueue_init; 362 if(cnd_init(&ctq->cond) != thrd_success) goto ERR_ctqueue_init;
363 363
364 ctq->thrdarr = calloc(ctq->talen, sizeof(thrd_t)); 364 ctq->thrdarr = VXGG_CALLOC(ctq->talen, sizeof(thrd_t));
365 if(!ctq->thrdarr) goto ERR_ctqueue_init; 365 if(!ctq->thrdarr) goto ERR_ctqueue_init;
366 366
367 return ctq; 367 return ctq;