Merge branch 'main' into HEAD

For image-related changes
This commit is contained in:
mivirl 2025-06-24 17:46:05 +00:00
commit d487d5990b
No known key found for this signature in database
22 changed files with 396 additions and 217 deletions

142
README.md
View file

@ -92,7 +92,7 @@ int main() {
.backgroundColor = COLOR_LIGHT .backgroundColor = COLOR_LIGHT
}) { }) {
CLAY({ .id = CLAY_ID("ProfilePictureOuter"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = COLOR_RED }) { CLAY({ .id = CLAY_ID("ProfilePictureOuter"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = COLOR_RED }) {
CLAY({ .id = CLAY_ID("ProfilePicture"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture, .sourceDimensions = {60, 60} } }) {} CLAY({ .id = CLAY_ID("ProfilePicture"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture } }) {}
CLAY_TEXT(CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} })); CLAY_TEXT(CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} }));
} }
@ -161,8 +161,8 @@ For help starting out or to discuss clay, considering joining [the discord serve
- [Clay_MinMemorySize](#clay_minmemorysize) - [Clay_MinMemorySize](#clay_minmemorysize)
- [Clay_CreateArenaWithCapacityAndMemory](#clay_createarenawithcapacityandmemory) - [Clay_CreateArenaWithCapacityAndMemory](#clay_createarenawithcapacityandmemory)
- [Clay_SetMeasureTextFunction](#clay_setmeasuretextfunction) - [Clay_SetMeasureTextFunction](#clay_setmeasuretextfunction)
- [Clay_ResetMeasureTextCache](#clau_resetmeasuretextcache) - [Clay_ResetMeasureTextCache](#clay_resetmeasuretextcache)
- [Clay_SetMaxElementCount](clay_setmaxelementcount) - [Clay_SetMaxElementCount](#clay_setmaxelementcount)
- [Clay_SetMaxMeasureTextCacheWordCount](#clay_setmaxmeasuretextcachewordcount) - [Clay_SetMaxMeasureTextCacheWordCount](#clay_setmaxmeasuretextcachewordcount)
- [Clay_Initialize](#clay_initialize) - [Clay_Initialize](#clay_initialize)
- [Clay_GetCurrentContext](#clay_getcurrentcontext) - [Clay_GetCurrentContext](#clay_getcurrentcontext)
@ -360,11 +360,11 @@ Clay_UpdateScrollContainers(
); );
// ... // ...
// Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element // Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element
CLAY({ .clip = vertical, .childOffset = Clay_GetScrollOffset() }) { CLAY({ .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
// Scrolling contents // Scrolling contents
} }
// .childOffset can be provided directly if you would prefer to manage scrolling outside of clay // .childOffset can be provided directly if you would prefer to manage scrolling outside of clay
CLAY({ .clip = vertical, .childOffset = myData.scrollContainer.offset }) { CLAY({ .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
// Scrolling contents // Scrolling contents
} }
``` ```
@ -839,7 +839,6 @@ Clay_TextElementConfig {
CLAY_TEXT_WRAP_NEWLINES, CLAY_TEXT_WRAP_NEWLINES,
CLAY_TEXT_WRAP_NONE, CLAY_TEXT_WRAP_NONE,
}; };
bool hashStringContents
}; };
``` ```
@ -899,14 +898,6 @@ Available options are:
--- ---
**`.hashStringContents`**
`CLAY_TEXT_CONFIG(.hashStringContents = true)`
By default, clay will cache the dimensions of text measured by [the provided MeasureText function](#clay_setmeasuretextfunction) based on the string's pointer and length. Setting `.hashStringContents = true` will cause Clay to hash the entire string contents. Used to fix incorrect measurements caused by re-use of string memory, disabled by default as it will incur significant performance overhead for very large bodies of text.
---
**Examples** **Examples**
```C ```C
@ -1062,6 +1053,7 @@ typedef struct {
Clay_LayoutConfig layout; Clay_LayoutConfig layout;
Clay_Color backgroundColor; Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius; Clay_CornerRadius cornerRadius;
Clay_AspectRatioElementConfig aspectRatio;
Clay_ImageElementConfig image; Clay_ImageElementConfig image;
Clay_FloatingElementConfig floating; Clay_FloatingElementConfig floating;
Clay_CustomElementConfig custom; Clay_CustomElementConfig custom;
@ -1108,9 +1100,17 @@ Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to p
--- ---
**`.aspectRatio`** - `Clay_AspectRatioElementConfig`
`CLAY({ .aspectRatio = 1 })`
Uses [Clay_AspectRatioElementConfig](#clay_aspectratioelementconfig). Configures the element as an aspect ratio scaling element. Especially useful for rendering images, but can also be used to enforce a fixed width / height ratio of other elements.
---
**`.image`** - `Clay_ImageElementConfig` **`.image`** - `Clay_ImageElementConfig`
`CLAY({ .image = { .imageData = &myImage, .sourceDimensions = { 640, 480 } } })` `CLAY({ .image = { .imageData = &myImage } })`
Uses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element as an image element. Causes a render command with type `IMAGE` to be emitted. Uses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element as an image element. Causes a render command with type `IMAGE` to be emitted.
@ -1294,23 +1294,12 @@ CLAY({ .id = CLAY_ID("Button"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTO
```C ```C
Clay_ImageElementConfig { Clay_ImageElementConfig {
Clay_Dimensions sourceDimensions {
float width; float height;
};
void * imageData; void * imageData;
}; };
``` ```
**Fields** **Fields**
**`.sourceDimensions`** - `Clay_Dimensions`
`CLAY({ .image = { .sourceDimensions = { 1024, 768 } } }) {}`
Used to perform **aspect ratio scaling** on the image element. As of this version of clay, aspect ratio scaling only applies to the `height` of an image (i.e. image height will scale with width growth and limitations, but width will not scale with height growth and limitations)
---
**`.imageData`** - `void *` **`.imageData`** - `void *`
`CLAY({ .image = { .imageData = &myImage } }) {}` `CLAY({ .image = { .imageData = &myImage } }) {}`
@ -1321,7 +1310,7 @@ Used to perform **aspect ratio scaling** on the image element. As of this versio
// Load an image somewhere in your code // Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png"); YourImage profilePicture = LoadYourImage("profilePicture.png");
// Note that when rendering, .imageData will be void* type. // Note that when rendering, .imageData will be void* type.
CLAY({ .image = { .imageData = &profilePicture, .sourceDimensions = { 60, 60 } } }) {} CLAY({ .image = { .imageData = &profilePicture } }) {}
``` ```
**Examples** **Examples**
@ -1330,11 +1319,105 @@ CLAY({ .image = { .imageData = &profilePicture, .sourceDimensions = { 60, 60 } }
// Load an image somewhere in your code // Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png"); YourImage profilePicture = LoadYourImage("profilePicture.png");
// Declare a reusable image config // Declare a reusable image config
Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture, .sourceDimensions = {60, 60} }; Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };
// Declare an image element using a reusable config // Declare an image element using a reusable config
CLAY({ .image = imageConfig }) {} CLAY({ .image = imageConfig }) {}
// Declare an image element using an inline config // Declare an image element using an inline config
CLAY({ .image = { .imageData = &profilePicture, .sourceDimensions = {60, 60} } }) {} CLAY({ .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
// Rendering example
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
```
**Rendering**
Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand`s with `commandType = CLAY_RENDER_COMMAND_TYPE_IMAGE` will be created. The user will need to access `renderCommand->renderData.image->imageData` to retrieve image data referenced during layout creation. It's also up to the user to decide how / if they wish to blend `renderCommand->renderData.image->backgroundColor` with the image.
---
### Clay_AspectRatioElementConfig
**Usage**
`CLAY({ .aspectRatio = 16.0 / 9.0 }) {}`
**Clay_AspectRatioElementConfig** configures a clay element to enforce a fixed width / height ratio in its final dimensions. Mostly used for image elements, but can also be used for non image elements.
**Struct API (Pseudocode)**
```C
Clay_AspectRatioElementConfig {
float aspectRatio;
};
```
**Fields**
**`.aspectRatio`** - `float`
`CLAY({ .aspectRatio = { .aspectRatio = 16.0 / 9.0 } }) {}`
or alternatively, as C will automatically pass the value to the first nested struct field:
`CLAY({ .aspectRatio = 16.0 / 9.0 }) {}`
**Examples**
```C
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Declare an image element that will grow along the X axis while maintaining its original aspect ratio
CLAY({
.layout = { .width = CLAY_SIZING_GROW() },
.aspectRatio = profilePicture.width / profilePicture.height,
.image = { .imageData = &profilePicture },
}) {}
```
---
### Clay_ImageElementConfig
**Usage**
`CLAY({ .image = { ...image config } }) {}`
**Clay_ImageElementConfig** configures a clay element to render an image as its background.
**Struct API (Pseudocode)**
```C
Clay_ImageElementConfig {
void * imageData;
};
```
**Fields**
**`.imageData`** - `void *`
`CLAY({ .image = { .imageData = &myImage } }) {}`
`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.
```C
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Note that when rendering, .imageData will be void* type.
CLAY({ .image = { .imageData = &profilePicture } }) {}
```
Note: for an image to maintain its original aspect ratio when using dynamic scaling, the [.aspectRatio](#clay_aspectratioelementconfig) config option must be used.
**Examples**
```C
// Load an image somewhere in your code
YourImage profilePicture = LoadYourImage("profilePicture.png");
// Declare a reusable image config
Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };
// Declare an image element using a reusable config
CLAY({ .image = imageConfig }) {}
// Declare an image element using an inline config
CLAY({ .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
// Rendering example // Rendering example
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData; YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
``` ```
@ -2020,7 +2103,6 @@ typedef struct {
typedef struct { typedef struct {
Clay_Color backgroundColor; Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius; Clay_CornerRadius cornerRadius;
Clay_Dimensions sourceDimensions;
void* imageData; void* imageData;
} Clay_ImageRenderData; } Clay_ImageRenderData;
``` ```

View file

@ -41,10 +41,10 @@ error_handler :: proc "c" (errorData: clay.ErrorData) {
// Do something with the error data. // Do something with the error data.
} }
min_memory_size: u32 = clay.MinMemorySize() min_memory_size := clay.MinMemorySize()
memory := make([^]u8, min_memory_size) memory := make([^]u8, min_memory_size)
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(min_memory_size, memory) arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(uint(min_memory_size), memory)
clay.Initialize(arena, { width = 1080, height = 720 }, { handler = error_handler }) clay.Initialize(arena, {1080, 720}, { handler = error_handler })
``` ```
3. Provide a `measure_text(text, config)` proc "c" with [clay.SetMeasureTextFunction(function)](https://github.com/nicbarker/clay/blob/main/README.md#clay_setmeasuretextfunction) so that Clay can measure and wrap text. 3. Provide a `measure_text(text, config)` proc "c" with [clay.SetMeasureTextFunction(function)](https://github.com/nicbarker/clay/blob/main/README.md#clay_setmeasuretextfunction) so that Clay can measure and wrap text.
@ -74,7 +74,7 @@ clay.SetMeasureTextFunction(measure_text, nil)
```Odin ```Odin
// Update internal pointer position for handling mouseover / click / touch events // Update internal pointer position for handling mouseover / click / touch events
clay.SetPointerState( clay.SetPointerState(
clay.Vector2 { mouse_pos_x, mouse_pos_y }, { mouse_pos_x, mouse_pos_y },
is_mouse_down, is_mouse_down,
) )
``` ```
@ -148,6 +148,7 @@ create_layout :: proc() -> clay.ClayArray(clay.RenderCommand) {
sizing = { width = clay.SizingFixed(60), height = clay.SizingFixed(60) }, sizing = { width = clay.SizingFixed(60), height = clay.SizingFixed(60) },
}, },
image = { image = {
// How you define `profile_picture` depends on your renderer.
imageData = &profile_picture, imageData = &profile_picture,
sourceDimensions = { sourceDimensions = {
width = 60, width = 60,
@ -179,8 +180,7 @@ create_layout :: proc() -> clay.ClayArray(clay.RenderCommand) {
} }
// Returns a list of render commands // Returns a list of render commands
render_commands: clay.ClayArray(clay.RenderCommand) = clay.EndLayout() return clay.EndLayout()
return render_commands
} }
``` ```

View file

@ -113,9 +113,12 @@ TextElementConfig :: struct {
textAlignment: TextAlignment, textAlignment: TextAlignment,
} }
AspectRatioElementConfig :: struct {
aspectRatio: f32,
}
ImageElementConfig :: struct { ImageElementConfig :: struct {
imageData: rawptr, imageData: rawptr,
sourceDimensions: Dimensions,
} }
CustomElementConfig :: struct { CustomElementConfig :: struct {
@ -203,7 +206,6 @@ RectangleRenderData :: struct {
ImageRenderData :: struct { ImageRenderData :: struct {
backgroundColor: Color, backgroundColor: Color,
cornerRadius: CornerRadius, cornerRadius: CornerRadius,
sourceDimensions: Dimensions,
imageData: rawptr, imageData: rawptr,
} }
@ -340,6 +342,7 @@ ElementDeclaration :: struct {
layout: LayoutConfig, layout: LayoutConfig,
backgroundColor: Color, backgroundColor: Color,
cornerRadius: CornerRadius, cornerRadius: CornerRadius,
aspectRatio: AspectRatioElementConfig,
image: ImageElementConfig, image: ImageElementConfig,
floating: FloatingElementConfig, floating: FloatingElementConfig,
custom: CustomElementConfig, custom: CustomElementConfig,

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -72,7 +72,8 @@ LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Colo
if clay.UI()({ if clay.UI()({
id = clay.ID("CheckImage", index), id = clay.ID("CheckImage", index),
layout = { sizing = { width = clay.SizingFixed(32) } }, layout = { sizing = { width = clay.SizingFixed(32) } },
image = { imageData = image, sourceDimensions = { 128, 128 } }, aspectRatio = { 1.0 },
image = { imageData = image },
}) {} }) {}
clay.Text(text, clay.TextConfig({fontSize = fontSize, fontId = fontId, textColor = color})) clay.Text(text, clay.TextConfig({fontSize = fontSize, fontId = fontId, textColor = color}))
} }
@ -213,7 +214,8 @@ DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizi
if clay.UI()({ if clay.UI()({
id = clay.ID("SyntaxPageRightImageInner"), id = clay.ID("SyntaxPageRightImageInner"),
layout = { sizing = { width = clay.SizingGrow({ max = 568 }) } }, layout = { sizing = { width = clay.SizingGrow({ max = 568 }) } },
image = { imageData = &syntaxImage, sourceDimensions = { 1136, 1194 } }, aspectRatio = { 1136.0 / 1194.0 },
image = { imageData = &syntaxImage },
}) {} }) {}
} }
} }

241
clay.h
View file

@ -1,4 +1,4 @@
// VERSION: 0.13 // VERSION: 0.14
/* /*
NOTE: In order to use this library you must define NOTE: In order to use this library you must define
@ -31,7 +31,8 @@
#if !( \ #if !( \
(defined(__cplusplus) && __cplusplus >= 202002L) || \ (defined(__cplusplus) && __cplusplus >= 202002L) || \
(defined(__STDC__) && __STDC__ == 1 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ (defined(__STDC__) && __STDC__ == 1 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \
defined(_MSC_VER) \ defined(_MSC_VER) || \
defined(__OBJC__) \
) )
#error "Clay requires C99, C++20, or MSVC" #error "Clay requires C99, C++20, or MSVC"
#endif #endif
@ -184,7 +185,7 @@ extern "C" {
// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string, // Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
// but it is also used to represent slices. // but it is also used to represent slices.
typedef struct { typedef struct Clay_String {
// Set this boolean to true if the char* data underlying this string will live for the entire lifetime of the program. // Set this boolean to true if the char* data underlying this string will live for the entire lifetime of the program.
// This will automatically be set for strings created with CLAY_STRING, as the macro requires a string literal. // This will automatically be set for strings created with CLAY_STRING, as the macro requires a string literal.
bool isStaticallyAllocated; bool isStaticallyAllocated;
@ -195,7 +196,7 @@ typedef struct {
// Clay_StringSlice is used to represent non owning string slices, and includes // Clay_StringSlice is used to represent non owning string slices, and includes
// a baseChars field which points to the string this slice is derived from. // a baseChars field which points to the string this slice is derived from.
typedef struct { typedef struct Clay_StringSlice {
int32_t length; int32_t length;
const char *chars; const char *chars;
const char *baseChars; // The source string / char* that this slice was derived from const char *baseChars; // The source string / char* that this slice was derived from
@ -205,33 +206,33 @@ typedef struct Clay_Context Clay_Context;
// Clay_Arena is a memory arena structure that is used by clay to manage its internal allocations. // Clay_Arena is a memory arena structure that is used by clay to manage its internal allocations.
// Rather than creating it by hand, it's easier to use Clay_CreateArenaWithCapacityAndMemory() // Rather than creating it by hand, it's easier to use Clay_CreateArenaWithCapacityAndMemory()
typedef struct { typedef struct Clay_Arena {
uintptr_t nextAllocation; uintptr_t nextAllocation;
size_t capacity; size_t capacity;
char *memory; char *memory;
} Clay_Arena; } Clay_Arena;
typedef struct { typedef struct Clay_Dimensions {
float width, height; float width, height;
} Clay_Dimensions; } Clay_Dimensions;
typedef struct { typedef struct Clay_Vector2 {
float x, y; float x, y;
} Clay_Vector2; } Clay_Vector2;
// Internally clay conventionally represents colors as 0-255, but interpretation is up to the renderer. // Internally clay conventionally represents colors as 0-255, but interpretation is up to the renderer.
typedef struct { typedef struct Clay_Color {
float r, g, b, a; float r, g, b, a;
} Clay_Color; } Clay_Color;
typedef struct { typedef struct Clay_BoundingBox {
float x, y, width, height; float x, y, width, height;
} Clay_BoundingBox; } Clay_BoundingBox;
// Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros. // Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros.
// Represents a hashed string ID used for identifying and finding specific clay UI elements, required // Represents a hashed string ID used for identifying and finding specific clay UI elements, required
// by functions such as Clay_PointerOver() and Clay_GetElementData(). // by functions such as Clay_PointerOver() and Clay_GetElementData().
typedef struct { typedef struct Clay_ElementId {
uint32_t id; // The resulting hash generated from the other fields. uint32_t id; // The resulting hash generated from the other fields.
uint32_t offset; // A numerical offset applied after computing the hash from stringId. uint32_t offset; // A numerical offset applied after computing the hash from stringId.
uint32_t baseId; // A base hash value to start from, for example the parent element ID is used when calculating CLAY_ID_LOCAL(). uint32_t baseId; // A base hash value to start from, for example the parent element ID is used when calculating CLAY_ID_LOCAL().
@ -248,7 +249,7 @@ typedef struct
// Controls the "radius", or corner rounding of elements, including rectangles, borders and images. // Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels. // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
typedef struct { typedef struct Clay_CornerRadius {
float topLeft; float topLeft;
float topRight; float topRight;
float bottomLeft; float bottomLeft;
@ -298,20 +299,20 @@ typedef CLAY_PACKED_ENUM {
} Clay__SizingType; } Clay__SizingType;
// Controls how child elements are aligned on each axis. // Controls how child elements are aligned on each axis.
typedef struct { typedef struct Clay_ChildAlignment {
Clay_LayoutAlignmentX x; // Controls alignment of children along the x axis. Clay_LayoutAlignmentX x; // Controls alignment of children along the x axis.
Clay_LayoutAlignmentY y; // Controls alignment of children along the y axis. Clay_LayoutAlignmentY y; // Controls alignment of children along the y axis.
} Clay_ChildAlignment; } Clay_ChildAlignment;
// Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to, // 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. // overriding sizing types such as FIT or GROW.
typedef struct { typedef struct Clay_SizingMinMax {
float min; // The smallest final size of the element on this axis will be this value in pixels. float min; // The smallest final size of the element on this axis will be this value in pixels.
float max; // The largest final size of the element on this axis will be this value in pixels. float max; // The largest final size of the element on this axis will be this value in pixels.
} Clay_SizingMinMax; } Clay_SizingMinMax;
// Controls the sizing of this element along one axis inside its parent container. // Controls the sizing of this element along one axis inside its parent container.
typedef struct { typedef struct Clay_SizingAxis {
union { union {
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. 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.
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. 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.
@ -320,14 +321,14 @@ typedef struct {
} Clay_SizingAxis; } Clay_SizingAxis;
// Controls the sizing of this element along one axis inside its parent container. // Controls the sizing of this element along one axis inside its parent container.
typedef struct { typedef struct Clay_Sizing {
Clay_SizingAxis width; // Controls the width sizing of the element, along the x axis. Clay_SizingAxis width; // Controls the width sizing of the element, along the x axis.
Clay_SizingAxis height; // Controls the height sizing of the element, along the y axis. Clay_SizingAxis height; // Controls the height sizing of the element, along the y axis.
} Clay_Sizing; } Clay_Sizing;
// Controls "padding" in pixels, which is a gap between the bounding box of this element and where its children // Controls "padding" in pixels, which is a gap between the bounding box of this element and where its children
// will be placed. // will be placed.
typedef struct { typedef struct Clay_Padding {
uint16_t left; uint16_t left;
uint16_t right; uint16_t right;
uint16_t top; uint16_t top;
@ -338,7 +339,7 @@ CLAY__WRAPPER_STRUCT(Clay_Padding);
// Controls various settings that affect the size and position of an element, as well as the sizes and positions // Controls various settings that affect the size and position of an element, as well as the sizes and positions
// of any child elements. // of any child elements.
typedef struct { typedef struct Clay_LayoutConfig {
Clay_Sizing sizing; // Controls the sizing of this element inside it's parent container, including FIT, GROW, PERCENT and FIXED sizing. Clay_Sizing sizing; // Controls the sizing of this element inside it's parent container, including FIT, GROW, PERCENT and FIXED sizing.
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. 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.
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). 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).
@ -371,7 +372,7 @@ typedef CLAY_PACKED_ENUM {
} Clay_TextAlignment; } Clay_TextAlignment;
// Controls various functionality related to text elements. // Controls various functionality related to text elements.
typedef struct { typedef struct Clay_TextElementConfig {
// A pointer that will be transparently passed through to the resulting render command. // A pointer that will be transparently passed through to the resulting render command.
void *userData; void *userData;
// The RGBA color of the font to render, conventionally specified as 0-255. // The RGBA color of the font to render, conventionally specified as 0-255.
@ -399,12 +400,20 @@ typedef struct {
CLAY__WRAPPER_STRUCT(Clay_TextElementConfig); CLAY__WRAPPER_STRUCT(Clay_TextElementConfig);
// Aspect Ratio --------------------------------
// Controls various settings related to aspect ratio scaling element.
typedef struct Clay_AspectRatioElementConfig {
float aspectRatio; // A float representing the target "Aspect ratio" for an element, which is its final width divided by its final height.
} Clay_AspectRatioElementConfig;
CLAY__WRAPPER_STRUCT(Clay_AspectRatioElementConfig);
// Image -------------------------------- // Image --------------------------------
// Controls various settings related to image elements. // Controls various settings related to image elements.
typedef struct { typedef struct Clay_ImageElementConfig {
void* imageData; // A transparent pointer used to pass image data through to the renderer. void* imageData; // A transparent pointer used to pass image data through to the renderer.
Clay_Dimensions sourceDimensions; // The original dimensions of the source image, used to control aspect ratio.
} Clay_ImageElementConfig; } Clay_ImageElementConfig;
CLAY__WRAPPER_STRUCT(Clay_ImageElementConfig); CLAY__WRAPPER_STRUCT(Clay_ImageElementConfig);
@ -426,7 +435,7 @@ typedef CLAY_PACKED_ENUM {
} Clay_FloatingAttachPointType; } Clay_FloatingAttachPointType;
// Controls where a floating element is offset relative to its parent element. // Controls where a floating element is offset relative to its parent element.
typedef struct { typedef struct Clay_FloatingAttachPoints {
Clay_FloatingAttachPointType element; // Controls the origin point on a floating element that attaches to its parent. Clay_FloatingAttachPointType element; // Controls the origin point on a floating element that attaches to its parent.
Clay_FloatingAttachPointType parent; // Controls the origin point on the parent element that the floating element attaches to. Clay_FloatingAttachPointType parent; // Controls the origin point on the parent element that the floating element attaches to.
} Clay_FloatingAttachPoints; } Clay_FloatingAttachPoints;
@ -463,7 +472,7 @@ typedef CLAY_PACKED_ENUM {
// Controls various settings related to "floating" elements, which are elements that "float" above other elements, potentially overlapping their boundaries, // Controls various settings related to "floating" elements, which are elements that "float" above other elements, potentially overlapping their boundaries,
// and not affecting the layout of sibling or parent elements. // and not affecting the layout of sibling or parent elements.
typedef struct { typedef struct Clay_FloatingElementConfig {
// Offsets this floating element by the provided x,y coordinates from its attachPoints. // Offsets this floating element by the provided x,y coordinates from its attachPoints.
Clay_Vector2 offset; Clay_Vector2 offset;
// Expands the boundaries of the outer floating element without affecting its children. // Expands the boundaries of the outer floating element without affecting its children.
@ -500,7 +509,7 @@ CLAY__WRAPPER_STRUCT(Clay_FloatingElementConfig);
// Custom ----------------------------- // Custom -----------------------------
// Controls various settings related to custom elements. // Controls various settings related to custom elements.
typedef struct { typedef struct Clay_CustomElementConfig {
// A transparent pointer through which you can pass custom data to the renderer. // A transparent pointer through which you can pass custom data to the renderer.
// Generates CUSTOM render commands. // Generates CUSTOM render commands.
void* customData; void* customData;
@ -511,7 +520,7 @@ CLAY__WRAPPER_STRUCT(Clay_CustomElementConfig);
// Scroll ----------------------------- // Scroll -----------------------------
// Controls the axis on which an element switches to "scrolling", which clips the contents and allows scrolling in that direction. // Controls the axis on which an element switches to "scrolling", which clips the contents and allows scrolling in that direction.
typedef struct { typedef struct Clay_ClipElementConfig {
bool horizontal; // Clip overflowing elements on the X axis. bool horizontal; // Clip overflowing elements on the X axis.
bool vertical; // Clip overflowing elements on the Y axis. bool vertical; // Clip overflowing elements on the Y axis.
Clay_Vector2 childOffset; // Offsets the x,y positions of all child elements. Used primarily for scrolling containers. Clay_Vector2 childOffset; // Offsets the x,y positions of all child elements. Used primarily for scrolling containers.
@ -522,7 +531,7 @@ CLAY__WRAPPER_STRUCT(Clay_ClipElementConfig);
// Border ----------------------------- // Border -----------------------------
// Controls the widths of individual element borders. // Controls the widths of individual element borders.
typedef struct { typedef struct Clay_BorderWidth {
uint16_t left; uint16_t left;
uint16_t right; uint16_t right;
uint16_t top; uint16_t top;
@ -534,7 +543,7 @@ typedef struct {
} Clay_BorderWidth; } Clay_BorderWidth;
// Controls settings related to element borders. // Controls settings related to element borders.
typedef struct { typedef struct Clay_BorderElementConfig {
Clay_Color color; // Controls the color of all borders with width > 0. Conventionally represented as 0-255, but interpretation is up to the renderer. Clay_Color color; // Controls the color of all borders with width > 0. Conventionally represented as 0-255, but interpretation is up to the renderer.
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. 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.
} Clay_BorderElementConfig; } Clay_BorderElementConfig;
@ -544,7 +553,7 @@ CLAY__WRAPPER_STRUCT(Clay_BorderElementConfig);
// Render Command Data ----------------------------- // Render Command Data -----------------------------
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT
typedef struct { typedef struct Clay_TextRenderData {
// A string slice containing the text to be rendered. // A string slice containing the text to be rendered.
// Note: this is not guaranteed to be null terminated. // Note: this is not guaranteed to be null terminated.
Clay_StringSlice stringContents; Clay_StringSlice stringContents;
@ -560,7 +569,7 @@ typedef struct {
} Clay_TextRenderData; } Clay_TextRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE
typedef struct { typedef struct Clay_RectangleRenderData {
// The solid background color to fill this rectangle with. Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer. // The solid background color to fill this rectangle with. Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color backgroundColor; Clay_Color backgroundColor;
// Controls the "radius", or corner rounding of elements, including rectangles, borders and images. // Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
@ -569,7 +578,7 @@ typedef struct {
} Clay_RectangleRenderData; } Clay_RectangleRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE
typedef struct { typedef struct Clay_ImageRenderData {
// The tint color for this image. Note that the default value is 0,0,0,0 and should likely be interpreted // The tint color for this image. Note that the default value is 0,0,0,0 and should likely be interpreted
// as "untinted". // as "untinted".
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer. // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
@ -577,14 +586,12 @@ typedef struct {
// Controls the "radius", or corner rounding of this image. // Controls the "radius", or corner rounding of this image.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels. // The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
Clay_CornerRadius cornerRadius; Clay_CornerRadius cornerRadius;
// The original dimensions of the source image, used to control aspect ratio.
Clay_Dimensions sourceDimensions;
// A pointer transparently passed through from the original element definition, typically used to represent image data. // A pointer transparently passed through from the original element definition, typically used to represent image data.
void* imageData; void* imageData;
} Clay_ImageRenderData; } Clay_ImageRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM
typedef struct { typedef struct Clay_CustomRenderData {
// Passed through from .backgroundColor in the original element declaration. // Passed through from .backgroundColor in the original element declaration.
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer. // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color backgroundColor; Clay_Color backgroundColor;
@ -596,13 +603,13 @@ typedef struct {
} Clay_CustomRenderData; } Clay_CustomRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START || commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_END // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START || commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_END
typedef struct { typedef struct Clay_ScrollRenderData {
bool horizontal; bool horizontal;
bool vertical; bool vertical;
} Clay_ClipRenderData; } Clay_ClipRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER
typedef struct { typedef struct Clay_BorderRenderData {
// Controls a shared color for all this element's borders. // Controls a shared color for all this element's borders.
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer. // Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color color; Clay_Color color;
@ -614,7 +621,7 @@ typedef struct {
} Clay_BorderRenderData; } Clay_BorderRenderData;
// A struct union containing data specific to this command's .commandType // A struct union containing data specific to this command's .commandType
typedef union { typedef union Clay_RenderData {
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE
Clay_RectangleRenderData rectangle; Clay_RectangleRenderData rectangle;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT
@ -632,7 +639,7 @@ typedef union {
// Miscellaneous Structs & Enums --------------------------------- // Miscellaneous Structs & Enums ---------------------------------
// Data representing the current internal state of a scrolling element. // Data representing the current internal state of a scrolling element.
typedef struct { typedef struct Clay_ScrollContainerData {
// Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.
// Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.
Clay_Vector2 *scrollPosition; Clay_Vector2 *scrollPosition;
@ -647,7 +654,7 @@ typedef struct {
} Clay_ScrollContainerData; } Clay_ScrollContainerData;
// Bounding box and other data for a specific UI element. // Bounding box and other data for a specific UI element.
typedef struct { typedef struct Clay_ElementData {
// The rectangle that encloses this UI element, with the position relative to the root of the layout. // The rectangle that encloses this UI element, with the position relative to the root of the layout.
Clay_BoundingBox boundingBox; Clay_BoundingBox boundingBox;
// Indicates whether an actual Element matched the provided ID or if the default struct was returned. // Indicates whether an actual Element matched the provided ID or if the default struct was returned.
@ -674,7 +681,7 @@ typedef CLAY_PACKED_ENUM {
CLAY_RENDER_COMMAND_TYPE_CUSTOM, CLAY_RENDER_COMMAND_TYPE_CUSTOM,
} Clay_RenderCommandType; } Clay_RenderCommandType;
typedef struct { typedef struct Clay_RenderCommand {
// A rectangular box that fully encloses this UI element, with the position relative to the root of the layout. // A rectangular box that fully encloses this UI element, with the position relative to the root of the layout.
Clay_BoundingBox boundingBox; Clay_BoundingBox boundingBox;
// A struct union containing data specific to this command's commandType. // A struct union containing data specific to this command's commandType.
@ -699,7 +706,7 @@ typedef struct {
} Clay_RenderCommand; } Clay_RenderCommand;
// A sized array of render commands. // A sized array of render commands.
typedef struct { typedef struct Clay_RenderCommandArray {
// The underlying max capacity of the array, not necessarily all initialized. // The underlying max capacity of the array, not necessarily all initialized.
int32_t capacity; int32_t capacity;
// The number of initialized elements in this array. Used for loops and iteration. // The number of initialized elements in this array. Used for loops and iteration.
@ -721,7 +728,7 @@ typedef CLAY_PACKED_ENUM {
} Clay_PointerDataInteractionState; } Clay_PointerDataInteractionState;
// Information on the current state of pointer interactions this frame. // Information on the current state of pointer interactions this frame.
typedef struct { typedef struct Clay_PointerData {
// The position of the mouse / touch / pointer relative to the root of the layout. // The position of the mouse / touch / pointer relative to the root of the layout.
Clay_Vector2 position; Clay_Vector2 position;
// Represents the current state of interaction with clay this frame. // Represents the current state of interaction with clay this frame.
@ -732,7 +739,7 @@ typedef struct {
Clay_PointerDataInteractionState state; Clay_PointerDataInteractionState state;
} Clay_PointerData; } Clay_PointerData;
typedef struct { typedef struct Clay_ElementDeclaration {
// Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros. // Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros.
// Represents a hashed string ID used for identifying and finding specific clay UI elements, required by functions such as Clay_PointerOver() and Clay_GetElementData(). // Represents a hashed string ID used for identifying and finding specific clay UI elements, required by functions such as Clay_PointerOver() and Clay_GetElementData().
Clay_ElementId id; Clay_ElementId id;
@ -744,6 +751,8 @@ typedef struct {
Clay_Color backgroundColor; Clay_Color backgroundColor;
// Controls the "radius", or corner rounding of elements, including rectangles, borders and images. // Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
Clay_CornerRadius cornerRadius; Clay_CornerRadius cornerRadius;
// Controls settings related to aspect ratio scaling.
Clay_AspectRatioElementConfig aspectRatio;
// Controls settings related to image elements. // Controls settings related to image elements.
Clay_ImageElementConfig image; Clay_ImageElementConfig image;
// 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. // 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.
@ -783,7 +792,7 @@ typedef CLAY_PACKED_ENUM {
} Clay_ErrorType; } Clay_ErrorType;
// Data to identify the error that clay has encountered. // Data to identify the error that clay has encountered.
typedef struct { typedef struct Clay_ErrorData {
// Represents the type of error clay encountered while computing layout. // Represents the type of error clay encountered while computing layout.
// CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED - A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null. // CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED - A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.
// 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(). // 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().
@ -1058,6 +1067,7 @@ CLAY__ARRAY_DEFINE(char, Clay__charArray)
CLAY__ARRAY_DEFINE_FUNCTIONS(Clay_ElementId, Clay_ElementIdArray) CLAY__ARRAY_DEFINE_FUNCTIONS(Clay_ElementId, Clay_ElementIdArray)
CLAY__ARRAY_DEFINE(Clay_LayoutConfig, Clay__LayoutConfigArray) CLAY__ARRAY_DEFINE(Clay_LayoutConfig, Clay__LayoutConfigArray)
CLAY__ARRAY_DEFINE(Clay_TextElementConfig, Clay__TextElementConfigArray) CLAY__ARRAY_DEFINE(Clay_TextElementConfig, Clay__TextElementConfigArray)
CLAY__ARRAY_DEFINE(Clay_AspectRatioElementConfig, Clay__AspectRatioElementConfigArray)
CLAY__ARRAY_DEFINE(Clay_ImageElementConfig, Clay__ImageElementConfigArray) CLAY__ARRAY_DEFINE(Clay_ImageElementConfig, Clay__ImageElementConfigArray)
CLAY__ARRAY_DEFINE(Clay_FloatingElementConfig, Clay__FloatingElementConfigArray) CLAY__ARRAY_DEFINE(Clay_FloatingElementConfig, Clay__FloatingElementConfigArray)
CLAY__ARRAY_DEFINE(Clay_CustomElementConfig, Clay__CustomElementConfigArray) CLAY__ARRAY_DEFINE(Clay_CustomElementConfig, Clay__CustomElementConfigArray)
@ -1072,6 +1082,7 @@ typedef CLAY_PACKED_ENUM {
CLAY__ELEMENT_CONFIG_TYPE_BORDER, CLAY__ELEMENT_CONFIG_TYPE_BORDER,
CLAY__ELEMENT_CONFIG_TYPE_FLOATING, CLAY__ELEMENT_CONFIG_TYPE_FLOATING,
CLAY__ELEMENT_CONFIG_TYPE_CLIP, CLAY__ELEMENT_CONFIG_TYPE_CLIP,
CLAY__ELEMENT_CONFIG_TYPE_ASPECT,
CLAY__ELEMENT_CONFIG_TYPE_IMAGE, CLAY__ELEMENT_CONFIG_TYPE_IMAGE,
CLAY__ELEMENT_CONFIG_TYPE_TEXT, CLAY__ELEMENT_CONFIG_TYPE_TEXT,
CLAY__ELEMENT_CONFIG_TYPE_CUSTOM, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM,
@ -1080,6 +1091,7 @@ typedef CLAY_PACKED_ENUM {
typedef union { typedef union {
Clay_TextElementConfig *textElementConfig; Clay_TextElementConfig *textElementConfig;
Clay_AspectRatioElementConfig *aspectRatioElementConfig;
Clay_ImageElementConfig *imageElementConfig; Clay_ImageElementConfig *imageElementConfig;
Clay_FloatingElementConfig *floatingElementConfig; Clay_FloatingElementConfig *floatingElementConfig;
Clay_CustomElementConfig *customElementConfig; Clay_CustomElementConfig *customElementConfig;
@ -1236,13 +1248,14 @@ struct Clay_Context {
Clay__int32_tArray layoutElementChildren; Clay__int32_tArray layoutElementChildren;
Clay__int32_tArray layoutElementChildrenBuffer; Clay__int32_tArray layoutElementChildrenBuffer;
Clay__TextElementDataArray textElementData; Clay__TextElementDataArray textElementData;
Clay__int32_tArray imageElementPointers; Clay__int32_tArray aspectRatioElementIndexes;
Clay__int32_tArray reusableElementIndexBuffer; Clay__int32_tArray reusableElementIndexBuffer;
Clay__int32_tArray layoutElementClipElementIds; Clay__int32_tArray layoutElementClipElementIds;
// Configs // Configs
Clay__LayoutConfigArray layoutConfigs; Clay__LayoutConfigArray layoutConfigs;
Clay__ElementConfigArray elementConfigs; Clay__ElementConfigArray elementConfigs;
Clay__TextElementConfigArray textElementConfigs; Clay__TextElementConfigArray textElementConfigs;
Clay__AspectRatioElementConfigArray aspectRatioElementConfigs;
Clay__ImageElementConfigArray imageElementConfigs; Clay__ImageElementConfigArray imageElementConfigs;
Clay__FloatingElementConfigArray floatingElementConfigs; Clay__FloatingElementConfigArray floatingElementConfigs;
Clay__ClipElementConfigArray clipElementConfigs; Clay__ClipElementConfigArray clipElementConfigs;
@ -1271,15 +1284,12 @@ struct Clay_Context {
Clay_Context* Clay__Context_Allocate_Arena(Clay_Arena *arena) { Clay_Context* Clay__Context_Allocate_Arena(Clay_Arena *arena) {
size_t totalSizeBytes = sizeof(Clay_Context); size_t totalSizeBytes = sizeof(Clay_Context);
uintptr_t memoryAddress = (uintptr_t)arena->memory; if (totalSizeBytes > arena->capacity)
// Make sure the memory address passed in for clay to use is cache line aligned
uintptr_t nextAllocOffset = (memoryAddress % 64);
if (nextAllocOffset + totalSizeBytes > arena->capacity)
{ {
return NULL; return NULL;
} }
arena->nextAllocation = nextAllocOffset + totalSizeBytes; arena->nextAllocation += totalSizeBytes;
return (Clay_Context*)(memoryAddress + nextAllocOffset); return (Clay_Context*)(arena->memory);
} }
Clay_String Clay__WriteStringToCharBuffer(Clay__charArray *buffer, Clay_String string) { Clay_String Clay__WriteStringToCharBuffer(Clay__charArray *buffer, Clay_String string) {
@ -1310,6 +1320,7 @@ uint32_t Clay__GetParentElementId(void) {
Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay_GetCurrentContext()->layoutConfigs, config); } Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &CLAY_LAYOUT_DEFAULT : Clay__LayoutConfigArray_Add(&Clay_GetCurrentContext()->layoutConfigs, config); }
Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_TextElementConfig_DEFAULT : Clay__TextElementConfigArray_Add(&Clay_GetCurrentContext()->textElementConfigs, config); } Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_TextElementConfig_DEFAULT : Clay__TextElementConfigArray_Add(&Clay_GetCurrentContext()->textElementConfigs, config); }
Clay_AspectRatioElementConfig * Clay__StoreAspectRatioElementConfig(Clay_AspectRatioElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_AspectRatioElementConfig_DEFAULT : Clay__AspectRatioElementConfigArray_Add(&Clay_GetCurrentContext()->aspectRatioElementConfigs, config); }
Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_ImageElementConfig_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay_GetCurrentContext()->imageElementConfigs, config); } Clay_ImageElementConfig * Clay__StoreImageElementConfig(Clay_ImageElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_ImageElementConfig_DEFAULT : Clay__ImageElementConfigArray_Add(&Clay_GetCurrentContext()->imageElementConfigs, config); }
Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_FloatingElementConfig_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay_GetCurrentContext()->floatingElementConfigs, config); } Clay_FloatingElementConfig * Clay__StoreFloatingElementConfig(Clay_FloatingElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_FloatingElementConfig_DEFAULT : Clay__FloatingElementConfigArray_Add(&Clay_GetCurrentContext()->floatingElementConfigs, config); }
Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_CustomElementConfig_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay_GetCurrentContext()->customElementConfigs, config); } Clay_CustomElementConfig * Clay__StoreCustomElementConfig(Clay_CustomElementConfig config) { return Clay_GetCurrentContext()->booleanWarnings.maxElementsExceeded ? &Clay_CustomElementConfig_DEFAULT : Clay__CustomElementConfigArray_Add(&Clay_GetCurrentContext()->customElementConfigs, config); }
@ -1483,7 +1494,7 @@ uint64_t Clay__HashData(const uint8_t* data, size_t length) {
uint64_t Clay__HashData(const uint8_t* data, size_t length) { uint64_t Clay__HashData(const uint8_t* data, size_t length) {
uint64_t hash = 0; uint64_t hash = 0;
for (int32_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
hash += data[i]; hash += data[i];
hash += (hash << 10); hash += (hash << 10);
hash ^= (hash >> 6); hash ^= (hash >> 6);
@ -1632,7 +1643,10 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
char current = text->chars[end]; char current = text->chars[end];
if (current == ' ' || current == '\n') { if (current == ' ' || current == '\n') {
int32_t length = end - start; int32_t length = end - start;
Clay_Dimensions dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = length, .chars = &text->chars[start], .baseChars = text->chars }, config, context->measureTextUserData); Clay_Dimensions dimensions = {};
if (length > 0) {
dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) {.length = length, .chars = &text->chars[start], .baseChars = text->chars}, config, context->measureTextUserData);
}
measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth); measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
measuredHeight = CLAY__MAX(measuredHeight, dimensions.height); measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
if (current == ' ') { if (current == ' ') {
@ -1661,7 +1675,7 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
measuredHeight = CLAY__MAX(measuredHeight, dimensions.height); measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth); measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
} }
measuredWidth = CLAY__MAX(lineWidth, measuredWidth); measuredWidth = CLAY__MAX(lineWidth, measuredWidth) - config->letterSpacing;
measured->measuredWordsStartIndex = tempWord.next; measured->measuredWordsStartIndex = tempWord.next;
measured->unwrappedDimensions.width = measuredWidth; measured->unwrappedDimensions.width = measuredWidth;
@ -1760,16 +1774,15 @@ bool Clay__ElementHasConfig(Clay_LayoutElement *layoutElement, Clay__ElementConf
void Clay__UpdateAspectRatioBox(Clay_LayoutElement *layoutElement) { void Clay__UpdateAspectRatioBox(Clay_LayoutElement *layoutElement) {
for (int32_t j = 0; j < layoutElement->elementConfigs.length; j++) { for (int32_t j = 0; j < layoutElement->elementConfigs.length; j++) {
Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&layoutElement->elementConfigs, j); Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&layoutElement->elementConfigs, j);
if (config->type == CLAY__ELEMENT_CONFIG_TYPE_IMAGE) { if (config->type == CLAY__ELEMENT_CONFIG_TYPE_ASPECT) {
Clay_ImageElementConfig *imageConfig = config->config.imageElementConfig; Clay_AspectRatioElementConfig *aspectConfig = config->config.aspectRatioElementConfig;
if (imageConfig->sourceDimensions.width == 0 || imageConfig->sourceDimensions.height == 0) { if (aspectConfig->aspectRatio == 0) {
break; break;
} }
float aspect = imageConfig->sourceDimensions.width / imageConfig->sourceDimensions.height;
if (layoutElement->dimensions.width == 0 && layoutElement->dimensions.height != 0) { if (layoutElement->dimensions.width == 0 && layoutElement->dimensions.height != 0) {
layoutElement->dimensions.width = layoutElement->dimensions.height * aspect; layoutElement->dimensions.width = layoutElement->dimensions.height * aspectConfig->aspectRatio;
} else if (layoutElement->dimensions.width != 0 && layoutElement->dimensions.height == 0) { } else if (layoutElement->dimensions.width != 0 && layoutElement->dimensions.height == 0) {
layoutElement->dimensions.height = layoutElement->dimensions.height * (1 / aspect); layoutElement->dimensions.height = layoutElement->dimensions.width * (1 / aspectConfig->aspectRatio);
} }
break; break;
} }
@ -1783,13 +1796,13 @@ void Clay__CloseElement(void) {
} }
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement(); Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
Clay_LayoutConfig *layoutConfig = openLayoutElement->layoutConfig; Clay_LayoutConfig *layoutConfig = openLayoutElement->layoutConfig;
bool elementHasScrollHorizontal = false; bool elementHasClipHorizontal = false;
bool elementHasScrollVertical = false; bool elementHasClipVertical = false;
for (int32_t i = 0; i < openLayoutElement->elementConfigs.length; i++) { for (int32_t i = 0; i < openLayoutElement->elementConfigs.length; i++) {
Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&openLayoutElement->elementConfigs, i); Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&openLayoutElement->elementConfigs, i);
if (config->type == CLAY__ELEMENT_CONFIG_TYPE_CLIP) { if (config->type == CLAY__ELEMENT_CONFIG_TYPE_CLIP) {
elementHasScrollHorizontal = config->config.clipElementConfig->horizontal; elementHasClipHorizontal = config->config.clipElementConfig->horizontal;
elementHasScrollVertical = config->config.clipElementConfig->vertical; elementHasClipVertical = config->config.clipElementConfig->vertical;
context->openClipElementStack.length--; context->openClipElementStack.length--;
break; break;
} else if (config->type == CLAY__ELEMENT_CONFIG_TYPE_FLOATING) { } else if (config->type == CLAY__ELEMENT_CONFIG_TYPE_FLOATING) {
@ -1810,19 +1823,21 @@ void Clay__CloseElement(void) {
Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex); Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
openLayoutElement->dimensions.width += child->dimensions.width; openLayoutElement->dimensions.width += child->dimensions.width;
openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + topBottomPadding); openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + topBottomPadding);
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents // Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents
if (!elementHasScrollHorizontal) { if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width += child->minDimensions.width; openLayoutElement->minDimensions.width += child->minDimensions.width;
} }
if (!elementHasScrollVertical) { if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + topBottomPadding); openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + topBottomPadding);
} }
Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex); Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
} }
float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap); float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
openLayoutElement->dimensions.width += childGap; // TODO this is technically a bug with childgap and scroll containers openLayoutElement->dimensions.width += childGap;
if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width += childGap; openLayoutElement->minDimensions.width += childGap;
} }
}
else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) { else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
openLayoutElement->dimensions.height = topBottomPadding; openLayoutElement->dimensions.height = topBottomPadding;
openLayoutElement->minDimensions.height = topBottomPadding; openLayoutElement->minDimensions.height = topBottomPadding;
@ -1831,19 +1846,21 @@ void Clay__CloseElement(void) {
Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex); Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
openLayoutElement->dimensions.height += child->dimensions.height; openLayoutElement->dimensions.height += child->dimensions.height;
openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + leftRightPadding); openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + leftRightPadding);
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents // Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents
if (!elementHasScrollVertical) { if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height += child->minDimensions.height; openLayoutElement->minDimensions.height += child->minDimensions.height;
} }
if (!elementHasScrollHorizontal) { if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + leftRightPadding); openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + leftRightPadding);
} }
Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex); Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
} }
float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap); float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
openLayoutElement->dimensions.height += childGap; // TODO this is technically a bug with childgap and scroll containers openLayoutElement->dimensions.height += childGap;
if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height += childGap; openLayoutElement->minDimensions.height += childGap;
} }
}
context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.children.length; context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.children.length;
@ -2048,7 +2065,10 @@ void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
} }
if (declaration->image.imageData) { if (declaration->image.imageData) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration->image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE); Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration->image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);
Clay__int32_tArray_Add(&context->imageElementPointers, context->layoutElements.length - 1); }
if (declaration->aspectRatio.aspectRatio > 0) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .aspectRatioElementConfig = Clay__StoreAspectRatioElementConfig(declaration->aspectRatio) }, CLAY__ELEMENT_CONFIG_TYPE_ASPECT);
Clay__int32_tArray_Add(&context->aspectRatioElementIndexes, context->layoutElements.length - 1);
} }
if (declaration->floating.attachTo != CLAY_ATTACH_TO_NONE) { if (declaration->floating.attachTo != CLAY_ATTACH_TO_NONE) {
Clay_FloatingElementConfig floatingConfig = declaration->floating; Clay_FloatingElementConfig floatingConfig = declaration->floating;
@ -2145,6 +2165,7 @@ void Clay__InitializeEphemeralMemory(Clay_Context* context) {
context->layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(maxElementCount, arena); context->layoutConfigs = Clay__LayoutConfigArray_Allocate_Arena(maxElementCount, arena);
context->elementConfigs = Clay__ElementConfigArray_Allocate_Arena(maxElementCount, arena); context->elementConfigs = Clay__ElementConfigArray_Allocate_Arena(maxElementCount, arena);
context->textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(maxElementCount, arena); context->textElementConfigs = Clay__TextElementConfigArray_Allocate_Arena(maxElementCount, arena);
context->aspectRatioElementConfigs = Clay__AspectRatioElementConfigArray_Allocate_Arena(maxElementCount, arena);
context->imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(maxElementCount, arena); context->imageElementConfigs = Clay__ImageElementConfigArray_Allocate_Arena(maxElementCount, arena);
context->floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(maxElementCount, arena); context->floatingElementConfigs = Clay__FloatingElementConfigArray_Allocate_Arena(maxElementCount, arena);
context->clipElementConfigs = Clay__ClipElementConfigArray_Allocate_Arena(maxElementCount, arena); context->clipElementConfigs = Clay__ClipElementConfigArray_Allocate_Arena(maxElementCount, arena);
@ -2159,7 +2180,7 @@ void Clay__InitializeEphemeralMemory(Clay_Context* context) {
context->layoutElementChildren = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); context->layoutElementChildren = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
context->openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); context->openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
context->textElementData = Clay__TextElementDataArray_Allocate_Arena(maxElementCount, arena); context->textElementData = Clay__TextElementDataArray_Allocate_Arena(maxElementCount, arena);
context->imageElementPointers = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena); context->aspectRatioElementIndexes = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
context->renderCommands = Clay_RenderCommandArray_Allocate_Arena(maxElementCount, arena); context->renderCommands = Clay_RenderCommandArray_Allocate_Arena(maxElementCount, arena);
context->treeNodeVisited = Clay__boolArray_Allocate_Arena(maxElementCount, arena); context->treeNodeVisited = Clay__boolArray_Allocate_Arena(maxElementCount, arena);
context->treeNodeVisited.length = context->treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list context->treeNodeVisited.length = context->treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list
@ -2248,7 +2269,7 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
if (childSizing.type != CLAY__SIZING_TYPE_PERCENT if (childSizing.type != CLAY__SIZING_TYPE_PERCENT
&& childSizing.type != CLAY__SIZING_TYPE_FIXED && childSizing.type != CLAY__SIZING_TYPE_FIXED
&& (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS)) // todo too many loops && (!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
&& (xAxis || !Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) // && (xAxis || !Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT))
) { ) {
Clay__int32_tArray_Add(&resizableContainerBuffer, childElementIndex); Clay__int32_tArray_Add(&resizableContainerBuffer, childElementIndex);
} }
@ -2287,9 +2308,9 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
// The content is too large, compress the children as much as possible // The content is too large, compress the children as much as possible
if (sizeToDistribute < 0) { if (sizeToDistribute < 0) {
// If the parent clips content in this axis direction, don't compress children, just leave them alone // If the parent clips content in this axis direction, don't compress children, just leave them alone
Clay_ClipElementConfig *scrollElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig; Clay_ClipElementConfig *clipElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP).clipElementConfig;
if (scrollElementConfig) { if (clipElementConfig) {
if (((xAxis && scrollElementConfig->horizontal) || (!xAxis && scrollElementConfig->vertical))) { if (((xAxis && clipElementConfig->horizontal) || (!xAxis && clipElementConfig->vertical))) {
continue; continue;
} }
} }
@ -2382,10 +2403,6 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
float minSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height; float minSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height;
float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) {
continue; // Currently we don't support resizing aspect ratio images on the Y axis because it would break the ratio
}
float maxSize = parentSize - parentPadding; float maxSize = parentSize - parentPadding;
// 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 // 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
if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP)) { if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_CLIP)) {
@ -2500,7 +2517,7 @@ void Clay__CalculateFinalLayout(void) {
// measuredWord->length == 0 means a newline character // measuredWord->length == 0 means a newline character
else if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) { else if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) {
// Wrapped text lines list has overflowed, just render out the line // Wrapped text lines list has overflowed, just render out the line
bool finalCharIsSpace = textElementData->text.chars[lineStartOffset + lineLengthChars - 1] == ' '; bool finalCharIsSpace = textElementData->text.chars[CLAY__MAX(lineStartOffset + lineLengthChars - 1, 0)] == ' ';
Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth + (finalCharIsSpace ? -spaceWidth : 0), lineHeight }, { .length = lineLengthChars + (finalCharIsSpace ? -1 : 0), .chars = &textElementData->text.chars[lineStartOffset] } }); Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth + (finalCharIsSpace ? -spaceWidth : 0), lineHeight }, { .length = lineLengthChars + (finalCharIsSpace ? -1 : 0), .chars = &textElementData->text.chars[lineStartOffset] } });
textElementData->wrappedLines.length++; textElementData->wrappedLines.length++;
if (lineLengthChars == 0 || measuredWord->length == 0) { if (lineLengthChars == 0 || measuredWord->length == 0) {
@ -2510,26 +2527,27 @@ void Clay__CalculateFinalLayout(void) {
lineLengthChars = 0; lineLengthChars = 0;
lineStartOffset = measuredWord->startOffset; lineStartOffset = measuredWord->startOffset;
} else { } else {
lineWidth += measuredWord->width; lineWidth += measuredWord->width + textConfig->letterSpacing;
lineLengthChars += measuredWord->length; lineLengthChars += measuredWord->length;
wordIndex = measuredWord->next; wordIndex = measuredWord->next;
} }
} }
if (lineLengthChars > 0) { if (lineLengthChars > 0) {
Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth, lineHeight }, {.length = lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } }); Clay__WrappedTextLineArray_Add(&context->wrappedTextLines, CLAY__INIT(Clay__WrappedTextLine) { { lineWidth - textConfig->letterSpacing, lineHeight }, {.length = lineLengthChars, .chars = &textElementData->text.chars[lineStartOffset] } });
textElementData->wrappedLines.length++; textElementData->wrappedLines.length++;
} }
containerElement->dimensions.height = lineHeight * (float)textElementData->wrappedLines.length; containerElement->dimensions.height = lineHeight * (float)textElementData->wrappedLines.length;
} }
// Scale vertical image heights according to aspect ratio // Scale vertical heights according to aspect ratio
for (int32_t i = 0; i < context->imageElementPointers.length; ++i) { for (int32_t i = 0; i < context->aspectRatioElementIndexes.length; ++i) {
Clay_LayoutElement* imageElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->imageElementPointers, i)); Clay_LayoutElement* aspectElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->aspectRatioElementIndexes, i));
Clay_ImageElementConfig *config = Clay__FindElementConfigWithType(imageElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE).imageElementConfig; Clay_AspectRatioElementConfig *config = Clay__FindElementConfigWithType(aspectElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
imageElement->dimensions.height = (config->sourceDimensions.height / CLAY__MAX(config->sourceDimensions.width, 1)) * imageElement->dimensions.width; aspectElement->dimensions.height = (1 / config->aspectRatio) * aspectElement->dimensions.width;
aspectElement->layoutConfig->sizing.height.size.minMax.max = aspectElement->dimensions.height;
} }
// Propagate effect of text wrapping, image aspect scaling etc. on height of parents // Propagate effect of text wrapping, aspect scaling etc. on height of parents
Clay__LayoutElementTreeNodeArray dfsBuffer = context->layoutElementTreeNodeArray1; Clay__LayoutElementTreeNodeArray dfsBuffer = context->layoutElementTreeNodeArray1;
dfsBuffer.length = 0; dfsBuffer.length = 0;
for (int32_t i = 0; i < context->layoutElementTreeRoots.length; ++i) { for (int32_t i = 0; i < context->layoutElementTreeRoots.length; ++i) {
@ -2580,6 +2598,13 @@ void Clay__CalculateFinalLayout(void) {
// Calculate sizing along the Y axis // Calculate sizing along the Y axis
Clay__SizeContainersAlongAxis(false); Clay__SizeContainersAlongAxis(false);
// Scale horizontal widths according to aspect ratio
for (int32_t i = 0; i < context->aspectRatioElementIndexes.length; ++i) {
Clay_LayoutElement* aspectElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->aspectRatioElementIndexes, i));
Clay_AspectRatioElementConfig *config = Clay__FindElementConfigWithType(aspectElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
aspectElement->dimensions.width = config->aspectRatio * aspectElement->dimensions.height;
}
// Sort tree roots by z-index // Sort tree roots by z-index
int32_t sortMax = context->layoutElementTreeRoots.length - 1; int32_t sortMax = context->layoutElementTreeRoots.length - 1;
while (sortMax > 0) { // todo dumb bubble sort while (sortMax > 0) { // todo dumb bubble sort
@ -2670,7 +2695,6 @@ void Clay__CalculateFinalLayout(void) {
if (clipConfig->vertical) { if (clipConfig->vertical) {
rootPosition.y += clipConfig->childOffset.y; rootPosition.y += clipConfig->childOffset.y;
} }
break;
} }
Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) { Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
.boundingBox = clipHashMapItem->boundingBox, .boundingBox = clipHashMapItem->boundingBox,
@ -2776,6 +2800,7 @@ void Clay__CalculateFinalLayout(void) {
// 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 // Culling - Don't bother to generate render commands for rectangles entirely outside the screen - this won't stop their children from being rendered if they overflow
bool shouldRender = !offscreen; bool shouldRender = !offscreen;
switch (elementConfig->type) { switch (elementConfig->type) {
case CLAY__ELEMENT_CONFIG_TYPE_ASPECT:
case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: case CLAY__ELEMENT_CONFIG_TYPE_FLOATING:
case CLAY__ELEMENT_CONFIG_TYPE_SHARED: case CLAY__ELEMENT_CONFIG_TYPE_SHARED:
case CLAY__ELEMENT_CONFIG_TYPE_BORDER: { case CLAY__ELEMENT_CONFIG_TYPE_BORDER: {
@ -2798,7 +2823,6 @@ void Clay__CalculateFinalLayout(void) {
.image = { .image = {
.backgroundColor = sharedConfig->backgroundColor, .backgroundColor = sharedConfig->backgroundColor,
.cornerRadius = sharedConfig->cornerRadius, .cornerRadius = sharedConfig->cornerRadius,
.sourceDimensions = elementConfig->config.imageElementConfig->sourceDimensions,
.imageData = elementConfig->config.imageElementConfig->imageData, .imageData = elementConfig->config.imageElementConfig->imageData,
} }
}; };
@ -2907,6 +2931,7 @@ void Clay__CalculateFinalLayout(void) {
default: break; default: break;
} }
currentElementTreeNode->nextChildOffset.x += extraSpace; currentElementTreeNode->nextChildOffset.x += extraSpace;
extraSpace = CLAY__MAX(0, extraSpace);
} else { } else {
for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
@ -2920,6 +2945,7 @@ void Clay__CalculateFinalLayout(void) {
case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break; case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break;
default: break; default: break;
} }
extraSpace = CLAY__MAX(0, extraSpace);
currentElementTreeNode->nextChildOffset.y += extraSpace; currentElementTreeNode->nextChildOffset.y += extraSpace;
} }
@ -3097,6 +3123,7 @@ Clay__DebugElementConfigTypeLabelConfig Clay__DebugGetElementConfigTypeLabel(Cla
switch (type) { switch (type) {
case CLAY__ELEMENT_CONFIG_TYPE_SHARED: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Shared"), {243,134,48,255} }; case CLAY__ELEMENT_CONFIG_TYPE_SHARED: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Shared"), {243,134,48,255} };
case CLAY__ELEMENT_CONFIG_TYPE_TEXT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Text"), {105,210,231,255} }; case CLAY__ELEMENT_CONFIG_TYPE_TEXT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Text"), {105,210,231,255} };
case CLAY__ELEMENT_CONFIG_TYPE_ASPECT: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Aspect"), {101,149,194,255} };
case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Image"), {121,189,154,255} }; case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Image"), {121,189,154,255} };
case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Floating"), {250,105,0,255} }; case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) { CLAY_STRING("Floating"), {250,105,0,255} };
case CLAY__ELEMENT_CONFIG_TYPE_CLIP: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) {CLAY_STRING("Scroll"), {242, 196, 90, 255} }; case CLAY__ELEMENT_CONFIG_TYPE_CLIP: return CLAY__INIT(Clay__DebugElementConfigTypeLabelConfig) {CLAY_STRING("Scroll"), {242, 196, 90, 255} };
@ -3581,21 +3608,25 @@ void Clay__RenderDebugView(void) {
} }
break; break;
} }
case CLAY__ELEMENT_CONFIG_TYPE_ASPECT: {
Clay_AspectRatioElementConfig *aspectRatioConfig = elementConfig->config.aspectRatioElementConfig;
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoAspectRatioBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_TEXT(CLAY_STRING("Aspect Ratio"), infoTitleConfig);
// Aspect Ratio
CLAY_TEXT(Clay__IntToString(aspectRatioConfig->aspectRatio), infoTextConfig);
}
break;
}
case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: {
Clay_ImageElementConfig *imageConfig = elementConfig->config.imageElementConfig; Clay_ImageElementConfig *imageConfig = elementConfig->config.imageElementConfig;
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoImageBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) { Clay_AspectRatioElementConfig aspectConfig = { 1 };
// .sourceDimensions if (Clay__ElementHasConfig(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT)) {
CLAY_TEXT(CLAY_STRING("Source Dimensions"), infoTitleConfig); aspectConfig = *Clay__FindElementConfigWithType(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoImageDimensions") }) {
CLAY_TEXT(CLAY_STRING("{ width: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(imageConfig->sourceDimensions.width), infoTextConfig);
CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(imageConfig->sourceDimensions.height), infoTextConfig);
CLAY_TEXT(CLAY_STRING(" }"), infoTextConfig);
} }
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoImageBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Image Preview // Image Preview
CLAY_TEXT(CLAY_STRING("Preview"), infoTitleConfig); CLAY_TEXT(CLAY_STRING("Preview"), infoTitleConfig);
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0, imageConfig->sourceDimensions.width) }}, .image = *imageConfig }) {} CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(64, 128), .height = CLAY_SIZING_GROW(64, 128) }}, .aspectRatio = aspectConfig, .image = *imageConfig }) {}
} }
break; break;
} }
@ -3793,7 +3824,7 @@ Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning i
void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena) void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena)
{ {
size_t totalSizeBytes = capacity * itemSize; size_t totalSizeBytes = capacity * itemSize;
uintptr_t nextAllocOffset = arena->nextAllocation + (64 - (arena->nextAllocation % 64)); uintptr_t nextAllocOffset = arena->nextAllocation + ((64 - (arena->nextAllocation % 64)) & 63);
if (nextAllocOffset + totalSizeBytes <= arena->capacity) { if (nextAllocOffset + totalSizeBytes <= arena->capacity) {
arena->nextAllocation = nextAllocOffset + totalSizeBytes; arena->nextAllocation = nextAllocOffset + totalSizeBytes;
return (void*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset); return (void*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);
@ -3913,7 +3944,7 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
Clay_BoundingBox elementBox = mapItem->boundingBox; Clay_BoundingBox elementBox = mapItem->boundingBox;
elementBox.x -= root->pointerOffset.x; elementBox.x -= root->pointerOffset.x;
elementBox.y -= root->pointerOffset.y; elementBox.y -= root->pointerOffset.y;
if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)))) { if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)) || context->externalScrollHandlingEnabled)) {
if (mapItem->onHoverFunction) { if (mapItem->onHoverFunction) {
mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData); mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData);
} }
@ -3961,6 +3992,10 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
CLAY_WASM_EXPORT("Clay_Initialize") CLAY_WASM_EXPORT("Clay_Initialize")
Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler) { Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler) {
// Cacheline align memory passed in
uintptr_t baseOffset = 64 - ((uintptr_t)arena.memory % 64);
baseOffset = baseOffset == 64 ? 0 : baseOffset;
arena.memory += baseOffset;
Clay_Context *context = Clay__Context_Allocate_Arena(&arena); Clay_Context *context = Clay__Context_Allocate_Arena(&arena);
if (context == NULL) return NULL; if (context == NULL) return NULL;
// DEFAULTS // DEFAULTS

View file

@ -55,4 +55,5 @@ add_custom_command(
TARGET SDL2_video_demo POST_BUILD TARGET SDL2_video_demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/resources
${CMAKE_CURRENT_BINARY_DIR}/resources) ${CMAKE_CURRENT_BINARY_DIR}/resources
)

View file

@ -26,7 +26,7 @@ set_property(DIRECTORY "${sdl_SOURCE_DIR}" PROPERTY EXCLUDE_FROM_ALL TRUE)
FetchContent_Declare( FetchContent_Declare(
SDL_ttf SDL_ttf
GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git
GIT_TAG main # Slightly risky to use main branch, but it's the only one available GIT_TAG release-3.2.2
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_PROGRESS TRUE GIT_PROGRESS TRUE
) )
@ -38,7 +38,7 @@ set_property(DIRECTORY "${sdl_ttf_SOURCE_DIR}" PROPERTY EXCLUDE_FROM_ALL TRUE)
FetchContent_Declare( FetchContent_Declare(
SDL_image SDL_image
GIT_REPOSITORY "https://github.com/libsdl-org/SDL_image.git" GIT_REPOSITORY "https://github.com/libsdl-org/SDL_image.git"
GIT_TAG release-3.2.0 # Slightly risky to use main branch, but it's the only one available GIT_TAG release-3.2.0
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_PROGRESS TRUE GIT_PROGRESS TRUE
) )

View file

@ -32,6 +32,7 @@ static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextEl
TTF_Font *font = fonts[config->fontId]; TTF_Font *font = fonts[config->fontId];
int width, height; int width, height;
TTF_SetFontSize(font, config->fontSize);
if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) { if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s", SDL_GetError());
} }
@ -65,12 +66,9 @@ Clay_RenderCommandArray ClayImageSample_CreateLayout() {
.layout = { .layout = {
.sizing = layoutExpand .sizing = layoutExpand
}, },
.aspectRatio = { 23.0 / 42.0 },
.image = { .image = {
.imageData = sample_image, .imageData = sample_image,
.sourceDimensions = {
.width = 23,
.height = 42
},
} }
}); });
} }

View file

@ -88,7 +88,7 @@ void Layout() {
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }},
.border = { .color = PRIMARY, .width = 2, 2, 2, 2 }, .cornerRadius = 10 .border = { .color = PRIMARY, .width = 2, 2, 2, 2 }, .cornerRadius = 10
}) { }) {
CLAY({ .layout = { .sizing = { CLAY_SIZING_FIXED(32), CLAY_SIZING_FIXED(32) } }, .image = { .sourceDimensions = { 32, 32 }, .imageData = "resources/check.png" }}); CLAY({ .layout = { .sizing = { CLAY_SIZING_FIXED(32), CLAY_SIZING_FIXED(32) } }, .image = { .imageData = "resources/check.png" }});
} }
} }
} }

View file

@ -97,6 +97,7 @@
{name: 'a', type: 'float' }, {name: 'a', type: 'float' },
]}; ]};
let stringDefinition = { type: 'struct', members: [ let stringDefinition = { type: 'struct', members: [
{name: 'isStaticallyAllocated', type: 'uint32_t'},
{name: 'length', type: 'uint32_t' }, {name: 'length', type: 'uint32_t' },
{name: 'chars', type: 'uint32_t' }, {name: 'chars', type: 'uint32_t' },
]}; ]};
@ -144,7 +145,6 @@
let imageRenderDataDefinition = { type: 'struct', members: [ let imageRenderDataDefinition = { type: 'struct', members: [
{ name: 'backgroundColor', ...colorDefinition }, { name: 'backgroundColor', ...colorDefinition },
{ name: 'cornerRadius', ...cornerRadiusDefinition }, { name: 'cornerRadius', ...cornerRadiusDefinition },
{ name: 'sourceDimensions', ...dimensionsDefinition },
{ name: 'imageData', type: 'uint32_t' }, { name: 'imageData', type: 'uint32_t' },
]}; ]};
let customRenderDataDefinition = { type: 'struct', members: [ let customRenderDataDefinition = { type: 'struct', members: [
@ -158,7 +158,7 @@
{ name: 'width', ...borderWidthDefinition }, { name: 'width', ...borderWidthDefinition },
{ name: 'padding', type: 'uint16_t'} { name: 'padding', type: 'uint16_t'}
]}; ]};
let scrollRenderDataDefinition = { type: 'struct', members: [ let clipRenderDataDefinition = { type: 'struct', members: [
{ name: 'horizontal', type: 'bool' }, { name: 'horizontal', type: 'bool' },
{ name: 'vertical', type: 'bool' }, { name: 'vertical', type: 'bool' },
]}; ]};
@ -166,9 +166,10 @@
{ name: 'link', ...stringDefinition }, { name: 'link', ...stringDefinition },
{ name: 'cursorPointer', type: 'uint8_t' }, { name: 'cursorPointer', type: 'uint8_t' },
{ name: 'disablePointerEvents', type: 'uint8_t' }, { name: 'disablePointerEvents', type: 'uint8_t' },
{ name: 'padding', type: 'uint16_t'}
]}; ]};
let renderCommandDefinition = { let renderCommandDefinition = {
name: 'CLay_RenderCommand', name: 'Clay_RenderCommand',
type: 'struct', type: 'struct',
members: [ members: [
{ name: 'boundingBox', type: 'struct', members: [ { name: 'boundingBox', type: 'struct', members: [
@ -183,7 +184,7 @@
{ name: 'image', ...imageRenderDataDefinition }, { name: 'image', ...imageRenderDataDefinition },
{ name: 'custom', ...customRenderDataDefinition }, { name: 'custom', ...customRenderDataDefinition },
{ name: 'border', ...borderRenderDataDefinition }, { name: 'border', ...borderRenderDataDefinition },
{ name: 'scroll', ...scrollRenderDataDefinition }, { name: 'clip', ...clipRenderDataDefinition },
]}, ]},
{ name: 'userData', type: 'uint32_t'}, { name: 'userData', type: 'uint32_t'},
{ name: 'id', type: 'uint32_t' }, { name: 'id', type: 'uint32_t' },
@ -380,7 +381,7 @@
memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true);
memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true);
instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value);
instance.exports.SetScratchMemory(arenaAddress, clayScratchSpaceAddress); instance.exports.SetScratchMemory(clayScratchSpaceAddress);
renderCommandSize = getStructTotalSize(renderCommandDefinition); renderCommandSize = getStructTotalSize(renderCommandDefinition);
renderLoop(); renderLoop();
} }
@ -425,10 +426,13 @@
if (!elementCache[renderCommand.id.value]) { if (!elementCache[renderCommand.id.value]) {
let elementType = 'div'; let elementType = 'div';
switch (renderCommand.commandType.value & 0xff) { switch (renderCommand.commandType.value & 0xff) {
case CLAY_RENDER_COMMAND_TYPE_TEXT:
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
// if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links if (renderCommand.userData.value !== 0) {
// elementType = 'a'; if (readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition).link.length.value > 0) {
// } elementType = 'a';
}
}
break; break;
} }
case CLAY_RENDER_COMMAND_TYPE_IMAGE: { case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
@ -549,7 +553,6 @@
} }
case (CLAY_RENDER_COMMAND_TYPE_TEXT): { case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = renderCommand.renderData.text; let config = renderCommand.renderData.text;
let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
let configMemory = JSON.stringify(config); let configMemory = JSON.stringify(config);
let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value)); let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));
if (configMemory !== elementData.previousMemoryConfig) { if (configMemory !== elementData.previousMemoryConfig) {
@ -559,7 +562,23 @@
element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`; element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;
element.style.fontFamily = fontsById[config.fontId.value]; element.style.fontFamily = fontsById[config.fontId.value];
element.style.fontSize = fontSize + 'px'; element.style.fontSize = fontSize + 'px';
if (renderCommand.userData.value !== 0) {
let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all'; element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';
let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;
memoryDataView.setUint32(0, renderCommand.id.value, true);
if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {
window.location.href = linkContents;
}
if (linkContents.length > 0) {
element.href = linkContents;
}
if (linkContents.length > 0 || customData.cursorPointer.value) {
element.style.pointerEvents = 'all';
element.style.cursor = 'pointer';
}
}
elementData.previousMemoryConfig = configMemory; elementData.previousMemoryConfig = configMemory;
} }
if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) { if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {
@ -570,7 +589,7 @@
} }
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {
scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 }); scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 });
let config = renderCommand.renderData.scroll; let config = renderCommand.renderData.clip;
let configMemory = JSON.stringify(config); let configMemory = JSON.stringify(config);
if (configMemory === elementData.previousMemoryConfig) { if (configMemory === elementData.previousMemoryConfig) {
break; break;

View file

@ -97,6 +97,7 @@
{name: 'a', type: 'float' }, {name: 'a', type: 'float' },
]}; ]};
let stringDefinition = { type: 'struct', members: [ let stringDefinition = { type: 'struct', members: [
{name: 'isStaticallyAllocated', type: 'uint32_t'},
{name: 'length', type: 'uint32_t' }, {name: 'length', type: 'uint32_t' },
{name: 'chars', type: 'uint32_t' }, {name: 'chars', type: 'uint32_t' },
]}; ]};
@ -144,7 +145,6 @@
let imageRenderDataDefinition = { type: 'struct', members: [ let imageRenderDataDefinition = { type: 'struct', members: [
{ name: 'backgroundColor', ...colorDefinition }, { name: 'backgroundColor', ...colorDefinition },
{ name: 'cornerRadius', ...cornerRadiusDefinition }, { name: 'cornerRadius', ...cornerRadiusDefinition },
{ name: 'sourceDimensions', ...dimensionsDefinition },
{ name: 'imageData', type: 'uint32_t' }, { name: 'imageData', type: 'uint32_t' },
]}; ]};
let customRenderDataDefinition = { type: 'struct', members: [ let customRenderDataDefinition = { type: 'struct', members: [
@ -158,7 +158,7 @@
{ name: 'width', ...borderWidthDefinition }, { name: 'width', ...borderWidthDefinition },
{ name: 'padding', type: 'uint16_t'} { name: 'padding', type: 'uint16_t'}
]}; ]};
let scrollRenderDataDefinition = { type: 'struct', members: [ let clipRenderDataDefinition = { type: 'struct', members: [
{ name: 'horizontal', type: 'bool' }, { name: 'horizontal', type: 'bool' },
{ name: 'vertical', type: 'bool' }, { name: 'vertical', type: 'bool' },
]}; ]};
@ -166,9 +166,10 @@
{ name: 'link', ...stringDefinition }, { name: 'link', ...stringDefinition },
{ name: 'cursorPointer', type: 'uint8_t' }, { name: 'cursorPointer', type: 'uint8_t' },
{ name: 'disablePointerEvents', type: 'uint8_t' }, { name: 'disablePointerEvents', type: 'uint8_t' },
{ name: 'padding', type: 'uint16_t'}
]}; ]};
let renderCommandDefinition = { let renderCommandDefinition = {
name: 'CLay_RenderCommand', name: 'Clay_RenderCommand',
type: 'struct', type: 'struct',
members: [ members: [
{ name: 'boundingBox', type: 'struct', members: [ { name: 'boundingBox', type: 'struct', members: [
@ -183,7 +184,7 @@
{ name: 'image', ...imageRenderDataDefinition }, { name: 'image', ...imageRenderDataDefinition },
{ name: 'custom', ...customRenderDataDefinition }, { name: 'custom', ...customRenderDataDefinition },
{ name: 'border', ...borderRenderDataDefinition }, { name: 'border', ...borderRenderDataDefinition },
{ name: 'scroll', ...scrollRenderDataDefinition }, { name: 'clip', ...clipRenderDataDefinition },
]}, ]},
{ name: 'userData', type: 'uint32_t'}, { name: 'userData', type: 'uint32_t'},
{ name: 'id', type: 'uint32_t' }, { name: 'id', type: 'uint32_t' },
@ -380,7 +381,7 @@
memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true);
memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true);
instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value);
instance.exports.SetScratchMemory(arenaAddress, clayScratchSpaceAddress); instance.exports.SetScratchMemory(clayScratchSpaceAddress);
renderCommandSize = getStructTotalSize(renderCommandDefinition); renderCommandSize = getStructTotalSize(renderCommandDefinition);
renderLoop(); renderLoop();
} }
@ -425,10 +426,13 @@
if (!elementCache[renderCommand.id.value]) { if (!elementCache[renderCommand.id.value]) {
let elementType = 'div'; let elementType = 'div';
switch (renderCommand.commandType.value & 0xff) { switch (renderCommand.commandType.value & 0xff) {
case CLAY_RENDER_COMMAND_TYPE_TEXT:
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
// if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links if (renderCommand.userData.value !== 0) {
// elementType = 'a'; if (readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition).link.length.value > 0) {
// } elementType = 'a';
}
}
break; break;
} }
case CLAY_RENDER_COMMAND_TYPE_IMAGE: { case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
@ -549,7 +553,6 @@
} }
case (CLAY_RENDER_COMMAND_TYPE_TEXT): { case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = renderCommand.renderData.text; let config = renderCommand.renderData.text;
let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
let configMemory = JSON.stringify(config); let configMemory = JSON.stringify(config);
let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value)); let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));
if (configMemory !== elementData.previousMemoryConfig) { if (configMemory !== elementData.previousMemoryConfig) {
@ -559,7 +562,23 @@
element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`; element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;
element.style.fontFamily = fontsById[config.fontId.value]; element.style.fontFamily = fontsById[config.fontId.value];
element.style.fontSize = fontSize + 'px'; element.style.fontSize = fontSize + 'px';
if (renderCommand.userData.value !== 0) {
let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all'; element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';
let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0;
memoryDataView.setUint32(0, renderCommand.id.value, true);
if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) {
window.location.href = linkContents;
}
if (linkContents.length > 0) {
element.href = linkContents;
}
if (linkContents.length > 0 || customData.cursorPointer.value) {
element.style.pointerEvents = 'all';
element.style.cursor = 'pointer';
}
}
elementData.previousMemoryConfig = configMemory; elementData.previousMemoryConfig = configMemory;
} }
if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) { if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {
@ -570,7 +589,7 @@
} }
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {
scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 }); scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 });
let config = renderCommand.renderData.scroll; let config = renderCommand.renderData.clip;
let configMemory = JSON.stringify(config); let configMemory = JSON.stringify(config);
if (configMemory === elementData.previousMemoryConfig) { if (configMemory === elementData.previousMemoryConfig) {
break; break;

View file

@ -44,7 +44,7 @@ typedef struct {
Arena frameArena = {}; Arena frameArena = {};
typedef struct { typedef struct d {
Clay_String link; Clay_String link;
bool cursorPointer; bool cursorPointer;
bool disablePointerEvents; bool disablePointerEvents;
@ -66,7 +66,7 @@ Clay_String* FrameAllocateString(Clay_String string) {
void LandingPageBlob(int index, int fontSize, Clay_Color color, Clay_String text, Clay_String imageURL) { void LandingPageBlob(int index, int fontSize, Clay_Color color, Clay_String text, Clay_String imageURL) {
CLAY({ .id = CLAY_IDI("HeroBlob", index), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 480) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .border = { .color = color, .width = { 2, 2, 2, 2 }}, .cornerRadius = CLAY_CORNER_RADIUS(10) }) { CLAY({ .id = CLAY_IDI("HeroBlob", index), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 480) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .border = { .color = color, .width = { 2, 2, 2, 2 }}, .cornerRadius = CLAY_CORNER_RADIUS(10) }) {
CLAY({ .id = CLAY_IDI("CheckImage", index), .layout = { .sizing = { CLAY_SIZING_FIXED(32) } }, .image = { .sourceDimensions = { 128, 128 }, .imageData = FrameAllocateString(imageURL) } }) {} CLAY({ .id = CLAY_IDI("CheckImage", index), .layout = { .sizing = { CLAY_SIZING_FIXED(32) } }, .aspectRatio = { 1 }, .image = { .imageData = FrameAllocateString(imageURL) } }) {}
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color })); CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color }));
} }
} }
@ -156,7 +156,7 @@ void DeclarativeSyntaxPageDesktop() {
CLAY_TEXT(CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); CLAY_TEXT(CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
} }
CLAY({ .id = CLAY_ID("SyntaxPageRightImage"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) { CLAY({ .id = CLAY_ID("SyntaxPageRightImage"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) {
CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .image = { .sourceDimensions = {1136, 1194}, .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {} CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
} }
} }
} }
@ -172,7 +172,7 @@ void DeclarativeSyntaxPageMobile() {
CLAY_TEXT(CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); CLAY_TEXT(CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
} }
CLAY({ .id = CLAY_ID("SyntaxPageRightImage"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) { CLAY({ .id = CLAY_ID("SyntaxPageRightImage"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) {
CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .image = { .sourceDimensions = {1136, 1194}, .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {} CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
} }
} }
} }
@ -323,7 +323,7 @@ void DebuggerPageDesktop() {
CLAY_TEXT(CLAY_STRING("Press the \"d\" key to try it out now!"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE })); CLAY_TEXT(CLAY_STRING("Press the \"d\" key to try it out now!"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE }));
} }
CLAY({ .id = CLAY_ID("DebuggerRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) { CLAY({ .id = CLAY_ID("DebuggerRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
CLAY({ .id = CLAY_ID("DebuggerPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .image = { .sourceDimensions = {1620, 1474}, .imageData = FrameAllocateString(CLAY_STRING("/clay/images/debugger.png")) } }) {} CLAY({ .id = CLAY_ID("DebuggerPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .aspectRatio = { 1620.0 / 1474.0 }, .image = {.imageData = FrameAllocateString(CLAY_STRING("/clay/images/debugger.png")) } }) {}
} }
} }
} }
@ -345,11 +345,18 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
CLAY_TEXT(CLAY_STRING("Clay"), &headerTextConfig); CLAY_TEXT(CLAY_STRING("Clay"), &headerTextConfig);
CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {} CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
if (!mobileScreen) { if (!mobileScreen) {
CLAY({ .id = CLAY_ID("LinkExamplesOuter"), .layout = { .padding = {8, 8} }, .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples") }) }) { CLAY({ .id = CLAY_ID("LinkExamplesOuter"), .layout = { .padding = {8, 8} } }) {
CLAY_TEXT(CLAY_STRING("Examples"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); CLAY_TEXT(CLAY_STRING("Examples"), CLAY_TEXT_CONFIG({
.userData = FrameAllocateCustomData((CustomHTMLData) {
.link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples")
}),
.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
} }
CLAY({ .id = CLAY_ID("LinkDocsOuter"), .layout = { .padding = {8, 8} }, .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md") }) }) { CLAY({ .id = CLAY_ID("LinkDocsOuter"), .layout = { .padding = {8, 8} } }) {
CLAY_TEXT(CLAY_STRING("Docs"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); CLAY_TEXT(CLAY_STRING("Docs"), CLAY_TEXT_CONFIG({
.userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md") }),
.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })
);
} }
} }
CLAY({ CLAY({
@ -357,9 +364,11 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.cornerRadius = CLAY_CORNER_RADIUS(10), .cornerRadius = CLAY_CORNER_RADIUS(10),
.userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples") }), .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://discord.gg/b4FTWkxdvT") }),
}) { }) {
CLAY_TEXT(CLAY_STRING("Discord"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); CLAY_TEXT(CLAY_STRING("Discord"), CLAY_TEXT_CONFIG({
.userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true }),
.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
} }
CLAY({ CLAY({
.layout = { .padding = {16, 16, 6, 6} }, .layout = { .padding = {16, 16, 6, 6} },
@ -368,7 +377,9 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
.cornerRadius = CLAY_CORNER_RADIUS(10), .cornerRadius = CLAY_CORNER_RADIUS(10),
.userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay") }), .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay") }),
}) { }) {
CLAY_TEXT(CLAY_STRING("Github"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); CLAY_TEXT(CLAY_STRING("Github"), CLAY_TEXT_CONFIG({
.userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true }),
.fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
} }
} }
Clay_LayoutConfig topBorderConfig = (Clay_LayoutConfig) { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(4) }}; Clay_LayoutConfig topBorderConfig = (Clay_LayoutConfig) { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(4) }};

File diff suppressed because one or more lines are too long

View file

@ -23,6 +23,7 @@ static Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementC
SDL2_Font *fonts = (SDL2_Font*)userData; SDL2_Font *fonts = (SDL2_Font*)userData;
TTF_Font *font = fonts[config->fontId].font; TTF_Font *font = fonts[config->fontId].font;
TTF_SetFontSize(font, config->fontSize);
char *chars = (char *)calloc(text.length + 1, 1); char *chars = (char *)calloc(text.length + 1, 1);
memcpy(chars, text.chars, text.length); memcpy(chars, text.chars, text.length);
int width = 0; int width = 0;
@ -294,6 +295,7 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
char *cloned = (char *)calloc(config->stringContents.length + 1, 1); char *cloned = (char *)calloc(config->stringContents.length + 1, 1);
memcpy(cloned, config->stringContents.chars, config->stringContents.length); memcpy(cloned, config->stringContents.chars, config->stringContents.length);
TTF_Font* font = fonts[config->fontId].font; TTF_Font* font = fonts[config->fontId].font;
TTF_SetFontSize(font, config->fontSize);
SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) { SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) {
.r = (Uint8)config->textColor.r, .r = (Uint8)config->textColor.r,
.g = (Uint8)config->textColor.g, .g = (Uint8)config->textColor.g,

View file

@ -164,6 +164,7 @@ static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Cla
case CLAY_RENDER_COMMAND_TYPE_TEXT: { case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *config = &rcmd->renderData.text; Clay_TextRenderData *config = &rcmd->renderData.text;
TTF_Font *font = rendererData->fonts[config->fontId]; TTF_Font *font = rendererData->fonts[config->fontId];
TTF_SetFontSize(font, config->fontSize);
TTF_Text *text = TTF_CreateText(rendererData->textEngine, font, config->stringContents.chars, config->stringContents.length); TTF_Text *text = TTF_CreateText(rendererData->textEngine, font, config->stringContents.chars, config->stringContents.length);
TTF_SetTextColor(text, config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a); TTF_SetTextColor(text, config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a);
TTF_DrawRendererText(text, rect.x, rect.y); TTF_DrawRendererText(text, rect.x, rect.y);
@ -184,11 +185,11 @@ static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Cla
if (config->width.left > 0) { if (config->width.left > 0) {
const float starting_y = rect.y + clampedRadii.topLeft; const float starting_y = rect.y + clampedRadii.topLeft;
const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft; const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft;
SDL_FRect line = { rect.x, starting_y, config->width.left, length }; SDL_FRect line = { rect.x - 1, starting_y, config->width.left, length };
SDL_RenderFillRect(rendererData->renderer, &line); SDL_RenderFillRect(rendererData->renderer, &line);
} }
if (config->width.right > 0) { if (config->width.right > 0) {
const float starting_x = rect.x + rect.w - (float)config->width.right; const float starting_x = rect.x + rect.w - (float)config->width.right + 1;
const float starting_y = rect.y + clampedRadii.topRight; const float starting_y = rect.y + clampedRadii.topRight;
const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight; const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight;
SDL_FRect line = { starting_x, starting_y, config->width.right, length }; SDL_FRect line = { starting_x, starting_y, config->width.right, length };
@ -197,12 +198,12 @@ static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Cla
if (config->width.top > 0) { if (config->width.top > 0) {
const float starting_x = rect.x + clampedRadii.topLeft; const float starting_x = rect.x + clampedRadii.topLeft;
const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight; const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight;
SDL_FRect line = { starting_x, rect.y, length, config->width.top }; SDL_FRect line = { starting_x, rect.y - 1, length, config->width.top };
SDL_RenderFillRect(rendererData->renderer, &line); SDL_RenderFillRect(rendererData->renderer, &line);
} }
if (config->width.bottom > 0) { if (config->width.bottom > 0) {
const float starting_x = rect.x + clampedRadii.bottomLeft; const float starting_x = rect.x + clampedRadii.bottomLeft;
const float starting_y = rect.y + rect.h - (float)config->width.bottom; const float starting_y = rect.y + rect.h - (float)config->width.bottom + 1;
const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight; const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight;
SDL_FRect line = { starting_x, starting_y, length, config->width.bottom }; SDL_FRect line = { starting_x, starting_y, length, config->width.bottom };
SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a); SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);
@ -211,25 +212,25 @@ static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Cla
//corners //corners
if (config->cornerRadius.topLeft > 0) { if (config->cornerRadius.topLeft > 0) {
const float centerX = rect.x + clampedRadii.topLeft -1; const float centerX = rect.x + clampedRadii.topLeft -1;
const float centerY = rect.y + clampedRadii.topLeft; const float centerY = rect.y + clampedRadii.topLeft - 1;
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft,
180.0f, 270.0f, config->width.top, config->color); 180.0f, 270.0f, config->width.top, config->color);
} }
if (config->cornerRadius.topRight > 0) { if (config->cornerRadius.topRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.topRight -1; const float centerX = rect.x + rect.w - clampedRadii.topRight;
const float centerY = rect.y + clampedRadii.topRight; const float centerY = rect.y + clampedRadii.topRight - 1;
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,
270.0f, 360.0f, config->width.top, config->color); 270.0f, 360.0f, config->width.top, config->color);
} }
if (config->cornerRadius.bottomLeft > 0) { if (config->cornerRadius.bottomLeft > 0) {
const float centerX = rect.x + clampedRadii.bottomLeft -1; const float centerX = rect.x + clampedRadii.bottomLeft -1;
const float centerY = rect.y + rect.h - clampedRadii.bottomLeft -1; const float centerY = rect.y + rect.h - clampedRadii.bottomLeft;
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft,
90.0f, 180.0f, config->width.bottom, config->color); 90.0f, 180.0f, config->width.bottom, config->color);
} }
if (config->cornerRadius.bottomRight > 0) { if (config->cornerRadius.bottomRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.bottomRight -1; //TODO: why need to -1 in all calculations??? const float centerX = rect.x + rect.w - clampedRadii.bottomRight;
const float centerY = rect.y + rect.h - clampedRadii.bottomRight -1; const float centerY = rect.y + rect.h - clampedRadii.bottomRight;
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,
0.0f, 90.0f, config->width.bottom, config->color); 0.0f, 90.0f, config->width.bottom, config->color);
} }

View file

@ -87,6 +87,8 @@ static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_Tex
float maxTextWidth = 0.0f; float maxTextWidth = 0.0f;
float lineTextWidth = 0; float lineTextWidth = 0;
int maxLineCharCount = 0;
int lineCharCount = 0;
float textHeight = config->fontSize; float textHeight = config->fontSize;
Font* fonts = (Font*)userData; Font* fonts = (Font*)userData;
@ -99,11 +101,13 @@ static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_Tex
float scaleFactor = config->fontSize/(float)fontToUse.baseSize; float scaleFactor = config->fontSize/(float)fontToUse.baseSize;
for (int i = 0; i < text.length; ++i) for (int i = 0; i < text.length; ++i, lineCharCount++)
{ {
if (text.chars[i] == '\n') { if (text.chars[i] == '\n') {
maxTextWidth = fmax(maxTextWidth, lineTextWidth); maxTextWidth = fmax(maxTextWidth, lineTextWidth);
maxLineCharCount = CLAY__MAX(maxLineCharCount, lineCharCount);
lineTextWidth = 0; lineTextWidth = 0;
lineCharCount = 0;
continue; continue;
} }
int index = text.chars[i] - 32; int index = text.chars[i] - 32;
@ -112,8 +116,9 @@ static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_Tex
} }
maxTextWidth = fmax(maxTextWidth, lineTextWidth); maxTextWidth = fmax(maxTextWidth, lineTextWidth);
maxLineCharCount = CLAY__MAX(maxLineCharCount, lineCharCount);
textSize.width = maxTextWidth * scaleFactor; textSize.width = maxTextWidth * scaleFactor + (lineCharCount * config->letterSpacing);
textSize.height = textHeight; textSize.height = textHeight;
return textSize; return textSize;
@ -145,7 +150,7 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)
for (int j = 0; j < renderCommands.length; j++) for (int j = 0; j < renderCommands.length; j++)
{ {
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j); Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);
Clay_BoundingBox boundingBox = renderCommand->boundingBox; Clay_BoundingBox boundingBox = {roundf(renderCommand->boundingBox.x), roundf(renderCommand->boundingBox.y), roundf(renderCommand->boundingBox.width), roundf(renderCommand->boundingBox.height)};
switch (renderCommand->commandType) switch (renderCommand->commandType)
{ {
case CLAY_RENDER_COMMAND_TYPE_TEXT: { case CLAY_RENDER_COMMAND_TYPE_TEXT: {
@ -174,11 +179,12 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)
if (tintColor.r == 0 && tintColor.g == 0 && tintColor.b == 0 && tintColor.a == 0) { if (tintColor.r == 0 && tintColor.g == 0 && tintColor.b == 0 && tintColor.a == 0) {
tintColor = (Clay_Color) { 255, 255, 255, 255 }; tintColor = (Clay_Color) { 255, 255, 255, 255 };
} }
DrawTextureEx( DrawTexturePro(
imageTexture, imageTexture,
(Vector2){boundingBox.x, boundingBox.y}, (Rectangle) { 0, 0, imageTexture.width, imageTexture.height },
(Rectangle){boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height},
(Vector2) {},
0, 0,
boundingBox.width / (float)imageTexture.width,
CLAY_COLOR_TO_RAYLIB_COLOR(tintColor)); CLAY_COLOR_TO_RAYLIB_COLOR(tintColor));
break; break;
} }