Compare commits

...

44 commits
v0.14 ... main

Author SHA1 Message Date
Daniel Mayovskiy fd97d8179e
[Renderers/termbox] fixed horizontal text culling bug (#525)
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / check_changes (push) Has been cancelled
Odin Bindings Update / build (macos-latest) (push) Has been cancelled
Odin Bindings Update / build (ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / commit (push) Has been cancelled
2025-10-23 12:58:39 +11:00
Daniel Mayovskiy 7216815536
Fixed termbox2 demo build, added scroll functionality (#523) 2025-10-23 12:57:11 +11:00
Thomas Anderson 83129995f7
[Examples/official-website] updated paths in build.sh 2025-10-23 12:56:20 +11:00
Daniel Mayovskiy 588b93196c
[Renderers/termbox] Fixing termbox2-image-demo build error (#524)
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / check_changes (push) Has been cancelled
Odin Bindings Update / build (macos-latest) (push) Has been cancelled
Odin Bindings Update / build (ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / commit (push) Has been cancelled
2025-10-02 11:21:11 +10:00
github-actions[bot] 382dcde89d [bindings/odin] Update Odin bindings 2025-10-02 01:17:05 +00:00
elmfrain c6442bd192
[Bug Fix] Multiple Floating Elements Cannot Use Clay_Hovered() (#461)
Co-authored-by: Nic Barker <contact+github@nicbarker.com>
2025-10-02 11:14:17 +10:00
Nic Barker 7874cdb085 Fix README update
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / check_changes (push) Has been cancelled
Odin Bindings Update / build (macos-latest) (push) Has been cancelled
Odin Bindings Update / build (ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / commit (push) Has been cancelled
2025-09-29 13:27:40 +10:00
github-actions[bot] 37675089e3 [bindings/odin] Update Odin bindings 2025-09-16 01:40:04 +00:00
Nic Barker 1cbc56cbf2
[Core] Remove ID from element declaration struct and split clay macro into CLAY and CLAY_WITHID (#492) 2025-09-16 11:39:07 +10:00
github-actions[bot] 0d6969c8c3 [bindings/odin] Update Odin bindings 2025-09-08 02:15:33 +00:00
Víctor López 958d684b3c
[Compilers] Fix struct with default initializer not using CLAY__DEFAULT_STRUCT (#498) 2025-09-08 12:14:05 +10:00
Rats f55513493b
[Bindings/Odin] Fix ID procedure ignoring index (#496) 2025-09-08 12:13:00 +10:00
github-actions[bot] dabf8214a8 [bindings/odin] Update Odin bindings 2025-09-08 02:12:08 +00:00
fgungor 747643e84e
[Core] fix unused extra macro param in CLAY_SID_LOCAL (#505) 2025-09-08 12:10:10 +10:00
rivten 5a0d301c60
[Renderers/Sokol] allow usage of images with sokol renderer (#489)
Co-authored-by: Hugo Viala <h.viala@ganacos.com>
Co-authored-by: Andrew Sampson <1297077+andrewmd5@users.noreply.github.com>
2025-09-08 11:35:18 +10:00
Andrew Sampson 1bc5105272
[Renderers/Sokol] Text measurement/rendering width mismatch in sokol_clay (#480) 2025-08-19 12:14:13 +10:00
github-actions[bot] 61bab7bba6 [bindings/odin] Update Odin bindings 2025-08-14 23:38:20 +00:00
Nic Barker 7f767d2301 [Core] Increase the default scroll container limit to 100 2025-08-15 09:37:23 +10:00
Daniel S Jeremiah 2b192409b9
[Documentation] -- updated README (#475) 2025-08-14 10:02:23 +10:00
github-actions[bot] 91c6d05774 [bindings/odin] Update Odin bindings 2025-07-15 23:32:23 +00:00
Nic Barker 4aa3d75bef [Core] Split base ID hash from index ID hash 2025-07-16 09:30:54 +10:00
Hayden Gray 9d38edb989
[Bindings/Odin] SizingFit and SizingGrow default parameters (#462) 2025-07-09 09:07:10 +10:00
github-actions[bot] 516e85bdfe [bindings/odin] Update Odin bindings 2025-07-01 23:47:56 +00:00
Tomás Ralph 0022d12c0c
[DebugTools] Fix aspect ratio display, closes #449 (#451) 2025-07-02 09:46:12 +10:00
Nic Barker d9d0b6c37b [Core] Fix incorrect percentage sizing of floating elements 2025-07-02 09:37:38 +10:00
Hayden Gray 8dfcc944fa
[Bindings/Odin] - add CI to update binding libs (#452) 2025-06-27 15:05:13 +10:00
Mivirl a9c1f9a8a7
[Renderers/termbox2] Termbox2 renderer & examples (#419) 2025-06-27 08:26:38 +10:00
Nic Barker 281f961e3d [Bindings/Odin] Update Odin bindings 2025-06-24 13:57:15 +10:00
Nic Barker 3433a53a8e [Core] Fix an alignment bug for scrolling container contents 2025-06-24 13:55:20 +10:00
wolfee 4a91cac1b2
[Renderers/SDL3] Fix border position calculated incorrectly (#446) 2025-06-23 10:43:08 +10:00
Nic Barker ff7917798c [Core] Avoid calling measure text function with length zero 2025-06-23 10:10:13 +10:00
wolfee f97cb9ea10
[Renderers/SDL] Font sizing is ignored (#444) 2025-06-23 09:30:14 +10:00
Nic Barker 74f0ffbe26 [Renderers/Cairo] Fix outdated image API usage in cairo example 2025-06-23 09:16:41 +10:00
Nic Barker a5e212b8de [Renderers/HTML] Fix a mouse overlap bug with external scroll handling 2025-06-20 10:59:44 +10:00
Nic Barker 0835781602 [Bindings/Odin] Update Odin Bindings 2025-06-18 09:55:32 +10:00
Nic Barker 13ecd80ee5 [Renderers/SDL3] Fix up outdated API usage in SDL3 example 2025-06-18 09:54:27 +10:00
Nic Barker c524485c46 [Core] Fix case where there could be 64 extra bytes of padding between arrays in clays internal arena 2025-06-17 13:07:56 +10:00
Nic Barker b9e27178c0 [Core] Align base arena memory to 64 byte cache line 2025-06-17 10:32:30 +10:00
Nic Barker 6f10bf4b3d [Core] Remove erroneous break statement when using external scroll handling 2025-06-13 10:35:31 +10:00
Nic Barker adb1bd620a [Examples/clay-official-website] Fix links on official website example 2025-06-13 09:59:07 +10:00
Nic Barker dca9f60a6c [Core] Add objective-c support 2025-06-12 12:16:29 +10:00
Nic Barker 3ccfa0f8fa [Core] Fix a bug where extra child gap was added to the dimensions of clipped containers 2025-06-11 10:41:01 +10:00
Nic Barker 35b45d939b Fix some typos in the readme 2025-06-11 10:15:51 +10:00
Nic Barker 6b03a215b7 [Core] Fix an out of bounds access for single newline characters 2025-06-10 11:38:56 +10:00
41 changed files with 7346 additions and 612 deletions

View file

@ -0,0 +1,104 @@
name: Odin Bindings Update
on:
push:
branches: [main]
jobs:
check_changes:
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.check_clay.outputs.changed }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Check if clay.h changed
id: check_clay
run: |
if git diff --name-only HEAD^ HEAD | grep -Fx "clay.h"; then
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
build:
needs: check_changes
if: needs.check_changes.outputs.changed == 'true'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Install clang (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y clang
- name: Build libs
run: |
mkdir -p build
mkdir -p artifacts
cp clay.h clay.c
COMMON_FLAGS="-DCLAY_IMPLEMENTATION -fno-ident -frandom-seed=clay"
if [[ "$(uname)" == "Linux" ]]; then
mkdir -p artifacts/linux
mkdir -p artifacts/windows
mkdir -p artifacts/wasm
echo "Building for Linux..."
clang -c $COMMON_FLAGS -fPIC -ffreestanding -static -target x86_64-unknown-linux-gnu clay.c -o build/linux.o
ar rD artifacts/linux/clay.a build/linux.o
echo "Building for Windows..."
clang -c $COMMON_FLAGS -ffreestanding -target x86_64-pc-windows-msvc -fuse-ld=llvm-lib clay.c -o artifacts/windows/clay.lib
echo "Building for WASM..."
clang -c $COMMON_FLAGS -fPIC -target wasm32 -nostdlib -static clay.c -o artifacts/wasm/clay.o
elif [[ "$(uname)" == "Darwin" ]]; then
mkdir -p artifacts/macos
mkdir -p artifacts/macos-arm64
echo "Building for macOS (x86_64)..."
clang -c $COMMON_FLAGS -fPIC -target x86_64-apple-macos clay.c -o build/macos.o
libtool -static -o artifacts/macos/clay.a build/macos.o
echo "Building for macOS (ARM64)..."
clang -c $COMMON_FLAGS -fPIC -target arm64-apple-macos clay.c -o build/macos-arm64.o
libtool -static -o artifacts/macos-arm64/clay.a build/macos-arm64.o
fi
rm -f clay.c build/*.o
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: artifacts-${{ matrix.os }}
path: artifacts/
commit:
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Move artifacts
run: |
cp -r artifacts-ubuntu-latest/* bindings/odin/clay-odin/
cp -r artifacts-macos-latest/* bindings/odin/clay-odin/
- name: Commit/Push changes
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add bindings/odin/clay-odin/
git commit -m "[bindings/odin] Update Odin bindings"
git push

273
README.md
View file

@ -1,4 +1,4 @@
# Clay
# Clay, A UI Layout Library
**_Clay_** (short for **C Layout**) is a high performance 2D UI layout library.
### Major Features
@ -59,7 +59,7 @@ Clay_ElementDeclaration sidebarItemConfig = (Clay_ElementDeclaration) {
// Re-useable components are just normal functions
void SidebarItemComponent() {
CLAY(sidebarItemConfig) {
CLAY(id, sidebarItemConfig) {
// children go here...
}
}
@ -85,14 +85,13 @@ int main() {
Clay_BeginLayout();
// An example of laying out a UI with a fixed width sidebar and flexible width main content
CLAY({ .id = CLAY_ID("OuterContainer"), .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
CLAY({
.id = CLAY_ID("SideBar"),
CLAY(CLAY_ID("OuterContainer"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
CLAY(CLAY_ID("SideBar"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 },
.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("ProfilePicture"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture } }) {}
CLAY(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(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} }));
}
@ -101,7 +100,7 @@ int main() {
SidebarItemComponent();
}
CLAY({ .id = CLAY_ID("MainContent"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}
CLAY(CLAY_ID("MainContent"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}
}
}
@ -161,8 +160,8 @@ For help starting out or to discuss clay, considering joining [the discord serve
- [Clay_MinMemorySize](#clay_minmemorysize)
- [Clay_CreateArenaWithCapacityAndMemory](#clay_createarenawithcapacityandmemory)
- [Clay_SetMeasureTextFunction](#clay_setmeasuretextfunction)
- [Clay_ResetMeasureTextCache](#clau_resetmeasuretextcache)
- [Clay_SetMaxElementCount](clay_setmaxelementcount)
- [Clay_ResetMeasureTextCache](#clay_resetmeasuretextcache)
- [Clay_SetMaxElementCount](#clay_setmaxelementcount)
- [Clay_SetMaxMeasureTextCacheWordCount](#clay_setmaxmeasuretextcachewordcount)
- [Clay_Initialize](#clay_initialize)
- [Clay_GetCurrentContext](#clay_getcurrentcontext)
@ -194,16 +193,16 @@ For help starting out or to discuss clay, considering joining [the discord serve
## High Level Documentation
### Building UI Hierarchies
Clay UIs are built using the C macro `CLAY({ configuration })`. This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The `CLAY()` macro can also be _nested_, similar to other declarative UI systems like HTML.
Clay UIs are built using the C macro `CLAY(id, { configuration })`. This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The `CLAY()` macro can also be _nested_, similar to other declarative UI systems like HTML.
Child elements are added by opening a block: `{}` after calling the `CLAY()` macro (exactly like you would with an `if` statement or `for` loop), and declaring child components inside the braces.
```C
// Parent element with 8px of padding
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
CLAY(CLAY_ID("parent"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
// Child element 1
CLAY_TEXT(CLAY_STRING("Hello World"), CLAY_TEXT_CONFIG({ .fontSize = 16 }));
// Child element 2 with red background
CLAY({ .backgroundColor = COLOR_RED }) {
CLAY((CLAY_ID("child"), { .backgroundColor = COLOR_RED }) {
// etc
}
}
@ -214,13 +213,13 @@ However, unlike HTML and other declarative DSLs, this macro is just C. As a resu
// Re-usable "components" are just functions that declare more UI
void ButtonComponent(Clay_String buttonText) {
// Red box button with 8px of padding
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {
CLAY_TEXT(buttonText, textConfig);
}
}
// Parent element
CLAY({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY(CLAY_ID("parent"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Render a bunch of text elements
for (int i = 0; i < textArray.length; i++) {
CLAY_TEXT(textArray.elements[i], textConfig);
@ -240,7 +239,7 @@ CLAY({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
### Configuring Layout and Styling UI Elements
The layout and style of clay elements is configured with the [Clay_ElementDeclaration](#clay_elementdeclaration) struct passed to the `CLAY()` macro.
```C
CLAY({ .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY(CLAY_ID("box"), { .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Children are 8px inset into parent, and laid out top to bottom
}
```
@ -257,18 +256,21 @@ Clay_ElementDeclaration reuseableStyle = (Clay_ElementDeclaration) {
.cornerRadius = { 12, 12, 12, 12 }
};
CLAY(reuseableStyle) {
CLAY(CLAY_ID("box"), reuseableStyle) {
// ...
}
```
### Element IDs
Clay elements can optionally be tagged with a unique identifier using the `.id` field of an element declaration, and with the [CLAY_ID()](#clay_id) convenience macro.
The Clay macro by default accepts an ID as its first argument, which is usually provided by the [CLAY_ID()](#clay_id) convenience macro. Elements can also be created with auto generated IDs, by using the [CLAY_AUTO_ID()](#clay-auto-id) macro.
```C
// Will always produce the same ID from the same input string
CLAY({ .id = CLAY_ID("OuterContainer") }) {}
CLAY(CLAY_ID("OuterContainer"), { ...configuration }) {}
// Generates a unique ID that may not be the same between two layout calls
CLAY_AUTO_ID({ ...configuration }) {}
```
Element IDs have two main use cases. Firstly, tagging an element with an ID allows you to query information about the element later, such as its [mouseover state](#clay_pointerover) or dimensions.
@ -279,11 +281,11 @@ To avoid having to construct dynamic strings at runtime to differentiate ids in
```C
// This is the equivalent of calling CLAY_ID("Item0"), CLAY_ID("Item1") etc
for (int index = 0; index < items.length; index++) {
CLAY({ .id = CLAY_IDI("Item", index) }) {}
CLAY(CLAY_IDI("Item", index), { ..configuration }) {}
}
```
This ID (or, if not provided, an auto generated ID) will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect)
This ID will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect)
### Mouse, Touch and Pointer Interactions
@ -297,7 +299,7 @@ The function `bool Clay_Hovered()` can be called during element construction or
```C
// An orange button that turns blue when hovered
CLAY({ .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {
CLAY(CLAY_ID("Button"), { .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {
bool buttonHovered = Clay_Hovered();
CLAY_TEXT(buttonHovered ? CLAY_STRING("Hovered") : CLAY_STRING("Hover me!"), headerTextConfig);
}
@ -318,7 +320,7 @@ void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerI
ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
Clay_OnHover(HandleButtonInteraction, &linkButton);
CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig);
}
@ -360,11 +362,11 @@ Clay_UpdateScrollContainers(
);
// ...
// Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element
CLAY({ .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
// Scrolling contents
}
// .childOffset can be provided directly if you would prefer to manage scrolling outside of clay
CLAY({ .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
// Scrolling contents
}
```
@ -383,10 +385,10 @@ A classic example use case for floating elements is tooltips and modals.
```C
// The two text elements will be laid out top to bottom, and the floating container
// will be attached to "Outer"
CLAY({ .id = CLAY_ID("Outer"), .layout = { .layoutDirection = TOP_TO_BOTTOM } }) {
CLAY_TEXT(CLAY_ID("Button"), text, &headerTextConfig);
CLAY({ .id = CLAY_ID("Tooltip"), .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}
CLAY_TEXT(CLAY_ID("Button"), text, &headerTextConfig);
CLAY(CLAY_ID("Outer"), { .layout = { .layoutDirection = TOP_TO_BOTTOM } }) {
CLAY_TEXT(text, &headerTextConfig);
CLAY(CLAY_ID("Tooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}
CLAY_TEXT(text, &headerTextConfig);
}
```
@ -424,14 +426,11 @@ typedef struct {
// During init
Arena frameArena = (Arena) { .memory = malloc(1024) };
// ...
CLAY(0) {
// Custom elements only take a single pointer, so we need to store the data somewhere
CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
frameArena.offset += sizeof(CustomElementData);
CLAY({ .custom = { .customData = modelData } }) {}
}
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
// Later during your rendering
switch (renderCommand->commandType) {
@ -682,7 +681,7 @@ See [Scrolling Elements](#scrolling-elements) for more details.
```C
// Create a horizontally scrolling container
CLAY({
CLAY(CLAY_ID("ScrollContainer"), {
.clip = { .horizontal = true, .childOffset = Clay_GetScrollOffset() }
})
```
@ -732,9 +731,9 @@ void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerD
ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
Clay_OnHover(HandleButtonInteraction, &buttonData);
CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig);
CLAY_TEXT(CLAY_STRING("Click me!"), &headerTextConfig);
}
```
@ -788,13 +787,40 @@ Returns a [Clay_ElementId](#clay_elementid) for the provided id string, used for
**Examples**
```C
// Define an element with 16px of x and y padding
CLAY({ .id = CLAY_ID("Outer"), .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
CLAY(CLAY_ID("Outer"), { .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
// A nested child element
CLAY({ .id = CLAY_ID("SideBar"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
CLAY(CLAY_ID("SideBar"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
// Children laid out top to bottom with a 16 px gap between them
}
// A vertical scrolling container with a colored background
CLAY({
CLAY(CLAY_ID("ScrollContainer"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10),
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
}) {
// child elements
}
}
```
---
### CLAY_AUTO_ID()
A version of the core [CLAY()](#clay) element creation macro that generates an ID automatically instead of requiring it as the first argument.
Note that under the hood this ID is generated in the same way as [CLAY_ID_LOCAL()](#clay_id_local), which is based on the element's position in the hierarchy, and may chance between layout calls if elements are added / removed from the hierarchy before the element is defined. As a result, for transitions & retained mode backends to work correctly, IDs should be specified.
```C
// Note that CLAY_AUTO_ID only takes one argument: the configuration
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
// A nested child element
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
// Children laid out top to bottom with a 16 px gap between them
}
// A vertical scrolling container with a colored background
CLAY_AUTO_ID({
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10),
@ -931,8 +957,7 @@ To regenerate the same ID outside of layout declaration when using utility funct
```C
// Tag a button with the Id "Button"
CLAY({
.id = CLAY_ID("Button"),
CLAY(CLAY_ID("Button"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }
}) {
// ...children
@ -1065,18 +1090,9 @@ typedef struct {
**Fields**
**`.id`** - `Clay_ElementID`
`CLAY({ .id = CLAY_ID("FileButton") })`
Uses [Clay_ElementId](#clay_elementid). Tags the element with an ID that can be later used to query data about the element, and gives it a human readable name in the debug tools.
IDs are typically generated using the [CLAY_ID](#clay_id), [CLAY_IDI](#clay_idi), [CLAY_ID_LOCAL](#clay_id_local) and [CLAY_IDI_LOCAL](#clay_idi_local) macros.
---
**`.layout`** - `Clay_LayoutConfig`
`CLAY({ .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
`CLAY(CLAY_ID("Element"), { .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
Uses [Clay_LayoutConfig](#clay_layoutconfig). Controls various settings related to _layout_, which can be thought of as "the size and position of this element and its children".
@ -1084,7 +1100,7 @@ Uses [Clay_LayoutConfig](#clay_layoutconfig). Controls various settings related
**`.backgroundColor`** - `Clay_Color`
`CLAY({ .backgroundColor = {120, 120, 120, 255} } })`
`CLAY(CLAY_ID("Element"), { .backgroundColor = {120, 120, 120, 255} } })`
Uses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
@ -1092,7 +1108,7 @@ Uses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values betwe
**`.cornerRadius`** - `float`
`CLAY({ .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })`
`CLAY(CLAY_ID("Element"), { .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })`
Defines the radius in pixels for the arc of rectangle corners (`0` is square, `rectangle.width / 2` is circular).
@ -1102,7 +1118,7 @@ Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to p
**`.aspectRatio`** - `Clay_AspectRatioElementConfig`
`CLAY({ .aspectRatio = 1 })`
`CLAY(CLAY_ID("Element"), { .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.
@ -1110,7 +1126,7 @@ Uses [Clay_AspectRatioElementConfig](#clay_aspectratioelementconfig). Configures
**`.image`** - `Clay_ImageElementConfig`
`CLAY({ .image = { .imageData = &myImage } })`
`CLAY(CLAY_ID("Element"), { .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.
@ -1118,7 +1134,7 @@ Uses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element
**`.floating`** - `Clay_FloatingElementConfig`
`CLAY({ .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } })`
`CLAY(CLAY_ID("Element"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } })`
Uses [Clay_FloatingElementConfig](#clay_floatingelementconfig). Configures the element as an floating element, which allows it to stack "in front" and "on top" of other elements without affecting sibling or parent size or position.
@ -1126,7 +1142,7 @@ Uses [Clay_FloatingElementConfig](#clay_floatingelementconfig). Configures the e
**`.custom`** - `Clay_CustomElementConfig`
`CLAY({ .custom = { .customData = &my3DModel } })`
`CLAY(CLAY_ID("Element"), { .custom = { .customData = &my3DModel } })`
Uses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the element as a custom element, which allows you to pass custom data through to the renderer. Causes a render command with type `CUSTOM` to be emitted.
@ -1134,7 +1150,7 @@ Uses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the eleme
**`.clip`** - `Clay_ClipElementConfig`
`CLAY({ .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })`
`CLAY(CLAY_ID("Element"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })`
Uses [Clay_ClipElementConfig](#clay_scrollelementconfig). Configures the element as a clip element, which causes child elements to be clipped / masked if they overflow, and together with the functions listed in [Scrolling Elements](#scrolling-elements) enables scrolling of child contents.
@ -1144,7 +1160,7 @@ Uses [Clay_ClipElementConfig](#clay_scrollelementconfig). Configures the element
**`.border`** - `Clay_BorderElementConfig`
`CLAY({ .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })`
`CLAY(CLAY_ID("Element"), { .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })`
Uses [Clay_BorderElementConfig](#clay_borderelementconfig). Configures the element as a border element, which instructs the renderer to draw coloured border lines along the perimeter of this element's bounding box. Causes a render command with type `BORDER` to be emitted.
@ -1152,7 +1168,7 @@ Uses [Clay_BorderElementConfig](#clay_borderelementconfig). Configures the eleme
**`.userData`** - `void *`
`CLAY({ .userData = &extraData })`
`CLAY(CLAY_ID("Element"), { .userData = &extraData })`
Transparently passes a pointer through to the corresponding [Clay_RenderCommands](#clay_rendercommand)s generated by this element.
@ -1164,13 +1180,13 @@ Transparently passes a pointer through to the corresponding [Clay_RenderCommands
// Declare a reusable rectangle config, with a purple color and 10px rounded corners
Clay_RectangleElementConfig rectangleConfig = (Clay_RectangleElementConfig) { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) };
// Declare a rectangle element using a reusable config
CLAY(rectangleConfig)) {}
CLAY(CLAY_ID("Box"), rectangleConfig) {}
// Declare a retangle element using an inline config
CLAY({ .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {
CLAY(CLAY_ID("BoxInline"), { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {
// child elements
}
// Declare a scrolling container with a colored background
CLAY({
CLAY(CLAY_ID("ScrollingContainer"), {
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10)
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
@ -1210,7 +1226,7 @@ Clay_LayoutConfig {
**`.layoutDirection`** - `Clay_LayoutDirection`
`CLAY({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
`CLAY(CLAY_ID("Element"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
Controls the axis / direction in which child elements are laid out. Available options are `CLAY_LEFT_TO_RIGHT` (default) and `CLAY_TOP_TO_BOTTOM`.
@ -1222,7 +1238,7 @@ _Did you know that "left to right" and "top to bottom" both have 13 letters?_
**`.padding`** - `Clay_Padding`
`CLAY({ .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } })`
`CLAY(CLAY_ID("Element"), { .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } })`
Controls white-space "padding" around the **outside** of child elements.
@ -1232,7 +1248,7 @@ Controls white-space "padding" around the **outside** of child elements.
**`.childGap`** - `uint16_t`
`CLAY({ .layout = { .childGap = 16 } })`
`CLAY(CLAY_ID("Element"), { .layout = { .childGap = 16 } })`
Controls the white-space **between** child elements as they are laid out. When `.layoutDirection` is `CLAY_LEFT_TO_RIGHT` (default), this will be horizontal space, whereas for `CLAY_TOP_TO_BOTTOM` it will be vertical space.
@ -1242,7 +1258,7 @@ Controls the white-space **between** child elements as they are laid out. When `
**`.childAlignment`** - `Clay_ChildAlignment`
`CLAY({ .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })`
`CLAY(CLAY_ID("Element"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })`
Controls the alignment of children relative to the height and width of the parent container. Available options are:
```C
@ -1256,7 +1272,7 @@ Controls the alignment of children relative to the height and width of the paren
**`.sizing`** - `Clay_Sizing`
`CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } })`
`CLAY(CLAY_ID("Element"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } })`
Controls how final width and height of element are calculated. The same configurations are available for both the `.width` and `.height` axis. There are several options:
@ -1276,7 +1292,7 @@ Controls how final width and height of element are calculated. The same configur
**Example Usage**
```C
CLAY({ .id = CLAY_ID("Button"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) {
CLAY(CLAY_ID("Button"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) {
// Children will be laid out vertically with 16px of padding around and between
}
```
@ -1286,7 +1302,7 @@ CLAY({ .id = CLAY_ID("Button"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTO
### Clay_ImageElementConfig
**Usage**
`CLAY({ .image = { ...image config } }) {}`
`CLAY(CLAY_ID("Element"), { .image = { ...image config } }) {}`
**Clay_ImageElementConfig** configures a clay element to render an image as its background.
@ -1302,7 +1318,7 @@ Clay_ImageElementConfig {
**`.imageData`** - `void *`
`CLAY({ .image = { .imageData = &myImage } }) {}`
`CLAY(CLAY_ID("Image"), { .image = { .imageData = &myImage } }) {}`
`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.
@ -1310,7 +1326,7 @@ Clay_ImageElementConfig {
// 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 } }) {}
CLAY(CLAY_ID("Image"), { .image = { .imageData = &profilePicture } }) {}
```
**Examples**
@ -1321,9 +1337,9 @@ 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 }) {}
CLAY(CLAY_ID("Image"), { .image = imageConfig }) {}
// Declare an image element using an inline config
CLAY({ .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
CLAY(CLAY_ID("ImageInline"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
// Rendering example
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
```
@ -1338,7 +1354,7 @@ Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_
**Usage**
`CLAY({ .aspectRatio = 16.0 / 9.0 }) {}`
`CLAY(CLAY_ID("Aspect"), { .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.
@ -1354,11 +1370,11 @@ Clay_AspectRatioElementConfig {
**`.aspectRatio`** - `float`
`CLAY({ .aspectRatio = { .aspectRatio = 16.0 / 9.0 } }) {}`
`CLAY(CLAY_ID("Aspect"), { .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 }) {}`
`CLAY(CLAY_ID("Aspect"), { .aspectRatio = 16.0 / 9.0 }) {}`
**Examples**
@ -1366,7 +1382,7 @@ or alternatively, as C will automatically pass the value to the first nested str
// 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({
CLAY(CLAY_ID("ProfilePicture"), {
.layout = { .width = CLAY_SIZING_GROW() },
.aspectRatio = profilePicture.width / profilePicture.height,
.image = { .imageData = &profilePicture },
@ -1378,7 +1394,7 @@ CLAY({
### Clay_ImageElementConfig
**Usage**
`CLAY({ .image = { ...image config } }) {}`
`CLAY(CLAY_ID("Image"), { .image = { ...image config } }) {}`
**Clay_ImageElementConfig** configures a clay element to render an image as its background.
@ -1394,7 +1410,7 @@ Clay_ImageElementConfig {
**`.imageData`** - `void *`
`CLAY({ .image = { .imageData = &myImage } }) {}`
`CLAY(CLAY_ID("Image"), { .image = { .imageData = &myImage } }) {}`
`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.
@ -1402,7 +1418,7 @@ Clay_ImageElementConfig {
// 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 } }) {}
CLAY(CLAY_ID("Image"), { .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.
@ -1415,9 +1431,9 @@ 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 }) {}
CLAY(CLAY_ID("Image"), { .image = imageConfig }) {}
// Declare an image element using an inline config
CLAY({ .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
CLAY(CLAY_ID("ImageInline"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
// Rendering example
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
```
@ -1432,7 +1448,7 @@ Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_
**Usage**
`CLAY({ .clip = { ...clip config } }) {}`
`CLAY(CLAY_ID("ScrollBox"), { .clip = { ...clip config } }) {}`
**Notes**
@ -1453,7 +1469,7 @@ Clay_ClipElementConfig {
**`.horizontal`** - `bool`
`CLAY({ .clip = { .horizontal = true } })`
`CLAY(CLAY_ID("HorizontalScroll"), { .clip = { .horizontal = true } })`
Enables or disables horizontal clipping for this container element.
@ -1461,7 +1477,7 @@ Enables or disables horizontal clipping for this container element.
**`.vertical`** - `bool`
`CLAY({ .clip = { .vertical = true } })`
`CLAY(LAY_ID("VerticalScroll"), { .clip = { .vertical = true } })`
Enables or disables vertical clipping for this container element.
@ -1476,9 +1492,9 @@ Enabling clip for an element will result in two additional render commands:
**Examples**
```C
CLAY({ .clip = { .vertical = true } }) {
CLAY(CLAY_ID("ScrollOuter"), { .clip = { .vertical = true } }) {
// Create child content with a fixed height of 5000
CLAY({ .id = CLAY_ID("ScrollInner"), .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
CLAY(CLAY_ID("ScrollInner"), { .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
}
```
@ -1488,7 +1504,7 @@ CLAY({ .clip = { .vertical = true } }) {
**Usage**
`CLAY({ .border = { ...border config } }) {}`
`CLAY(CLAY_ID("Border"), { .border = { ...border config } }) {}`
**Notes**
@ -1516,7 +1532,7 @@ typedef struct Clay_BorderElementConfig
**`.color`** - `Clay_Color`
`CLAY({ .border = { .color = { 255, 0, 0, 255 } } })`
`CLAY(CLAY_ID("Border"), { .border = { .color = { 255, 0, 0, 255 } } })`
Uses [Clay_Color](#clay_color). Specifies the shared color for all borders configured by this element. Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
@ -1524,7 +1540,7 @@ Uses [Clay_Color](#clay_color). Specifies the shared color for all borders confi
**`.width`** - `Clay_BorderWidth`
`CLAY({ .border = { .width = { .left = 2, .right = 10 } } })`
`CLAY(CLAY_ID("Border"), { .border = { .width = { .left = 2, .right = 10 } } })`
Indicates to the renderer that a border of `.color` should be draw at the specified edges of the bounding box, **inset and overlapping the box contents by `.width`**.
@ -1534,7 +1550,7 @@ Note:
**`.width.betweenChildren`**
`CLAY({ .border = { .width = { .betweenChildren = 2 } }, .color = COLOR_RED })`
`CLAY(CLAY_ID("Border"), { .border = { .width = { .betweenChildren = 2 } }, .color = COLOR_RED })`
Configures the width and color of borders to be drawn between children. These borders will be vertical lines if the parent uses `.layoutDirection = CLAY_LEFT_TO_RIGHT` and horizontal lines if the parent uses `CLAY_TOP_TO_BOTTOM`. Unlike `.left, .top` etc, this option **will generate additional rectangle render commands representing the borders between children.** As a result, the renderer does not need to specifically implement rendering for these border elements.
@ -1544,8 +1560,7 @@ Configures the width and color of borders to be drawn between children. These bo
```C
// 300x300 container with a 1px red border around all the edges
CLAY({
.id = CLAY_ID("OuterBorder"),
CLAY(CLAY_ID("OuterBorder"), {
.layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) } },
.border = { .width = { 1, 1, 1, 1, 0 }, .color = COLOR_RED }
}) {
@ -1553,16 +1568,14 @@ CLAY({
}
// Container with a 3px yellow bottom border
CLAY({
.id = CLAY_ID("OuterBorder"),
CLAY(CLAY_ID("OuterBorder"), {
.border = { .width = { .bottom = 3 }, .color = COLOR_YELLOW }
}) {
// ...
}
// Container with a 5px curved border around the edges, and a 5px blue border between all children laid out top to bottom
CLAY({
.id = CLAY_ID("OuterBorder"),
CLAY(CLAY_ID("OuterBorder"), {
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
.border = { .width = { 5, 5, 5, 5, 5 }, .color = COLOR_BLUE }
}) {
@ -1585,7 +1598,7 @@ Rendering of borders and rounded corners is left up to the user. See the provide
**Usage**
`CLAY({ .floating = { ...floating config } }) {}`
`CLAY(CLAY_ID("Floating"), { .floating = { ...floating config } }) {}`
**Notes**
@ -1636,7 +1649,7 @@ Clay_FloatingElementConfig {
**`.offset`** - `Clay_Vector2`
`CLAY({ .floating = { .offset = { -24, -24 } } })`
`CLAY(CLAY_ID("Floating"), { .floating = { .offset = { -24, -24 } } })`
Used to apply a position offset to the floating container _after_ all other layout has been calculated.
@ -1644,7 +1657,7 @@ Used to apply a position offset to the floating container _after_ all other layo
**`.expand`** - `Clay_Dimensions`
`CLAY({ .floating = { .expand = { 16, 16 } } })`
`CLAY(CLAY_ID("Floating"), { .floating = { .expand = { 16, 16 } } })`
Used to expand the width and height of the floating container _before_ laying out child elements.
@ -1652,7 +1665,7 @@ Used to expand the width and height of the floating container _before_ laying ou
**`.zIndex`** - `float`
`CLAY({ .floating = { .zIndex = 1 } })`
`CLAY(CLAY_ID("Floating"), { .floating = { .zIndex = 1 } })`
All floating elements (as well as their entire child hierarchies) will be sorted by `.zIndex` order before being converted to render commands. If render commands are drawn in order, elements with higher `.zIndex` values will be drawn on top.
@ -1660,41 +1673,41 @@ All floating elements (as well as their entire child hierarchies) will be sorted
**`.parentId`** - `uint32_t`
`CLAY({ .floating = { .parentId = Clay_GetElementId("HeaderButton").id } })`
`CLAY(CLAY_ID("Floating"), { .floating = { .parentId = Clay_GetElementId("HeaderButton").id } })`
By default, floating containers will "attach" to the parent element that they are declared inside. However, there are cases where this limitation could cause significant performance or ergonomics problems. `.parentId` allows you to specify a `CLAY_ID().id` to attach the floating container to. The parent element with the matching id can be declared anywhere in the hierarchy, it doesn't need to be declared before or after the floating container in particular.
Consider the following case:
```C
// Load an image somewhere in your code
CLAY({ .id = CLAY_IDI("SidebarButton", 1) }) {
CLAY(CLAY_IDI("SidebarButton", 1), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 1) {
CLAY({ /* floating config... */ })
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
}
}
CLAY({ .id = CLAY_IDI("SidebarButton", 2) }) {
CLAY(CLAY_IDI("SidebarButton", 2), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 2) {
CLAY({ /* floating config... */ })
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
}
}
CLAY({ .id = CLAY_IDI("SidebarButton", 3) }) {
CLAY(CLAY_IDI("SidebarButton", 3), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 3) {
CLAY({ /* floating config... */ })
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
}
}
CLAY({ .id = CLAY_IDI("SidebarButton", 4) }) {
CLAY(CLAY_IDI("SidebarButton", 4), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 4) {
CLAY({ /* floating config... */ })
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
}
}
CLAY({ .id = CLAY_IDI("SidebarButton", 5) }) {
CLAY(CLAY_IDI("SidebarButton", 5), { }) {
// .. some button contents
if (tooltip.attachedButtonIndex == 5) {
CLAY({ /* floating config... */ })
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
}
}
```
@ -1703,24 +1716,24 @@ The definition of the above UI is significantly polluted by the need to conditio
```C
// Load an image somewhere in your code
CLAY({ .id = CLAY_IDI("SidebarButton", 1) }) {
CLAY(CLAY_IDI("SidebarButton", 1), { }) {
// .. some button contents
}
CLAY({ .id = CLAY_IDI("SidebarButton", 2) }) {
CLAY(CLAY_IDI("SidebarButton", 2), { }) {
// .. some button contents
}
CLAY({ .id = CLAY_IDI("SidebarButton", 3) }) {
CLAY(CLAY_IDI("SidebarButton", 3), { }) {
// .. some button contents
}
CLAY({ .id = CLAY_IDI("SidebarButton", 4) }) {
CLAY(CLAY_IDI("SidebarButton", 4), { }) {
// .. some button contents
}
CLAY({ .id = CLAY_IDI("SidebarButton", 5) }) {
CLAY(CLAY_IDI("SidebarButton", 5), { }) {
// .. some button contents
}
// Any other point in the hierarchy
CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_ID, .parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id }) {
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_ID, .parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id }) {
// Tooltip contents...
}
```
@ -1729,7 +1742,7 @@ CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .attachTo = CLAY_ATTACH_TO_
**`.attachment`** - `Clay_FloatingAttachPoints`
`CLAY({ .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } }) {}`
`CLAY(CLAY_ID("Floating"), { .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } }) {}`
In terms of positioning the floating container, `.attachment` specifies
@ -1744,7 +1757,7 @@ For example:
"Attach the LEFT_CENTER of the floating container to the RIGHT_TOP of the parent"
`CLAY({ .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } });`
`CLAY(CLAY_ID("Floating"), { .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } });`
![Screenshot 2024-08-23 at 11 53 24 AM](https://github.com/user-attachments/assets/ebe75e0d-1904-46b0-982d-418f929d1516)
@ -1758,31 +1771,31 @@ Controls whether pointer events like hover and click should pass through to cont
```C
// Horizontal container with three option buttons
CLAY({ .id = CLAY_ID("OptionsList"), .layout = { childGap = 16 } }) {
CLAY({ .id = CLAY_IDI("Option", 1), .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY(CLAY_ID("OptionsList"), { .layout = { childGap = 16 } }) {
CLAY(CLAY_IDI("Option", 1), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY_TEXT(CLAY_STRING("Option 1"), CLAY_TEXT_CONFIG());
}
CLAY({ .id = CLAY_IDI("Option", 2), .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY(CLAY_IDI("Option", 2), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY_TEXT(CLAY_STRING("Option 2"), CLAY_TEXT_CONFIG());
// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements
CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {
CLAY_TEXT(CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG());
}
}
CLAY({ .id = CLAY_IDI("Option", 3), .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY(CLAY_IDI("Option", 3), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
CLAY_TEXT(CLAY_STRING("Option 3"), CLAY_TEXT_CONFIG());
}
}
// Floating containers can also be declared elsewhere in a layout, to avoid branching or polluting other UI
for (int i = 0; i < 1000; i++) {
CLAY({ .id = CLAY_IDI("Option", i + 1) }) {
CLAY(CLAY_IDI("Option", i + 1), { }) {
// ...
}
}
// Note the use of "parentId".
// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements
CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER } } }) {
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER } } }) {
CLAY_TEXT(CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG());
}
```
@ -1799,7 +1812,7 @@ When using `.parentId`, the floating container can be declared anywhere after `B
**Usage**
`CLAY({ .custom = { .customData = &something } }) {}`
`CLAY(CLAY_ID("Custom"), { .custom = { .customData = &something } }) {}`
**Notes**
@ -1858,7 +1871,7 @@ CLAY(0) {
CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
frameArena.offset += sizeof(CustomElementData);
CLAY({ .custom = { .customData = modelData } }) {}
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
}
// Later during your rendering

View file

@ -1,13 +1,13 @@
cp ../../clay.h clay.c;
# Intel Mac
clang -c -DCLAY_IMPLEMENTATION -o clay.o -ffreestanding -static -target x86_64-apple-darwin clay.c -fPIC -O3 && ar r clay-odin/macos/clay.a clay.o;
rm -f clay-odin/macos/clay.a && clang -c -DCLAY_IMPLEMENTATION -o clay.o -ffreestanding -static -target x86_64-apple-darwin clay.c -fPIC -O3 && ar r clay-odin/macos/clay.a clay.o;
# ARM Mac
clang -c -DCLAY_IMPLEMENTATION -g -o clay.o -static clay.c -fPIC -O3 && ar r clay-odin/macos-arm64/clay.a clay.o;
rm -f clay-odin/macos-arm64/clay.a && clang -c -DCLAY_IMPLEMENTATION -g -o clay.o -static clay.c -fPIC -O3 && ar r clay-odin/macos-arm64/clay.a clay.o;
# x64 Windows
clang -c -DCLAY_IMPLEMENTATION -o clay-odin/windows/clay.lib -ffreestanding -target x86_64-pc-windows-msvc -fuse-ld=llvm-lib -static -O3 clay.c;
rm -f clay-odin/windows/clay.lib && clang -c -DCLAY_IMPLEMENTATION -o clay-odin/windows/clay.lib -ffreestanding -target x86_64-pc-windows-msvc -fuse-ld=llvm-lib -static -O3 clay.c;
# Linux
clang -c -DCLAY_IMPLEMENTATION -o clay.o -ffreestanding -static -target x86_64-unknown-linux-gnu clay.c -fPIC -O3 && ar r clay-odin/linux/clay.a clay.o;
rm -f clay-odin/linux/clay.a && clang -c -DCLAY_IMPLEMENTATION -o clay.o -ffreestanding -static -target x86_64-unknown-linux-gnu clay.c -fPIC -O3 && ar r clay-odin/linux/clay.a clay.o;
# WASM
clang -c -DCLAY_IMPLEMENTATION -o clay-odin/wasm/clay.o -target wasm32 -nostdlib -static -O3 clay.c;
rm -f clay-odin/wasm/clay.o && clang -c -DCLAY_IMPLEMENTATION -o clay-odin/wasm/clay.o -target wasm32 -nostdlib -static -O3 clay.c;
rm clay.o;
rm clay.c;

View file

@ -338,7 +338,6 @@ ClayArray :: struct($type: typeid) {
}
ElementDeclaration :: struct {
id: ElementId,
layout: LayoutConfig,
backgroundColor: Color,
cornerRadius: CornerRadius,
@ -378,6 +377,7 @@ Context :: struct {} // opaque structure, only use as a pointer
@(link_prefix = "Clay_", default_calling_convention = "c")
foreign Clay {
_OpenElement :: proc() ---
_OpenElementWithId :: proc(id: ElementId) ---
_CloseElement :: proc() ---
MinMemorySize :: proc() -> u32 ---
CreateArenaWithCapacityAndMemory :: proc(capacity: c.size_t, offset: [^]u8) -> Arena ---
@ -413,7 +413,8 @@ foreign Clay {
@(link_prefix = "Clay_", default_calling_convention = "c", private)
foreign Clay {
_ConfigureOpenElement :: proc(config: ElementDeclaration) ---
_HashString :: proc(key: String, offset: u32, seed: u32) -> ElementId ---
_HashString :: proc(key: String, seed: u32) -> ElementId ---
_HashStringWithOffset :: proc(key: String, index: u32, seed: u32) -> ElementId ---
_OpenTextElement :: proc(text: String, textConfig: ^TextElementConfig) ---
_StoreTextElementConfig :: proc(config: TextElementConfig) -> ^TextElementConfig ---
_GetParentElementId :: proc() -> u32 ---
@ -425,11 +426,19 @@ ConfigureOpenElement :: proc(config: ElementDeclaration) -> bool {
}
@(deferred_none = _CloseElement)
UI :: proc() -> proc (config: ElementDeclaration) -> bool {
UI_WithId :: proc(id: ElementId) -> proc (config: ElementDeclaration) -> bool {
_OpenElementWithId(id)
return ConfigureOpenElement
}
@(deferred_none = _CloseElement)
UI_AutoId :: proc() -> proc (config: ElementDeclaration) -> bool {
_OpenElement()
return ConfigureOpenElement
}
UI :: proc{UI_WithId, UI_AutoId};
Text :: proc($text: string, config: ^TextElementConfig) {
wrapped := MakeString(text)
wrapped.isStaticallyAllocated = true
@ -460,11 +469,11 @@ CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
return CornerRadius{radius, radius, radius, radius}
}
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax = {}) -> SizingAxis {
return SizingAxis{type = SizingType.Fit, constraints = {sizeMinMax = sizeMinMax}}
}
SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax = {}) -> SizingAxis {
return SizingAxis{type = SizingType.Grow, constraints = {sizeMinMax = sizeMinMax}}
}
@ -481,9 +490,9 @@ MakeString :: proc(label: string) -> String {
}
ID :: proc(label: string, index: u32 = 0) -> ElementId {
return _HashString(MakeString(label), index, 0)
return _HashString(MakeString(label), index)
}
ID_LOCAL :: proc(label: string, index: u32 = 0) -> ElementId {
return _HashString(MakeString(label), index, _GetParentElementId())
return _HashStringWithOffset(MakeString(label), index, _GetParentElementId())
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -63,14 +63,12 @@ border2pxRed := clay.BorderElementConfig {
}
LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, $text: string, image: ^raylib.Texture2D) {
if clay.UI()({
id = clay.ID("HeroBlob", index),
if clay.UI(clay.ID("HeroBlob", index))({
layout = { sizing = { width = clay.SizingGrow({ max = 480 }) }, padding = clay.PaddingAll(16), childGap = 16, childAlignment = clay.ChildAlignment{ y = .Center } },
border = border2pxRed,
cornerRadius = clay.CornerRadiusAll(10)
}) {
if clay.UI()({
id = clay.ID("CheckImage", index),
if clay.UI(clay.ID("CheckImage", index))({
layout = { sizing = { width = clay.SizingFixed(32) } },
aspectRatio = { 1.0 },
image = { imageData = image },
@ -80,16 +78,14 @@ LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Colo
}
LandingPageDesktop :: proc() {
if clay.UI()({
id = clay.ID("LandingPage1Desktop"),
layout = { sizing = { width = clay.SizingGrow({ }), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
if clay.UI(clay.ID("LandingPage1Desktop"))({
layout = { sizing = { width = clay.SizingGrow(), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
}) {
if clay.UI()({
id = clay.ID("LandingPage1"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
if clay.UI(clay.ID("LandingPage1"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
border = { COLOR_RED, { left = 2, right = 2 } },
}) {
if clay.UI()({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingPercent(0.55) }, layoutDirection = .TopToBottom, childGap = 8 } }) {
if clay.UI(clay.ID("LeftText"))({ layout = { sizing = { width = clay.SizingPercent(0.55) }, layoutDirection = .TopToBottom, childGap = 8 } }) {
clay.Text(
"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.",
clay.TextConfig({fontSize = 56, fontId = FONT_ID_TITLE_56, textColor = COLOR_RED}),
@ -100,8 +96,7 @@ LandingPageDesktop :: proc() {
clay.TextConfig({fontSize = 36, fontId = FONT_ID_TITLE_36, textColor = COLOR_ORANGE}),
)
}
if clay.UI()({
id = clay.ID("HeroImageOuter"),
if clay.UI(clay.ID("HeroImageOuter"))({
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingPercent(0.45) }, childAlignment = { x = .Center }, childGap = 16 },
}) {
LandingPageBlob(1, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_5, "High performance", &checkImage5)
@ -115,17 +110,16 @@ LandingPageDesktop :: proc() {
}
LandingPageMobile :: proc() {
if clay.UI()({
id = clay.ID("LandingPage1Mobile"),
if clay.UI(clay.ID("LandingPage1Mobile"))({
layout = {
layoutDirection = .TopToBottom,
sizing = { width = clay.SizingGrow({ }), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) },
sizing = { width = clay.SizingGrow(), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) },
childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 },
childGap = 32,
},
}) {
if clay.UI()({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingGrow({ }) }, layoutDirection = .TopToBottom, childGap = 8 } }) {
if clay.UI(clay.ID("LeftText"))({ layout = { sizing = { width = clay.SizingGrow() }, layoutDirection = .TopToBottom, childGap = 8 } }) {
clay.Text(
"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.",
clay.TextConfig({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}),
@ -136,9 +130,8 @@ LandingPageMobile :: proc() {
clay.TextConfig({fontSize = 32, fontId = FONT_ID_TITLE_32, textColor = COLOR_ORANGE}),
)
}
if clay.UI()({
id = clay.ID("HeroImageOuter"),
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow({ }) }, childAlignment = { x = .Center }, childGap = 16 },
if clay.UI(clay.ID("HeroImageOuter"))({
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow() }, childAlignment = { x = .Center }, childGap = 16 },
}) {
LandingPageBlob(1, 24, FONT_ID_BODY_24, COLOR_BLOB_BORDER_5, "High performance", &checkImage5)
LandingPageBlob(2, 24, FONT_ID_BODY_24, COLOR_BLOB_BORDER_4, "Flexbox-style responsive layout", &checkImage4)
@ -151,18 +144,16 @@ LandingPageMobile :: proc() {
FeatureBlocks :: proc(widthSizing: clay.SizingAxis, outerPadding: u16) {
textConfig := clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_RED})
if clay.UI()({
id = clay.ID("HFileBoxOuter"),
if clay.UI(clay.ID("HFileBoxOuter"))({
layout = { layoutDirection = .TopToBottom, sizing = { width = widthSizing }, childAlignment = { y = .Center }, padding = { outerPadding, outerPadding, 32, 32 }, childGap = 8 },
}) {
if clay.UI()({ id = clay.ID("HFileIncludeOuter"), layout = { padding = { 8, 8, 4, 4 } }, backgroundColor = COLOR_RED, cornerRadius = clay.CornerRadiusAll(8) }) {
if clay.UI(clay.ID("HFileIncludeOuter"))({ layout = { padding = { 8, 8, 4, 4 } }, backgroundColor = COLOR_RED, cornerRadius = clay.CornerRadiusAll(8) }) {
clay.Text("#include clay.h", clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_LIGHT}))
}
clay.Text("~2000 lines of C99.", textConfig)
clay.Text("Zero dependencies, including no C standard library.", textConfig)
}
if clay.UI()({
id = clay.ID("BringYourOwnRendererOuter"),
if clay.UI(clay.ID("BringYourOwnRendererOuter"))({
layout = { layoutDirection = .TopToBottom, sizing = { width = widthSizing }, childAlignment = { y = .Center }, padding = { outerPadding, outerPadding, 32, 32 }, childGap = 8 },
}) {
clay.Text("Renderer agnostic.", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = COLOR_ORANGE}))
@ -172,10 +163,9 @@ FeatureBlocks :: proc(widthSizing: clay.SizingAxis, outerPadding: u16) {
}
FeatureBlocksDesktop :: proc() {
if clay.UI()({ id = clay.ID("FeatureBlocksOuter"), layout = { sizing = { width = clay.SizingGrow({}) } } }) {
if clay.UI()({
id = clay.ID("FeatureBlocksInner"),
layout = { sizing = { width = clay.SizingGrow({ }) }, childAlignment = { y = .Center } },
if clay.UI(clay.ID("FeatureBlocksOuter"))({ layout = { sizing = { width = clay.SizingGrow({}) } } }) {
if clay.UI(clay.ID("FeatureBlocksInner"))({
layout = { sizing = { width = clay.SizingGrow() }, childAlignment = { y = .Center } },
border = { width = { betweenChildren = 2}, color = COLOR_RED },
}) {
FeatureBlocks(clay.SizingPercent(0.5), 50)
@ -184,9 +174,8 @@ FeatureBlocksDesktop :: proc() {
}
FeatureBlocksMobile :: proc() {
if clay.UI()({
id = clay.ID("FeatureBlocksInner"),
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow({ }) } },
if clay.UI(clay.ID("FeatureBlocksInner"))({
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow() } },
border = { width = { betweenChildren = 2}, color = COLOR_RED },
}) {
FeatureBlocks(clay.SizingGrow({}), 16)
@ -194,9 +183,9 @@ FeatureBlocksMobile :: proc() {
}
DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
if clay.UI()({ id = clay.ID("SyntaxPageLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
if clay.UI(clay.ID("SyntaxPageLeftText"))({ layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
clay.Text("Declarative Syntax", clay.TextConfig(titleTextConfig))
if clay.UI()({ id = clay.ID("SyntaxSpacer"), layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } } }) {}
if clay.UI(clay.ID("SyntaxSpacer"))({ layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } } }) {}
clay.Text(
"Flexible and readable declarative syntax with nested UI element hierarchies.",
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}),
@ -210,9 +199,8 @@ DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizi
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}),
)
}
if clay.UI()({ id = clay.ID("SyntaxPageRightImage"), layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
if clay.UI()({
id = clay.ID("SyntaxPageRightImageInner"),
if clay.UI(clay.ID("SyntaxPageRightImage"))({ layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
if clay.UI(clay.ID("SyntaxPageRightImageInner"))({
layout = { sizing = { width = clay.SizingGrow({ max = 568 }) } },
aspectRatio = { 1136.0 / 1194.0 },
image = { imageData = &syntaxImage },
@ -221,13 +209,11 @@ DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizi
}
DeclarativeSyntaxPageDesktop :: proc() {
if clay.UI()({
id = clay.ID("SyntaxPageDesktop"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
if clay.UI(clay.ID("SyntaxPageDesktop"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
}) {
if clay.UI()({
id = clay.ID("SyntaxPage"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
if clay.UI(clay.ID("SyntaxPage"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
border = border2pxRed,
}) {
DeclarativeSyntaxPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5))
@ -236,11 +222,10 @@ DeclarativeSyntaxPageDesktop :: proc() {
}
DeclarativeSyntaxPageMobile :: proc() {
if clay.UI()({
id = clay.ID("SyntaxPageMobile"),
if clay.UI(clay.ID("SyntaxPageMobile"))({
layout = {
layoutDirection = .TopToBottom,
sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 },
childGap = 16,
@ -257,7 +242,7 @@ ColorLerp :: proc(a: clay.Color, b: clay.Color, amount: f32) -> clay.Color {
LOREM_IPSUM_TEXT :: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
if clay.UI()({ id = clay.ID("PerformanceLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
if clay.UI(clay.ID("PerformanceLeftText"))({ layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
clay.Text("High Performance", clay.TextConfig(titleTextConfig))
if clay.UI()({ layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } }}) {}
clay.Text(
@ -273,22 +258,19 @@ HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementCon
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_LIGHT}),
)
}
if clay.UI()({ id = clay.ID("PerformanceRightImageOuter"), layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
if clay.UI()({
id = clay.ID("PerformanceRightBorder"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(400) } },
if clay.UI(clay.ID("PerformanceRightImageOuter"))({ layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
if clay.UI(clay.ID("PerformanceRightBorder"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(400) } },
border = { COLOR_LIGHT, {2, 2, 2, 2, 2} },
}) {
if clay.UI()({
id = clay.ID("AnimationDemoContainerLeft"),
layout = { sizing = { clay.SizingPercent(0.35 + 0.3 * lerpValue), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
if clay.UI(clay.ID("AnimationDemoContainerLeft"))({
layout = { sizing = { clay.SizingPercent(0.35 + 0.3 * lerpValue), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue),
}) {
clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT}))
}
if clay.UI()({
id = clay.ID("AnimationDemoContainerRight"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
if clay.UI(clay.ID("AnimationDemoContainerRight"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue),
}) {
clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT}))
@ -298,9 +280,8 @@ HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementCon
}
HighPerformancePageDesktop :: proc(lerpValue: f32) {
if clay.UI()({
id = clay.ID("PerformanceDesktop"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { 82, 82, 32, 32 }, childGap = 64 },
if clay.UI(clay.ID("PerformanceDesktop"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { 82, 82, 32, 32 }, childGap = 64 },
backgroundColor = COLOR_RED,
}) {
HighPerformancePage(lerpValue, {fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_LIGHT}, clay.SizingPercent(0.5))
@ -308,11 +289,10 @@ HighPerformancePageDesktop :: proc(lerpValue: f32) {
}
HighPerformancePageMobile :: proc(lerpValue: f32) {
if clay.UI()({
id = clay.ID("PerformanceMobile"),
if clay.UI(clay.ID("PerformanceMobile"))({
layout = {
layoutDirection = .TopToBottom,
sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 },
childGap = 32,
@ -335,8 +315,7 @@ RendererButtonActive :: proc(index: i32, $text: string) {
RendererButtonInactive :: proc(index: u32, $text: string) {
if clay.UI()({ border = border2pxRed }) {
if clay.UI()({
id = clay.ID("RendererButtonInactiveInner", index),
if clay.UI(clay.ID("RendererButtonInactiveInner", index))({
layout = { sizing = { width = clay.SizingFixed(300) }, padding = clay.PaddingAll(16) },
backgroundColor = COLOR_LIGHT,
cornerRadius = clay.CornerRadiusAll(10)
@ -347,7 +326,7 @@ RendererButtonInactive :: proc(index: u32, $text: string) {
}
RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
if clay.UI()({ id = clay.ID("RendererLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
if clay.UI(clay.ID("RendererLeftText"))({ layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
clay.Text("Renderer & Platform Agnostic", clay.TextConfig(titleTextConfig))
if clay.UI()({ layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } } }) {}
clay.Text(
@ -363,8 +342,7 @@ RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_RED}),
)
}
if clay.UI()({
id = clay.ID("RendererRightText"),
if clay.UI(clay.ID("RendererRightText"))({
layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center }, layoutDirection = .TopToBottom, childGap = 16 },
}) {
clay.Text("Try changing renderer!", clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE}))
@ -374,13 +352,11 @@ RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.
}
RendererPageDesktop :: proc() {
if clay.UI()({
id = clay.ID("RendererPageDesktop"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
if clay.UI(clay.ID("RendererPageDesktop"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
}) {
if clay.UI()({
id = clay.ID("RendererPage"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
if clay.UI(clay.ID("RendererPage"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
border = { COLOR_RED, { left = 2, right = 2 } },
}) {
RendererPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5))
@ -389,11 +365,10 @@ RendererPageDesktop :: proc() {
}
RendererPageMobile :: proc() {
if clay.UI()({
id = clay.ID("RendererMobile"),
if clay.UI(clay.ID("RendererMobile"))({
layout = {
layoutDirection = .TopToBottom,
sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 },
childGap = 32,
@ -416,28 +391,25 @@ animationLerpValue: f32 = -1.0
createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
mobileScreen := windowWidth < 750
clay.BeginLayout()
if clay.UI()({
id = clay.ID("OuterContainer"),
layout = { layoutDirection = .TopToBottom, sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) } },
if clay.UI(clay.ID("OuterContainer"))({
layout = { layoutDirection = .TopToBottom, sizing = { clay.SizingGrow(), clay.SizingGrow() } },
backgroundColor = COLOR_LIGHT,
}) {
if clay.UI()({
id = clay.ID("Header"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(50) }, childAlignment = { y = .Center }, childGap = 24, padding = { left = 32, right = 32 } },
if clay.UI(clay.ID("Header"))({
layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(50) }, childAlignment = { y = .Center }, childGap = 24, padding = { left = 32, right = 32 } },
}) {
clay.Text("Clay", &headerTextConfig)
if clay.UI()({ layout = { sizing = { width = clay.SizingGrow({ }) } } }) {}
if clay.UI()({ layout = { sizing = { width = clay.SizingGrow() } } }) {}
if (!mobileScreen) {
if clay.UI()({ id = clay.ID("LinkExamplesOuter"), backgroundColor = {0, 0, 0, 0} }) {
if clay.UI(clay.ID("LinkExamplesOuter"))({ backgroundColor = {0, 0, 0, 0} }) {
clay.Text("Examples", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
}
if clay.UI()({ id = clay.ID("LinkDocsOuter"), backgroundColor = {0, 0, 0, 0} }) {
if clay.UI(clay.ID("LinkDocsOuter"))({ backgroundColor = {0, 0, 0, 0} }) {
clay.Text("Docs", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
}
}
if clay.UI()({
id = clay.ID("LinkGithubOuter"),
if clay.UI(clay.ID("LinkGithubOuter"))({
layout = { padding = { 16, 16, 6, 6 } },
border = border2pxRed,
backgroundColor = clay.Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
@ -446,15 +418,14 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
clay.Text("Github", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
}
}
if clay.UI()({ id = clay.ID("TopBorder1"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_5 } ) {}
if clay.UI()({ id = clay.ID("TopBorder2"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_4 } ) {}
if clay.UI()({ id = clay.ID("TopBorder3"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_3 } ) {}
if clay.UI()({ id = clay.ID("TopBorder4"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_2 } ) {}
if clay.UI()({ id = clay.ID("TopBorder5"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_1 } ) {}
if clay.UI()({
id = clay.ID("ScrollContainerBackgroundRectangle"),
if clay.UI(clay.ID("TopBorder1"))({ layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_5 } ) {}
if clay.UI(clay.ID("TopBorder2"))({ layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_4 } ) {}
if clay.UI(clay.ID("TopBorder3"))({ layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_3 } ) {}
if clay.UI(clay.ID("TopBorder4"))({ layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_2 } ) {}
if clay.UI(clay.ID("TopBorder5"))({ layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_1 } ) {}
if clay.UI(clay.ID("ScrollContainerBackgroundRectangle"))({
clip = { vertical = true, childOffset = clay.GetScrollOffset() },
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, layoutDirection = clay.LayoutDirection.TopToBottom },
layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, layoutDirection = clay.LayoutDirection.TopToBottom },
backgroundColor = COLOR_LIGHT,
border = { COLOR_RED, { betweenChildren = 2} },
}) {

373
clay.h
View file

@ -31,7 +31,8 @@
#if !( \
(defined(__cplusplus) && __cplusplus >= 202002L) || \
(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"
#endif
@ -72,24 +73,24 @@
#define CLAY_SIZING_PERCENT(percentOfParent) (CLAY__INIT(Clay_SizingAxis) { .size = { .percent = (percentOfParent) }, .type = CLAY__SIZING_TYPE_PERCENT })
// Note: If a compile error led you here, you might be trying to use CLAY_ID with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SID instead.
#define CLAY_ID(label) CLAY_IDI(label, 0)
#define CLAY_ID(label) CLAY_SID(CLAY_STRING(label))
#define CLAY_SID(label) CLAY_SIDI(label, 0)
#define CLAY_SID(label) Clay__HashString(label, 0)
// Note: If a compile error led you here, you might be trying to use CLAY_IDI with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SIDI instead.
#define CLAY_IDI(label, index) CLAY_SIDI(CLAY_STRING(label), index)
#define CLAY_SIDI(label, index) Clay__HashString(label, index, 0)
#define CLAY_SIDI(label, index) Clay__HashStringWithOffset(label, index, 0)
// Note: If a compile error led you here, you might be trying to use CLAY_ID_LOCAL with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SID_LOCAL instead.
#define CLAY_ID_LOCAL(label) CLAY_IDI_LOCAL(label, 0)
#define CLAY_ID_LOCAL(label) CLAY_SID_LOCAL(CLAY_STRING(label))
#define CLAY_SID_LOCAL(label) CLAY_SIDI_LOCAL(label, 0)
#define CLAY_SID_LOCAL(label) Clay__HashString(label, Clay__GetParentElementId())
// Note: If a compile error led you here, you might be trying to use CLAY_IDI_LOCAL with something other than a string literal. To construct an ID with a dynamic string, use CLAY_SIDI_LOCAL instead.
#define CLAY_IDI_LOCAL(label, index) CLAY_SIDI_LOCAL(CLAY_STRING(label), index)
#define CLAY_SIDI_LOCAL(label, index) Clay__HashString(label, index, Clay__GetParentElementId())
#define CLAY_SIDI_LOCAL(label, index) Clay__HashStringWithOffset(label, index, Clay__GetParentElementId())
#define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof((s)[0])) - sizeof((s)[0]))
@ -120,25 +121,32 @@ static inline void Clay__SuppressUnusedLatchDefinitionVariableWarning(void) { (v
Into calls like this:
Clay_OpenElement();
Clay_ConfigureOpenElement((Clay_ElementDeclaration) {
Clay__OpenElement();
Clay__ConfigureOpenElement((Clay_ElementDeclaration) {
.id = CLAY_ID("Container"),
.backgroundColor = { 255, 200, 200, 255 }
});
...children declared here
Clay_CloseElement();
Clay__CloseElement();
The for loop will only ever run a single iteration, putting Clay__CloseElement() in the increment of the loop
means that it will run after the body - where the children are declared. It just exists to make sure you don't forget
to call Clay_CloseElement().
*/
#define CLAY(...) \
#define CLAY_AUTO_ID(...) \
for ( \
CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElement(), Clay__ConfigureOpenElement(CLAY__CONFIG_WRAPPER(Clay_ElementDeclaration, __VA_ARGS__)), 0); \
CLAY__ELEMENT_DEFINITION_LATCH < 1; \
CLAY__ELEMENT_DEFINITION_LATCH=1, Clay__CloseElement() \
)
#define CLAY(id, ...) \
for ( \
CLAY__ELEMENT_DEFINITION_LATCH = (Clay__OpenElementWithId(id), Clay__ConfigureOpenElement(CLAY__CONFIG_WRAPPER(Clay_ElementDeclaration, __VA_ARGS__)), 0); \
CLAY__ELEMENT_DEFINITION_LATCH < 1; \
CLAY__ELEMENT_DEFINITION_LATCH=1, Clay__CloseElement() \
)
// These macros exist to allow the CLAY() macro to be called both with an inline struct definition, such as
// CLAY({ .id = something... });
// As well as by passing a predefined declaration struct
@ -739,9 +747,6 @@ typedef struct Clay_PointerData {
} Clay_PointerData;
typedef struct Clay_ElementDeclaration {
// 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().
Clay_ElementId id;
// Controls various settings that affect the size and position of an element, as well as the sizes and positions of any child elements.
Clay_LayoutConfig layout;
// Controls the background color of the resulting element.
@ -788,6 +793,8 @@ typedef CLAY_PACKED_ENUM {
CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,
// Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!
CLAY_ERROR_TYPE_INTERNAL_ERROR,
// Clay__OpenElement was called more times than Clay__CloseElement, so there were still remaining open elements when the layout ended.
CLAY_ERROR_TYPE_UNBALANCED_OPEN_CLOSE,
} Clay_ErrorType;
// Data to identify the error that clay has encountered.
@ -915,10 +922,12 @@ CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
// Internal API functions required by macros ----------------------
CLAY_DLL_EXPORT void Clay__OpenElement(void);
CLAY_DLL_EXPORT void Clay__OpenElementWithId(Clay_ElementId elementId);
CLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
CLAY_DLL_EXPORT void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *config);
CLAY_DLL_EXPORT void Clay__CloseElement(void);
CLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed);
CLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t seed);
CLAY_DLL_EXPORT Clay_ElementId Clay__HashStringWithOffset(Clay_String key, uint32_t offset, uint32_t seed);
CLAY_DLL_EXPORT void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
CLAY_DLL_EXPORT Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);
CLAY_DLL_EXPORT uint32_t Clay__GetParentElementId(void);
@ -1137,6 +1146,7 @@ typedef struct {
Clay_LayoutConfig *layoutConfig;
Clay__ElementConfigArraySlice elementConfigs;
uint32_t id;
uint16_t floatingChildrenCount;
} Clay_LayoutElement;
CLAY__ARRAY_DEFINE(Clay_LayoutElement, Clay_LayoutElementArray)
@ -1173,7 +1183,6 @@ typedef struct { // todo get this struct into a single cache line
intptr_t hoverFunctionUserData;
int32_t nextIndex;
uint32_t generation;
uint32_t idAlias;
Clay__DebugElementData *debugData;
} Clay_LayoutElementHashMapItem;
@ -1283,15 +1292,12 @@ struct Clay_Context {
Clay_Context* Clay__Context_Allocate_Arena(Clay_Arena *arena) {
size_t totalSizeBytes = sizeof(Clay_Context);
uintptr_t memoryAddress = (uintptr_t)arena->memory;
// 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)
if (totalSizeBytes > arena->capacity)
{
return NULL;
}
arena->nextAllocation = nextAllocOffset + totalSizeBytes;
return (Clay_Context*)(memoryAddress + nextAllocOffset);
arena->nextAllocation += totalSizeBytes;
return (Clay_Context*)(arena->memory);
}
Clay_String Clay__WriteStringToCharBuffer(Clay__charArray *buffer, Clay_String string) {
@ -1362,7 +1368,22 @@ Clay_ElementId Clay__HashNumber(const uint32_t offset, const uint32_t seed) {
return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = seed, .stringId = CLAY__STRING_DEFAULT }; // Reserve the hash result of zero as "null id"
}
Clay_ElementId Clay__HashString(Clay_String key, const uint32_t offset, const uint32_t seed) {
Clay_ElementId Clay__HashString(Clay_String key, const uint32_t seed) {
uint32_t hash = seed;
for (int32_t i = 0; i < key.length; i++) {
hash += key.chars[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = 0, .baseId = hash + 1, .stringId = key }; // Reserve the hash result of zero as "null id"
}
Clay_ElementId Clay__HashStringWithOffset(Clay_String key, const uint32_t offset, const uint32_t seed) {
uint32_t hash = 0;
uint32_t base = seed;
@ -1645,7 +1666,10 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
char current = text->chars[end];
if (current == ' ' || current == '\n') {
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 = CLAY__DEFAULT_STRUCT;
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);
measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
if (current == ' ') {
@ -1692,12 +1716,12 @@ bool Clay__PointIsInsideRect(Clay_Vector2 point, Clay_BoundingBox rect) {
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
}
Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Clay_LayoutElement* layoutElement, uint32_t idAlias) {
Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Clay_LayoutElement* layoutElement) {
Clay_Context* context = Clay_GetCurrentContext();
if (context->layoutElementsHashMapInternal.length == context->layoutElementsHashMapInternal.capacity - 1) {
return NULL;
}
Clay_LayoutElementHashMapItem item = { .elementId = elementId, .layoutElement = layoutElement, .nextIndex = -1, .generation = context->generation + 1, .idAlias = idAlias };
Clay_LayoutElementHashMapItem item = { .elementId = elementId, .layoutElement = layoutElement, .nextIndex = -1, .generation = context->generation + 1 };
uint32_t hashBucket = elementId.id % context->layoutElementsHashMap.capacity;
int32_t hashItemPrevious = -1;
int32_t hashItemIndex = context->layoutElementsHashMap.internalArray[hashBucket];
@ -1707,7 +1731,6 @@ Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Cl
item.nextIndex = hashItem->nextIndex;
if (hashItem->generation <= context->generation) { // First collision - assume this is the "same" element
hashItem->elementId = elementId; // Make sure to copy this across. If the stringId reference has changed, we should update the hash item to use the new one.
hashItem->idAlias = idAlias;
hashItem->generation = context->generation + 1;
hashItem->layoutElement = layoutElement;
hashItem->debugData->collision = false;
@ -1754,9 +1777,10 @@ Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) {
Clay_ElementId Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) {
Clay_Context* context = Clay_GetCurrentContext();
Clay_LayoutElement *parentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));
Clay_ElementId elementId = Clay__HashNumber(parentElement->childrenOrTextContent.children.length, parentElement->id);
uint32_t offset = parentElement->childrenOrTextContent.children.length + parentElement->floatingChildrenCount;
Clay_ElementId elementId = Clay__HashNumber(offset, parentElement->id);
openLayoutElement->id = elementId.id;
Clay__AddHashMapItem(elementId, openLayoutElement, 0);
Clay__AddHashMapItem(elementId, openLayoutElement);
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
return elementId;
}
@ -1795,13 +1819,17 @@ void Clay__CloseElement(void) {
}
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
Clay_LayoutConfig *layoutConfig = openLayoutElement->layoutConfig;
bool elementHasScrollHorizontal = false;
bool elementHasScrollVertical = false;
if (!layoutConfig) {
openLayoutElement->layoutConfig = &Clay_LayoutConfig_DEFAULT;
layoutConfig = &Clay_LayoutConfig_DEFAULT;
}
bool elementHasClipHorizontal = false;
bool elementHasClipVertical = false;
for (int32_t i = 0; i < openLayoutElement->elementConfigs.length; i++) {
Clay_ElementConfig *config = Clay__ElementConfigArraySlice_Get(&openLayoutElement->elementConfigs, i);
if (config->type == CLAY__ELEMENT_CONFIG_TYPE_CLIP) {
elementHasScrollHorizontal = config->config.clipElementConfig->horizontal;
elementHasScrollVertical = config->config.clipElementConfig->vertical;
elementHasClipHorizontal = config->config.clipElementConfig->horizontal;
elementHasClipVertical = config->config.clipElementConfig->vertical;
context->openClipElementStack.length--;
break;
} else if (config->type == CLAY__ELEMENT_CONFIG_TYPE_FLOATING) {
@ -1822,19 +1850,21 @@ void Clay__CloseElement(void) {
Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
openLayoutElement->dimensions.width += child->dimensions.width;
openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + topBottomPadding);
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents
if (!elementHasScrollHorizontal) {
// Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents
if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width += child->minDimensions.width;
}
if (!elementHasScrollVertical) {
if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + topBottomPadding);
}
Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
}
float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
openLayoutElement->dimensions.width += childGap; // TODO this is technically a bug with childgap and scroll containers
openLayoutElement->dimensions.width += childGap;
if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width += childGap;
}
}
else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
openLayoutElement->dimensions.height = topBottomPadding;
openLayoutElement->minDimensions.height = topBottomPadding;
@ -1843,19 +1873,21 @@ void Clay__CloseElement(void) {
Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
openLayoutElement->dimensions.height += child->dimensions.height;
openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + leftRightPadding);
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents
if (!elementHasScrollVertical) {
// Minimum size of child elements doesn't matter to clip containers as they can shrink and hide their contents
if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height += child->minDimensions.height;
}
if (!elementHasScrollHorizontal) {
if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + leftRightPadding);
}
Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
}
float childGap = (float)(CLAY__MAX(openLayoutElement->childrenOrTextContent.children.length - 1, 0) * layoutConfig->childGap);
openLayoutElement->dimensions.height += childGap; // TODO this is technically a bug with childgap and scroll containers
openLayoutElement->dimensions.height += childGap;
if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height += childGap;
}
}
context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.children.length;
@ -1887,9 +1919,15 @@ void Clay__CloseElement(void) {
// Close the currently open element
int32_t closingElementIndex = Clay__int32_tArray_RemoveSwapback(&context->openLayoutElementStack, (int)context->openLayoutElementStack.length - 1);
// Get the currently open parent
openLayoutElement = Clay__GetOpenLayoutElement();
if (!elementIsFloating && context->openLayoutElementStack.length > 1) {
if (context->openLayoutElementStack.length > 1) {
if(elementIsFloating) {
openLayoutElement->floatingChildrenCount++;
return;
}
openLayoutElement->childrenOrTextContent.children.length++;
Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, closingElementIndex);
}
@ -1967,8 +2005,28 @@ void Clay__OpenElement(void) {
return;
}
Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;
Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);
Clay_LayoutElement* openLayoutElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);
Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1);
Clay__GenerateIdForAnonymousElement(openLayoutElement);
if (context->openClipElementStack.length > 0) {
Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));
} else {
Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, 0);
}
}
void Clay__OpenElementWithId(Clay_ElementId elementId) {
Clay_Context* context = Clay_GetCurrentContext();
if (context->layoutElements.length == context->layoutElements.capacity - 1 || context->booleanWarnings.maxElementsExceeded) {
context->booleanWarnings.maxElementsExceeded = true;
return;
}
Clay_LayoutElement layoutElement = CLAY__DEFAULT_STRUCT;
layoutElement.id = elementId.id;
Clay_LayoutElement * openLayoutElement = Clay_LayoutElementArray_Add(&context->layoutElements, layoutElement);
Clay__int32_tArray_Add(&context->openLayoutElementStack, context->layoutElements.length - 1);
Clay__AddHashMapItem(elementId, openLayoutElement);
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
if (context->openClipElementStack.length > 0) {
Clay__int32_tArray_Set(&context->layoutElementClipElementIds, context->layoutElements.length - 1, Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1));
} else {
@ -1996,7 +2054,7 @@ void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig)
Clay__MeasureTextCacheItem *textMeasured = Clay__MeasureTextCached(&text, textConfig);
Clay_ElementId elementId = Clay__HashNumber(parentElement->childrenOrTextContent.children.length, parentElement->id);
textElement->id = elementId.id;
Clay__AddHashMapItem(elementId, textElement, 0);
Clay__AddHashMapItem(elementId, textElement);
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
Clay_Dimensions textDimensions = { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? (float)textConfig->lineHeight : textMeasured->unwrappedDimensions.height };
textElement->dimensions = textDimensions;
@ -2010,19 +2068,6 @@ void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig)
parentElement->childrenOrTextContent.children.length++;
}
Clay_ElementId Clay__AttachId(Clay_ElementId elementId) {
Clay_Context* context = Clay_GetCurrentContext();
if (context->booleanWarnings.maxElementsExceeded) {
return Clay_ElementId_DEFAULT;
}
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
uint32_t idAlias = openLayoutElement->id;
openLayoutElement->id = elementId.id;
Clay__AddHashMapItem(elementId, openLayoutElement, idAlias);
Clay__StringArray_Set(&context->layoutElementIdStrings, context->layoutElements.length - 1, elementId.stringId);
return elementId;
}
void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
Clay_Context* context = Clay_GetCurrentContext();
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
@ -2034,8 +2079,6 @@ void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
.userData = context->errorHandler.userData });
}
Clay_ElementId openLayoutElementId = declaration->id;
openLayoutElement->elementConfigs.internalArray = &context->elementConfigs.internalArray[context->elementConfigs.length];
Clay_SharedElementConfig *sharedConfig = NULL;
if (declaration->backgroundColor.a > 0) {
@ -2088,10 +2131,7 @@ void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(parentItem->layoutElement - context->layoutElements.internalArray));
}
} else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ROOT) {
floatingConfig.parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0, 0).id;
}
if (!openLayoutElementId.id) {
openLayoutElementId = Clay__HashString(CLAY_STRING("Clay__FloatingContainer"), context->layoutElementTreeRoots.length, 0);
floatingConfig.parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0).id;
}
if (declaration->floating.clipTo == CLAY_CLIP_TO_NONE) {
clipElementId = 0;
@ -2112,12 +2152,6 @@ void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration->custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);
}
if (openLayoutElementId.id != 0) {
Clay__AttachId(openLayoutElementId);
} else if (openLayoutElement->id == 0) {
openLayoutElementId = Clay__GenerateIdForAnonymousElement(openLayoutElement);
}
if (declaration->clip.horizontal | declaration->clip.vertical) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .clipElementConfig = Clay__StoreClipElementConfig(declaration->clip) }, CLAY__ELEMENT_CONFIG_TYPE_CLIP);
Clay__int32_tArray_Add(&context->openClipElementStack, (int)openLayoutElement->id);
@ -2191,7 +2225,7 @@ void Clay__InitializePersistentMemory(Clay_Context* context) {
int32_t maxMeasureTextCacheWordCount = context->maxMeasureTextCacheWordCount;
Clay_Arena *arena = &context->internalArena;
context->scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(10, arena);
context->scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(100, arena);
context->layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(maxElementCount, arena);
context->layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(maxElementCount, arena);
context->measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(maxElementCount, arena);
@ -2227,17 +2261,37 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId);
if (parentItem && parentItem != &Clay_LayoutElementHashMapItem_DEFAULT) {
Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement;
if (rootElement->layoutConfig->sizing.width.type == CLAY__SIZING_TYPE_GROW) {
switch (rootElement->layoutConfig->sizing.width.type) {
case CLAY__SIZING_TYPE_GROW: {
rootElement->dimensions.width = parentLayoutElement->dimensions.width;
break;
}
if (rootElement->layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_GROW) {
case CLAY__SIZING_TYPE_PERCENT: {
rootElement->dimensions.width = parentLayoutElement->dimensions.width * rootElement->layoutConfig->sizing.width.size.percent;
break;
}
default: break;
}
switch (rootElement->layoutConfig->sizing.height.type) {
case CLAY__SIZING_TYPE_GROW: {
rootElement->dimensions.height = parentLayoutElement->dimensions.height;
break;
}
case CLAY__SIZING_TYPE_PERCENT: {
rootElement->dimensions.height = parentLayoutElement->dimensions.height * rootElement->layoutConfig->sizing.height.size.percent;
break;
}
default: break;
}
}
}
if (rootElement->layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {
rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.size.minMax.min), rootElement->layoutConfig->sizing.width.size.minMax.max);
}
if (rootElement->layoutConfig->sizing.height.type != CLAY__SIZING_TYPE_PERCENT) {
rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.size.minMax.min), rootElement->layoutConfig->sizing.height.size.minMax.max);
}
for (int32_t i = 0; i < bfsBuffer.length; ++i) {
int32_t parentIndex = Clay__int32_tArray_GetValue(&bfsBuffer, i);
@ -2512,7 +2566,7 @@ void Clay__CalculateFinalLayout(void) {
// measuredWord->length == 0 means a newline character
else if (measuredWord->length == 0 || lineWidth + measuredWord->width > containerElement->dimensions.width) {
// Wrapped text lines list has overflowed, just render out the line
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] } });
textElementData->wrappedLines.length++;
if (lineLengthChars == 0 || measuredWord->length == 0) {
@ -2690,7 +2744,6 @@ void Clay__CalculateFinalLayout(void) {
if (clipConfig->vertical) {
rootPosition.y += clipConfig->childOffset.y;
}
break;
}
Clay__AddRenderCommand(CLAY__INIT(Clay_RenderCommand) {
.boundingBox = clipHashMapItem->boundingBox,
@ -2747,12 +2800,6 @@ void Clay__CalculateFinalLayout(void) {
Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(currentElement->id);
if (hashMapItem) {
hashMapItem->boundingBox = currentElementBoundingBox;
if (hashMapItem->idAlias) {
Clay_LayoutElementHashMapItem *hashMapItemAlias = Clay__GetHashMapItem(hashMapItem->idAlias);
if (hashMapItemAlias) {
hashMapItemAlias->boundingBox = currentElementBoundingBox;
}
}
}
int32_t sortedConfigIndexes[20];
@ -2927,6 +2974,7 @@ void Clay__CalculateFinalLayout(void) {
default: break;
}
currentElementTreeNode->nextChildOffset.x += extraSpace;
extraSpace = CLAY__MAX(0, extraSpace);
} else {
for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
@ -2940,6 +2988,7 @@ void Clay__CalculateFinalLayout(void) {
case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break;
default: break;
}
extraSpace = CLAY__MAX(0, extraSpace);
currentElementTreeNode->nextChildOffset.y += extraSpace;
}
@ -3148,8 +3197,8 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
Clay__int32_tArray_Add(&dfsBuffer, (int32_t)root->layoutElementIndex);
context->treeNodeVisited.internalArray[0] = false;
if (rootIndex > 0) {
CLAY({ .id = CLAY_IDI("Clay__DebugView_EmptyRowOuter", rootIndex), .layout = { .sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2, 0, 0, 0} } }) {
CLAY({ .id = CLAY_IDI("Clay__DebugView_EmptyRow", rootIndex), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }}, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .top = 1 } } }) {}
CLAY(CLAY_IDI("Clay__DebugView_EmptyRowOuter", rootIndex), { .layout = { .sizing = {.width = CLAY_SIZING_GROW(0)}, .padding = {CLAY__DEBUGVIEW_INDENT_WIDTH / 2, 0, 0, 0} } }) {
CLAY(CLAY_IDI("Clay__DebugView_EmptyRow", rootIndex), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED((float)CLAY__DEBUGVIEW_ROW_HEIGHT) }}, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .top = 1 } } }) {}
}
layoutData.rowCount++;
}
@ -3179,11 +3228,10 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
if (context->debugSelectedElementId == currentElement->id) {
layoutData.selectedElementRowIndex = layoutData.rowCount;
}
CLAY({ .id = CLAY_IDI("Clay__DebugView_ElementOuter", currentElement->id), .layout = Clay__DebugView_ScrollViewItemLayoutConfig }) {
CLAY(CLAY_IDI("Clay__DebugView_ElementOuter", currentElement->id), { .layout = Clay__DebugView_ScrollViewItemLayoutConfig }) {
// Collapse icon / button
if (!(Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || currentElement->childrenOrTextContent.children.length == 0)) {
CLAY({
.id = CLAY_IDI("Clay__DebugView_CollapseElement", currentElement->id),
CLAY(CLAY_IDI("Clay__DebugView_CollapseElement", currentElement->id), {
.layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} },
.cornerRadius = CLAY_CORNER_RADIUS(4),
.border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = {1, 1, 1, 1, 0} },
@ -3191,19 +3239,19 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
CLAY_TEXT((currentElementData && currentElementData->debugData->collapsed) ? CLAY_STRING("+") : CLAY_STRING("-"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
}
} else { // Square dot for empty containers
CLAY({ .layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER } } }) {
CLAY({ .layout = { .sizing = {CLAY_SIZING_FIXED(8), CLAY_SIZING_FIXED(8)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3, .cornerRadius = CLAY_CORNER_RADIUS(2) }) {}
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_FIXED(16), CLAY_SIZING_FIXED(16)}, .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER } } }) {
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_FIXED(8), CLAY_SIZING_FIXED(8)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3, .cornerRadius = CLAY_CORNER_RADIUS(2) }) {}
}
}
// Collisions and offscreen info
if (currentElementData) {
if (currentElementData->debugData->collision) {
CLAY({ .layout = { .padding = { 8, 8, 2, 2 }}, .border = { .color = {177, 147, 8, 255}, .width = {1, 1, 1, 1, 0} } }) {
CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 }}, .border = { .color = {177, 147, 8, 255}, .width = {1, 1, 1, 1, 0} } }) {
CLAY_TEXT(CLAY_STRING("Duplicate ID"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }));
}
}
if (offscreen) {
CLAY({ .layout = { .padding = { 8, 8, 2, 2 } }, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { 1, 1, 1, 1, 0} } }) {
CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { 1, 1, 1, 1, 0} } }) {
CLAY_TEXT(CLAY_STRING("Offscreen"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }));
}
}
@ -3220,12 +3268,12 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
Clay_Color backgroundColor = elementConfig->config.sharedElementConfig->backgroundColor;
Clay_CornerRadius radius = elementConfig->config.sharedElementConfig->cornerRadius;
if (backgroundColor.a > 0) {
CLAY({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0} } }) {
CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0} } }) {
CLAY_TEXT(CLAY_STRING("Color"), CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
}
}
if (radius.bottomLeft > 0) {
CLAY({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0 } } }) {
CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = labelColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = labelColor, .width = { 1, 1, 1, 1, 0 } } }) {
CLAY_TEXT(CLAY_STRING("Radius"), CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
}
}
@ -3234,7 +3282,7 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(elementConfig->type);
Clay_Color backgroundColor = config.color;
backgroundColor.a = 90;
CLAY({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {
CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {
CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = offscreen ? CLAY__DEBUGVIEW_COLOR_3 : CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
}
}
@ -3245,8 +3293,8 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
layoutData.rowCount++;
Clay__TextElementData *textElementData = currentElement->childrenOrTextContent.textElementData;
Clay_TextElementConfig *rawTextConfig = offscreen ? CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16 }) : &Clay__DebugView_TextNameConfig;
CLAY({ .layout = { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {
CLAY({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16) } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { .height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {
CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_INDENT_WIDTH + 16) } } }) {}
CLAY_TEXT(CLAY_STRING("\""), rawTextConfig);
CLAY_TEXT(textElementData->text.length > 40 ? (CLAY__INIT(Clay_String) { .length = 40, .chars = textElementData->text.chars }) : textElementData->text, rawTextConfig);
if (textElementData->text.length > 40) {
@ -3274,7 +3322,7 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
}
if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
Clay_ElementId collapseButtonId = Clay__HashString(CLAY_STRING("Clay__DebugView_CollapseElement"), 0, 0);
Clay_ElementId collapseButtonId = Clay__HashString(CLAY_STRING("Clay__DebugView_CollapseElement"), 0);
for (int32_t i = (int)context->pointerOverIds.length - 1; i >= 0; i--) {
Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);
if (elementId->baseId == collapseButtonId.baseId) {
@ -3286,8 +3334,8 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
}
if (highlightedElementId) {
CLAY({ .id = CLAY_ID("Clay__DebugView_ElementHighlight"), .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .parentId = highlightedElementId, .zIndex = 32767, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID } }) {
CLAY({ .id = CLAY_ID("Clay__DebugView_ElementHighlightRectangle"), .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .backgroundColor = Clay__debugViewHighlightColor }) {}
CLAY(CLAY_ID("Clay__DebugView_ElementHighlight"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .parentId = highlightedElementId, .zIndex = 32767, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID } }) {
CLAY(CLAY_ID("Clay__DebugView_ElementHighlightRectangle"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .backgroundColor = Clay__debugViewHighlightColor }) {}
}
}
return layoutData;
@ -3328,17 +3376,17 @@ void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__Eleme
Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(type);
Clay_Color backgroundColor = config.color;
backgroundColor.a = 90;
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(CLAY__DEBUGVIEW_OUTER_PADDING), .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {
CLAY({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {
CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(CLAY__DEBUGVIEW_OUTER_PADDING), .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } } }) {
CLAY_AUTO_ID({ .layout = { .padding = { 8, 8, 2, 2 } }, .backgroundColor = backgroundColor, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = config.color, .width = { 1, 1, 1, 1, 0 } } }) {
CLAY_TEXT(config.label, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
}
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
CLAY_TEXT(elementId, CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE }));
}
}
void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) {
CLAY({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_AUTO_ID({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_TEXT(CLAY_STRING("{ r: "), textConfig);
CLAY_TEXT(Clay__IntToString(color.r), textConfig);
CLAY_TEXT(CLAY_STRING(", g: "), textConfig);
@ -3348,13 +3396,13 @@ void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textCo
CLAY_TEXT(CLAY_STRING(", a: "), textConfig);
CLAY_TEXT(Clay__IntToString(color.a), textConfig);
CLAY_TEXT(CLAY_STRING(" }"), textConfig);
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_FIXED(10) } } }) {}
CLAY({ .layout = { .sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)} }, .backgroundColor = color, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = CLAY__DEBUGVIEW_COLOR_4, .width = { 1, 1, 1, 1, 0 } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_FIXED(10) } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 8)} }, .backgroundColor = color, .cornerRadius = CLAY_CORNER_RADIUS(4), .border = { .color = CLAY__DEBUGVIEW_COLOR_4, .width = { 1, 1, 1, 1, 0 } } }) {}
}
}
void Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) {
CLAY({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_AUTO_ID({ .layout = { .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_TEXT(CLAY_STRING("{ topLeft: "), textConfig);
CLAY_TEXT(Clay__IntToString(cornerRadius.topLeft), textConfig);
CLAY_TEXT(CLAY_STRING(", topRight: "), textConfig);
@ -3377,7 +3425,7 @@ void HandleDebugViewCloseButtonInteraction(Clay_ElementId elementId, Clay_Pointe
void Clay__RenderDebugView(void) {
Clay_Context* context = Clay_GetCurrentContext();
Clay_ElementId closeButtonId = Clay__HashString(CLAY_STRING("Clay__DebugViewTopHeaderCloseButtonOuter"), 0, 0);
Clay_ElementId closeButtonId = Clay__HashString(CLAY_STRING("Clay__DebugViewTopHeaderCloseButtonOuter"), 0);
if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {
Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);
@ -3392,7 +3440,7 @@ void Clay__RenderDebugView(void) {
uint32_t initialElementsLength = context->layoutElements.length;
Clay_TextElementConfig *infoTextConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });
Clay_TextElementConfig *infoTitleConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_3, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });
Clay_ElementId scrollId = Clay__HashString(CLAY_STRING("Clay__DebugViewOuterScrollPane"), 0, 0);
Clay_ElementId scrollId = Clay__HashString(CLAY_STRING("Clay__DebugViewOuterScrollPane"), 0);
float scrollYOffset = 0;
bool pointerInDebugView = context->pointerInfo.position.y < context->layoutDimensions.height - 300;
for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) {
@ -3413,16 +3461,16 @@ void Clay__RenderDebugView(void) {
highlightedRow = -1;
}
Clay__RenderDebugLayoutData layoutData = CLAY__DEFAULT_STRUCT;
CLAY({ .id = CLAY_ID("Clay__DebugView"),
CLAY(CLAY_ID("Clay__DebugView"), {
.layout = { .sizing = { CLAY_SIZING_FIXED((float)Clay__debugViewWidth) , CLAY_SIZING_FIXED(context->layoutDimensions.height) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },
.floating = { .zIndex = 32765, .attachPoints = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_CENTER }, .attachTo = CLAY_ATTACH_TO_ROOT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT },
.border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .bottom = 1 } }
}) {
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2 }) {
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2 }) {
CLAY_TEXT(CLAY_STRING("Clay Debug Tools"), infoTextConfig);
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
// Close button
CLAY({
CLAY_AUTO_ID({
.layout = { .sizing = {CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT - 10)}, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} },
.backgroundColor = {217,91,67,80},
.cornerRadius = CLAY_CORNER_RADIUS(4),
@ -3432,18 +3480,18 @@ void Clay__RenderDebugView(void) {
CLAY_TEXT(CLAY_STRING("x"), CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16 }));
}
}
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 } ) {}
CLAY({ .id = scrollId, .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1 }) {
Clay_ElementId panelContentsId = Clay__HashString(CLAY_STRING("Clay__DebugViewPaneOuter"), 0, 0);
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 } ) {}
CLAY(scrollId, { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = ((initialElementsLength + initialRootsLength) & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1 }) {
Clay_ElementId panelContentsId = Clay__HashString(CLAY_STRING("Clay__DebugViewPaneOuter"), 0);
// Element list
CLAY({ .id = panelContentsId, .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .zIndex = 32766, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_PARENT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT } }) {
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = { CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY(panelContentsId, { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)} }, .floating = { .zIndex = 32766, .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, .attachTo = CLAY_ATTACH_TO_PARENT, .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT } }) {
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = { CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
layoutData = Clay__RenderDebugLayoutElementsList((int32_t)initialRootsLength, highlightedRow);
}
}
float contentWidth = Clay__GetHashMapItem(panelContentsId.id)->layoutElement->dimensions.width;
CLAY({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(contentWidth) }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(contentWidth) }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {}
for (int32_t i = 0; i < layoutData.rowCount; i++) {
Clay_Color rowColor = (i & 1) == 0 ? CLAY__DEBUGVIEW_COLOR_2 : CLAY__DEBUGVIEW_COLOR_1;
if (i == layoutData.selectedElementRowIndex) {
@ -3454,22 +3502,22 @@ void Clay__RenderDebugView(void) {
rowColor.g *= 1.25f;
rowColor.b *= 1.25f;
}
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = rowColor } ) {}
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = rowColor } ) {}
}
}
}
CLAY({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 }) {}
CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 }) {}
if (context->debugSelectedElementId != 0) {
Clay_LayoutElementHashMapItem *selectedItem = Clay__GetHashMapItem(context->debugSelectedElementId);
CLAY({
CLAY_AUTO_ID({
.layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .layoutDirection = CLAY_TOP_TO_BOTTOM },
.backgroundColor = CLAY__DEBUGVIEW_COLOR_2 ,
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
.border = { .color = CLAY__DEBUGVIEW_COLOR_3, .width = { .betweenChildren = 1 } }
}) {
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_AUTO_ID({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT + 8)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_TEXT(CLAY_STRING("Layout Config"), infoTextConfig);
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
if (selectedItem->elementId.stringId.length != 0) {
CLAY_TEXT(selectedItem->elementId.stringId, infoTitleConfig);
if (selectedItem->elementId.offset != 0) {
@ -3481,10 +3529,10 @@ void Clay__RenderDebugView(void) {
}
Clay_Padding attributeConfigPadding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 8, 8};
// Clay_LayoutConfig debug info
CLAY({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// .boundingBox
CLAY_TEXT(CLAY_STRING("Bounding Box"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(selectedItem->boundingBox.x), infoTextConfig);
CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig);
@ -3501,17 +3549,17 @@ void Clay__RenderDebugView(void) {
CLAY_TEXT(layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM ? CLAY_STRING("TOP_TO_BOTTOM") : CLAY_STRING("LEFT_TO_RIGHT"), infoTextConfig);
// .sizing
CLAY_TEXT(CLAY_STRING("Sizing"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("width: "), infoTextConfig);
Clay__RenderDebugLayoutSizing(layoutConfig->sizing.width, infoTextConfig);
}
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("height: "), infoTextConfig);
Clay__RenderDebugLayoutSizing(layoutConfig->sizing.height, infoTextConfig);
}
// .padding
CLAY_TEXT(CLAY_STRING("Padding"), infoTitleConfig);
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoPadding") }) {
CLAY(CLAY_ID("Clay__DebugViewElementInfoPadding"), { }) {
CLAY_TEXT(CLAY_STRING("{ left: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(layoutConfig->padding.left), infoTextConfig);
CLAY_TEXT(CLAY_STRING(", right: "), infoTextConfig);
@ -3527,7 +3575,7 @@ void Clay__RenderDebugView(void) {
CLAY_TEXT(Clay__IntToString(layoutConfig->childGap), infoTextConfig);
// .childAlignment
CLAY_TEXT(CLAY_STRING("Child Alignment"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig);
Clay_String alignX = CLAY_STRING("LEFT");
if (layoutConfig->childAlignment.x == CLAY_ALIGN_X_CENTER) {
@ -3553,7 +3601,7 @@ void Clay__RenderDebugView(void) {
switch (elementConfig->type) {
case CLAY__ELEMENT_CONFIG_TYPE_SHARED: {
Clay_SharedElementConfig *sharedConfig = elementConfig->config.sharedElementConfig;
CLAY({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
// .backgroundColor
CLAY_TEXT(CLAY_STRING("Background Color"), infoTitleConfig);
Clay__RenderDebugViewColor(sharedConfig->backgroundColor, infoTextConfig);
@ -3565,7 +3613,7 @@ void Clay__RenderDebugView(void) {
}
case CLAY__ELEMENT_CONFIG_TYPE_TEXT: {
Clay_TextElementConfig *textConfig = elementConfig->config.textElementConfig;
CLAY({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// .fontSize
CLAY_TEXT(CLAY_STRING("Font Size"), infoTitleConfig);
CLAY_TEXT(Clay__IntToString(textConfig->fontSize), infoTextConfig);
@ -3604,10 +3652,19 @@ void Clay__RenderDebugView(void) {
}
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(CLAY_ID("Clay__DebugViewElementInfoAspectRatioBody"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_TEXT(CLAY_STRING("Aspect Ratio"), infoTitleConfig);
// Aspect Ratio
CLAY(CLAY_ID("Clay__DebugViewElementInfoAspectRatio"), { }) {
CLAY_TEXT(Clay__IntToString(aspectRatioConfig->aspectRatio), infoTextConfig);
CLAY_TEXT(CLAY_STRING("."), infoTextConfig);
float frac = aspectRatioConfig->aspectRatio - (int)(aspectRatioConfig->aspectRatio);
frac *= 100;
if ((int)frac < 10) {
CLAY_TEXT(CLAY_STRING("0"), infoTextConfig);
}
CLAY_TEXT(Clay__IntToString(frac), infoTextConfig);
}
}
break;
}
@ -3617,16 +3674,16 @@ void Clay__RenderDebugView(void) {
if (Clay__ElementHasConfig(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT)) {
aspectConfig = *Clay__FindElementConfigWithType(selectedItem->layoutElement, CLAY__ELEMENT_CONFIG_TYPE_ASPECT).aspectRatioElementConfig;
}
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoImageBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY(CLAY_ID("Clay__DebugViewElementInfoImageBody"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// Image Preview
CLAY_TEXT(CLAY_STRING("Preview"), infoTitleConfig);
CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(64, 128), .height = CLAY_SIZING_GROW(64, 128) }}, .aspectRatio = aspectConfig, .image = *imageConfig }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { .width = CLAY_SIZING_GROW(64, 128), .height = CLAY_SIZING_GROW(64, 128) }}, .aspectRatio = aspectConfig, .image = *imageConfig }) {}
}
break;
}
case CLAY__ELEMENT_CONFIG_TYPE_CLIP: {
Clay_ClipElementConfig *clipConfig = elementConfig->config.clipElementConfig;
CLAY({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// .vertical
CLAY_TEXT(CLAY_STRING("Vertical"), infoTitleConfig);
CLAY_TEXT(clipConfig->vertical ? CLAY_STRING("true") : CLAY_STRING("false") , infoTextConfig);
@ -3638,10 +3695,10 @@ void Clay__RenderDebugView(void) {
}
case CLAY__ELEMENT_CONFIG_TYPE_FLOATING: {
Clay_FloatingElementConfig *floatingConfig = elementConfig->config.floatingElementConfig;
CLAY({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_AUTO_ID({ .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
// .offset
CLAY_TEXT(CLAY_STRING("Offset"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("{ x: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(floatingConfig->offset.x), infoTextConfig);
CLAY_TEXT(CLAY_STRING(", y: "), infoTextConfig);
@ -3650,7 +3707,7 @@ void Clay__RenderDebugView(void) {
}
// .expand
CLAY_TEXT(CLAY_STRING("Expand"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("{ width: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(floatingConfig->expand.width), infoTextConfig);
CLAY_TEXT(CLAY_STRING(", height: "), infoTextConfig);
@ -3666,7 +3723,7 @@ void Clay__RenderDebugView(void) {
CLAY_TEXT(hashItem->elementId.stringId, infoTextConfig);
// .attachPoints
CLAY_TEXT(CLAY_STRING("Attach Points"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("{ element: "), infoTextConfig);
Clay_String attachPointElement = CLAY_STRING("LEFT_TOP");
if (floatingConfig->attachPoints.element == CLAY_ATTACH_POINT_LEFT_CENTER) {
@ -3739,9 +3796,9 @@ void Clay__RenderDebugView(void) {
}
case CLAY__ELEMENT_CONFIG_TYPE_BORDER: {
Clay_BorderElementConfig *borderConfig = elementConfig->config.borderElementConfig;
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoBorderBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY(CLAY_ID("Clay__DebugViewElementInfoBorderBody"), { .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_TEXT(CLAY_STRING("Border Widths"), infoTitleConfig);
CLAY({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT } }) {
CLAY_TEXT(CLAY_STRING("{ left: "), infoTextConfig);
CLAY_TEXT(Clay__IntToString(borderConfig->width.left), infoTextConfig);
CLAY_TEXT(CLAY_STRING(", right: "), infoTextConfig);
@ -3764,16 +3821,16 @@ void Clay__RenderDebugView(void) {
}
}
} else {
CLAY({ .id = CLAY_ID("Clay__DebugViewWarningsScrollPane"), .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .childGap = 6, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
CLAY(CLAY_ID("Clay__DebugViewWarningsScrollPane"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(300)}, .childGap = 6, .layoutDirection = CLAY_TOP_TO_BOTTOM }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_2, .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
Clay_TextElementConfig *warningConfig = CLAY_TEXT_CONFIG({ .textColor = CLAY__DEBUGVIEW_COLOR_4, .fontSize = 16, .wrapMode = CLAY_TEXT_WRAP_NONE });
CLAY({ .id = CLAY_ID("Clay__DebugViewWarningItemHeader"), .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY(CLAY_ID("Clay__DebugViewWarningItemHeader"), { .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_TEXT(CLAY_STRING("Warnings"), warningConfig);
}
CLAY({ .id = CLAY_ID("Clay__DebugViewWarningsTopBorder"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = {200, 200, 200, 255} }) {}
CLAY(CLAY_ID("Clay__DebugViewWarningsTopBorder"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)} }, .backgroundColor = {200, 200, 200, 255} }) {}
int32_t previousWarningsLength = context->warnings.length;
for (int32_t i = 0; i < previousWarningsLength; i++) {
Clay__Warning warning = context->warnings.internalArray[i];
CLAY({ .id = CLAY_IDI("Clay__DebugViewWarningItem", i), .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY(CLAY_IDI("Clay__DebugViewWarningItem", i), { .layout = { .sizing = {.height = CLAY_SIZING_FIXED(CLAY__DEBUGVIEW_ROW_HEIGHT)}, .padding = {CLAY__DEBUGVIEW_OUTER_PADDING, CLAY__DEBUGVIEW_OUTER_PADDING, 0, 0 }, .childGap = 8, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} } }) {
CLAY_TEXT(warning.baseMessage, warningConfig);
if (warning.dynamicMessage.length > 0) {
CLAY_TEXT(warning.dynamicMessage, warningConfig);
@ -3818,7 +3875,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)
{
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) {
arena->nextAllocation = nextAllocOffset + totalSizeBytes;
return (void*)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);
@ -3938,16 +3995,12 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
Clay_BoundingBox elementBox = mapItem->boundingBox;
elementBox.x -= root->pointerOffset.x;
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) {
mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData);
}
Clay_ElementIdArray_Add(&context->pointerOverIds, mapItem->elementId);
found = true;
if (mapItem->idAlias != 0) {
Clay_ElementIdArray_Add(&context->pointerOverIds, CLAY__INIT(Clay_ElementId) { .id = mapItem->idAlias });
}
}
if (Clay__ElementHasConfig(currentElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT)) {
dfsBuffer.length--;
@ -3986,6 +4039,10 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
CLAY_WASM_EXPORT("Clay_Initialize")
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);
if (context == NULL) return NULL;
// DEFAULTS
@ -4167,9 +4224,8 @@ void Clay_BeginLayout(void) {
rootDimensions.width -= (float)Clay__debugViewWidth;
}
context->booleanWarnings = CLAY__INIT(Clay_BooleanWarnings) CLAY__DEFAULT_STRUCT;
Clay__OpenElement();
Clay__OpenElementWithId(CLAY_ID("Clay__RootContainer"));
Clay__ConfigureOpenElement(CLAY__INIT(Clay_ElementDeclaration) {
.id = CLAY_ID("Clay__RootContainer"),
.layout = { .sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)} }
});
Clay__int32_tArray_Add(&context->openLayoutElementStack, 0);
@ -4198,20 +4254,25 @@ Clay_RenderCommandArray Clay_EndLayout(void) {
.renderData = { .text = { .stringContents = CLAY__INIT(Clay_StringSlice) { .length = message.length, .chars = message.chars, .baseChars = message.chars }, .textColor = {255, 0, 0, 255}, .fontSize = 16 } },
.commandType = CLAY_RENDER_COMMAND_TYPE_TEXT
});
} else {
Clay__CalculateFinalLayout();
}
if (context->openLayoutElementStack.length > 1) {
context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
.errorType = CLAY_ERROR_TYPE_UNBALANCED_OPEN_CLOSE,
.errorText = CLAY_STRING("There were still open layout elements when EndLayout was called. This results from an unequal number of calls to Clay__OpenElement and Clay__CloseElement."),
.userData = context->errorHandler.userData });
}
Clay__CalculateFinalLayout();
return context->renderCommands;
}
CLAY_WASM_EXPORT("Clay_GetElementId")
Clay_ElementId Clay_GetElementId(Clay_String idString) {
return Clay__HashString(idString, 0, 0);
return Clay__HashString(idString, 0);
}
CLAY_WASM_EXPORT("Clay_GetElementIdWithIndex")
Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index) {
return Clay__HashString(idString, index, 0);
return Clay__HashStringWithOffset(idString, index, 0);
}
bool Clay_Hovered(void) {

View file

@ -32,6 +32,7 @@ static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextEl
TTF_Font *font = fonts[config->fontId];
int width, height;
TTF_SetFontSize(font, config->fontSize);
if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s", SDL_GetError());
}
@ -52,7 +53,7 @@ Clay_RenderCommandArray ClayImageSample_CreateLayout() {
.height = CLAY_SIZING_GROW(0)
};
CLAY({ .id = CLAY_ID("OuterContainer"),
CLAY(CLAY_ID("OuterContainer"), {
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = layoutExpand,
@ -60,17 +61,13 @@ Clay_RenderCommandArray ClayImageSample_CreateLayout() {
.childGap = 16
}
}) {
CLAY({
.id = CLAY_ID("SampleImage"),
CLAY(CLAY_ID("SampleImage"), {
.layout = {
.sizing = layoutExpand
},
.aspectRatio = { 23.0 / 42.0 },
.image = {
.imageData = sample_image,
.sourceDimensions = {
.width = 23,
.height = 42
},
}
});
}

View file

@ -40,12 +40,12 @@ void Layout() {
static Clay_Color BACKGROUND = { 0xF4, 0xEB, 0xE6, 255 };
static Clay_Color ACCENT = { 0xFA, 0xE0, 0xD4, 255 };
CLAY({
CLAY_AUTO_ID({
.layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
.layoutDirection = CLAY_TOP_TO_BOTTOM },
.backgroundColor = BACKGROUND
}) {
CLAY({ .id = CLAY_ID("PageMargins"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
CLAY(CLAY_ID("PageMargins"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
.padding = { 70, 70, 50, 50 }, // Some nice looking page margins
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childGap = 10}
@ -54,9 +54,9 @@ void Layout() {
CLAY_TEXT(CLAY_STRING("Features Overview"), CLAY_TEXT_CONFIG({ .fontId = FONT_CALLISTOGA, .textColor = PRIMARY, .fontSize = 24 }));
// Feature Box
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }, .childGap = 10 }}) {
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }}, .backgroundColor = ACCENT, .cornerRadius = CLAY_CORNER_RADIUS(12) }) {
CLAY({ .layout = {.padding = CLAY_PADDING_ALL(20), .childGap = 4, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }, .childGap = 10 }}) {
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }}, .backgroundColor = ACCENT, .cornerRadius = CLAY_CORNER_RADIUS(12) }) {
CLAY_AUTO_ID({ .layout = {.padding = CLAY_PADDING_ALL(20), .childGap = 4, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
CLAY_TEXT(CLAY_STRING("- High performance"),
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
CLAY_TEXT(CLAY_STRING("- Declarative syntax"),
@ -69,7 +69,7 @@ void Layout() {
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
}
}
CLAY({
CLAY_AUTO_ID({
.layout = {
.sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_GROW(0)},
.padding = CLAY_PADDING_ALL(10),
@ -81,23 +81,23 @@ void Layout() {
.cornerRadius = CLAY_CORNER_RADIUS(8)
}) {
// Profile picture
CLAY({ .layout = {
CLAY_AUTO_ID({ .layout = {
.sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_GROW(0)},
.padding = { 30, 30, 0, 0 },
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }},
.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_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_FIXED(32), CLAY_SIZING_FIXED(32) } }, .image = { .imageData = "resources/check.png" }});
}
}
}
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(16) } }});
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(16) } }});
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childGap = 10, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childGap = 10, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
CLAY_TEXT(CLAY_STRING("Cairo"), CLAY_TEXT_CONFIG({ .fontId = FONT_CALLISTOGA, .fontSize = 24, .textColor = PRIMARY }));
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(10) }, .backgroundColor = ACCENT, .cornerRadius = 10 }) {
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(10) }, .backgroundColor = ACCENT, .cornerRadius = 10 }) {
CLAY_TEXT(CLAY_STRING("Officiis quia quia qui inventore ratione voluptas et. Quidem sunt unde similique. Qui est et exercitationem cumque harum illum. Numquam placeat aliquid quo voluptatem. "
"Deleniti saepe nihil exercitationem nemo illo. Consequatur beatae repellat provident similique. Provident qui exercitationem deserunt sapiente. Quam qui dolor corporis odit. "
"Assumenda corrupti sunt culpa pariatur. Vero sit ut minima. In est consequatur minus et cum sint illum aperiam. Qui ipsa quas nisi omnis aut quia nobis. "

View file

@ -15,5 +15,5 @@ mkdir -p build/clay \
-Wl,--initial-memory=6553600 \
-o build/clay/index.wasm \
main.c \
&& cp index.html build/clay/index.html && cp -r fonts/ build/clay/fonts \
&& cp index.html build/clay/index.html && cp -r images/ build/clay/images
&& cp index.html build/index.html && cp -r fonts/ build/clay/fonts \
&& cp -r images/ build/clay/images

View file

@ -166,6 +166,7 @@
{ name: 'link', ...stringDefinition },
{ name: 'cursorPointer', type: 'uint8_t' },
{ name: 'disablePointerEvents', type: 'uint8_t' },
{ name: 'padding', type: 'uint16_t'}
]};
let renderCommandDefinition = {
name: 'Clay_RenderCommand',
@ -425,10 +426,13 @@
if (!elementCache[renderCommand.id.value]) {
let elementType = 'div';
switch (renderCommand.commandType.value & 0xff) {
case CLAY_RENDER_COMMAND_TYPE_TEXT:
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
// if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links
// elementType = 'a';
// }
if (renderCommand.userData.value !== 0) {
if (readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition).link.length.value > 0) {
elementType = 'a';
}
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
@ -549,7 +553,6 @@
}
case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = renderCommand.renderData.text;
let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
let configMemory = JSON.stringify(config);
let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));
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.fontFamily = fontsById[config.fontId.value];
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';
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;
}
if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {

View file

@ -166,6 +166,7 @@
{ name: 'link', ...stringDefinition },
{ name: 'cursorPointer', type: 'uint8_t' },
{ name: 'disablePointerEvents', type: 'uint8_t' },
{ name: 'padding', type: 'uint16_t'}
]};
let renderCommandDefinition = {
name: 'Clay_RenderCommand',
@ -425,10 +426,13 @@
if (!elementCache[renderCommand.id.value]) {
let elementType = 'div';
switch (renderCommand.commandType.value & 0xff) {
case CLAY_RENDER_COMMAND_TYPE_TEXT:
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
// if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links
// elementType = 'a';
// }
if (renderCommand.userData.value !== 0) {
if (readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition).link.length.value > 0) {
elementType = 'a';
}
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
@ -549,7 +553,6 @@
}
case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = renderCommand.renderData.text;
let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
let configMemory = JSON.stringify(config);
let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));
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.fontFamily = fontsById[config.fontId.value];
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';
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;
}
if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {

View file

@ -44,7 +44,7 @@ typedef struct {
Arena frameArena = {};
typedef struct {
typedef struct d {
Clay_String link;
bool cursorPointer;
bool disablePointerEvents;
@ -65,21 +65,21 @@ Clay_String* FrameAllocateString(Clay_String string) {
}
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("CheckImage", index), .layout = { .sizing = { CLAY_SIZING_FIXED(32) } }, .aspectRatio = { 1 }, .image = { .imageData = FrameAllocateString(imageURL) } }) {}
CLAY(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(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 }));
}
}
void LandingPageDesktop() {
CLAY({ .id = CLAY_ID("LandingPage1Desktop"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
CLAY({ .id = CLAY_ID("LandingPage1"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {
CLAY({ .id = CLAY_ID("LeftText"), .layout = { .sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("LandingPage1Desktop"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
CLAY(CLAY_ID("LandingPage1"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {
CLAY(CLAY_ID("LeftText"), { .layout = { .sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance."), CLAY_TEXT_CONFIG({ .fontSize = 56, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
CLAY({ .id = CLAY_ID("LandingPageSpacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
CLAY(CLAY_ID("LandingPageSpacer"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
CLAY_TEXT(CLAY_STRING("Clay is laying out this webpage right now!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE }));
}
CLAY({ .id = CLAY_ID("HeroImageOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_PERCENT(0.45f) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) {
CLAY(CLAY_ID("HeroImageOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_PERCENT(0.45f) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) {
LandingPageBlob(1, 32, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), CLAY_STRING("/clay/images/check_5.png"));
LandingPageBlob(2, 32, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), CLAY_STRING("/clay/images/check_4.png"));
LandingPageBlob(3, 32, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), CLAY_STRING("/clay/images/check_3.png"));
@ -91,13 +91,13 @@ void LandingPageDesktop() {
}
void LandingPageMobile() {
CLAY({ .id = CLAY_ID("LandingPage1Mobile"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32 }, .childGap = 32 } }) {
CLAY({ .id = CLAY_ID("LeftText"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("LandingPage1Mobile"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32 }, .childGap = 32 } }) {
CLAY(CLAY_ID("LeftText"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance."), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
CLAY({ .id = CLAY_ID("LandingPageSpacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
CLAY(CLAY_ID("LandingPageSpacer"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
CLAY_TEXT(CLAY_STRING("Clay is laying out this webpage right now!"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE }));
}
CLAY({ .id = CLAY_ID("HeroImageOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) {
CLAY(CLAY_ID("HeroImageOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) {
LandingPageBlob(1, 28, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), CLAY_STRING("/clay/images/check_5.png"));
LandingPageBlob(2, 28, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), CLAY_STRING("/clay/images/check_4.png"));
LandingPageBlob(3, 28, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), CLAY_STRING("/clay/images/check_3.png"));
@ -108,17 +108,17 @@ void LandingPageMobile() {
}
void FeatureBlocksDesktop() {
CLAY({ .id = CLAY_ID("FeatureBlocksOuter"), .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {
CLAY({ .id = CLAY_ID("FeatureBlocksInner"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {
CLAY(CLAY_ID("FeatureBlocksOuter"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {
CLAY(CLAY_ID("FeatureBlocksInner"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {
Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED });
CLAY({ .id = CLAY_ID("HFileBoxOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {
CLAY({ .id = CLAY_ID("HFileIncludeOuter"), .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
CLAY(CLAY_ID("HFileBoxOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {
CLAY(CLAY_ID("HFileIncludeOuter"), { .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
CLAY_TEXT(CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT }));
}
CLAY_TEXT(CLAY_STRING("~2000 lines of C99."), textConfig);
CLAY_TEXT(CLAY_STRING("Zero dependencies, including no C standard library."), textConfig);
}
CLAY({ .id = CLAY_ID("BringYourOwnRendererOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {
CLAY(CLAY_ID("BringYourOwnRendererOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE }));
CLAY_TEXT(CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig);
CLAY_TEXT(CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig);
@ -128,16 +128,16 @@ void FeatureBlocksDesktop() {
}
void FeatureBlocksMobile() {
CLAY({ .id = CLAY_ID("FeatureBlocksInner"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {
CLAY(CLAY_ID("FeatureBlocksInner"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {
Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED });
CLAY({ .id = CLAY_ID("HFileBoxOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {
CLAY({ .id = CLAY_ID("HFileIncludeOuter"), .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
CLAY(CLAY_ID("HFileBoxOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {
CLAY(CLAY_ID("HFileIncludeOuter"), { .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
CLAY_TEXT(CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT }));
}
CLAY_TEXT(CLAY_STRING("~2000 lines of C99."), textConfig);
CLAY_TEXT(CLAY_STRING("Zero dependencies, including no C standard library."), textConfig);
}
CLAY({ .id = CLAY_ID("BringYourOwnRendererOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {
CLAY(CLAY_ID("BringYourOwnRendererOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE }));
CLAY_TEXT(CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig);
CLAY_TEXT(CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig);
@ -146,33 +146,33 @@ void FeatureBlocksMobile() {
}
void DeclarativeSyntaxPageDesktop() {
CLAY({ .id = CLAY_ID("SyntaxPageDesktop"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
CLAY({ .id = CLAY_ID("SyntaxPage"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED }}) {
CLAY({ .id = CLAY_ID("SyntaxPageLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("SyntaxPageDesktop"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
CLAY(CLAY_ID("SyntaxPage"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED }}) {
CLAY(CLAY_ID("SyntaxPageLeftText"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
CLAY({ .id = CLAY_ID("SyntaxSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}
CLAY(CLAY_ID("SyntaxSpacer"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}
CLAY_TEXT(CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
CLAY_TEXT(CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), 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("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
CLAY(CLAY_ID("SyntaxPageRightImage"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) {
CLAY(CLAY_ID("SyntaxPageRightImageInner"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
}
}
}
}
void DeclarativeSyntaxPageMobile() {
CLAY({ .id = CLAY_ID("SyntaxPageDesktop"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 16 } }) {
CLAY({ .id = CLAY_ID("SyntaxPageLeftText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("SyntaxPageDesktop"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 16 } }) {
CLAY(CLAY_ID("SyntaxPageLeftText"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
CLAY({ .id = CLAY_ID("SyntaxSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}
CLAY(CLAY_ID("SyntaxSpacer"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}
CLAY_TEXT(CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
CLAY_TEXT(CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), 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("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
CLAY(CLAY_ID("SyntaxPageRightImage"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) {
CLAY(CLAY_ID("SyntaxPageRightImageInner"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .aspectRatio = { 1136.0 / 1194.0 }, .image = { .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
}
}
}
@ -189,20 +189,20 @@ Clay_Color ColorLerp(Clay_Color a, Clay_Color b, float amount) {
Clay_String LOREM_IPSUM_TEXT = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
void HighPerformancePageDesktop(float lerpValue) {
CLAY({ .id = CLAY_ID("PerformanceOuter"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {82, 82, 32, 32}, .childGap = 64 }, .backgroundColor = COLOR_RED }) {
CLAY({ .id = CLAY_ID("PerformanceLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("PerformanceOuter"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {82, 82, 32, 32}, .childGap = 64 }, .backgroundColor = COLOR_RED }) {
CLAY(CLAY_ID("PerformanceLeftText"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
CLAY({ .id = CLAY_ID("PerformanceSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY(CLAY_ID("PerformanceSpacer"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY_TEXT(CLAY_STRING("Fast enough to recompute your entire UI every frame."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
CLAY_TEXT(CLAY_STRING("Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
CLAY_TEXT(CLAY_STRING("Simplify animations and reactive UI design by avoiding the standard performance hacks."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
}
CLAY({ .id = CLAY_ID("PerformanceRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_LIGHT } }) {
CLAY({ .id = CLAY_ID("AnimationDemoContainerLeft"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {
CLAY(CLAY_ID("PerformanceRightImageOuter"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_LIGHT } }) {
CLAY(CLAY_ID("AnimationDemoContainerLeft"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
}
CLAY({ .id = CLAY_ID("AnimationDemoContainerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {
CLAY(CLAY_ID("AnimationDemoContainerRight"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
}
}
@ -211,20 +211,20 @@ void HighPerformancePageDesktop(float lerpValue) {
}
void HighPerformancePageMobile(float lerpValue) {
CLAY({ .id = CLAY_ID("PerformanceOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_RED }) {
CLAY({ .id = CLAY_ID("PerformanceLeftText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("PerformanceOuter"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_RED }) {
CLAY(CLAY_ID("PerformanceLeftText"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
CLAY({ .id = CLAY_ID("PerformanceSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY(CLAY_ID("PerformanceSpacer"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY_TEXT(CLAY_STRING("Fast enough to recompute your entire UI every frame."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
CLAY_TEXT(CLAY_STRING("Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
CLAY_TEXT(CLAY_STRING("Simplify animations and reactive UI design by avoiding the standard performance hacks."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
}
CLAY({ .id = CLAY_ID("PerformanceRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
CLAY({ .id = CLAY_ID(""), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = { 2, 2, 2, 2 }, .color = COLOR_LIGHT }}) {
CLAY({ .id = CLAY_ID("AnimationDemoContainerLeft"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {
CLAY(CLAY_ID("PerformanceRightImageOuter"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = { 2, 2, 2, 2 }, .color = COLOR_LIGHT }}) {
CLAY(CLAY_ID("AnimationDemoContainerLeft"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
}
CLAY({ .id = CLAY_ID("AnimationDemoContainerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {
CLAY(CLAY_ID("AnimationDemoContainerRight"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
}
}
@ -241,7 +241,7 @@ void HandleRendererButtonInteraction(Clay_ElementId elementId, Clay_PointerData
}
void RendererButtonActive(Clay_String text) {
CLAY({
CLAY_AUTO_ID({
.layout = { .sizing = {CLAY_SIZING_FIXED(300) }, .padding = CLAY_PADDING_ALL(16) },
.backgroundColor = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED,
.cornerRadius = CLAY_CORNER_RADIUS(10),
@ -252,7 +252,7 @@ void RendererButtonActive(Clay_String text) {
}
void RendererButtonInactive(Clay_String text, size_t rendererIndex) {
CLAY({
CLAY_AUTO_ID({
.layout = { .sizing = {CLAY_SIZING_FIXED(300)}, .padding = CLAY_PADDING_ALL(16) },
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
@ -265,18 +265,18 @@ void RendererButtonInactive(Clay_String text, size_t rendererIndex) {
}
void RendererPageDesktop() {
CLAY({ .id = CLAY_ID("RendererPageDesktop"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
CLAY({ .id = CLAY_ID("RendererPage"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {
CLAY({ .id = CLAY_ID("RendererLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("RendererPageDesktop"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
CLAY(CLAY_ID("RendererPage"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {
CLAY(CLAY_ID("RendererLeftText"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
CLAY({ .id = CLAY_ID("RendererSpacerLeft"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY(CLAY_ID("RendererSpacerLeft"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY_TEXT(CLAY_STRING("Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
CLAY_TEXT(CLAY_STRING("Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
CLAY_TEXT(CLAY_STRING("There's even an HTML renderer - you're looking at it right now!"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
}
CLAY({ .id = CLAY_ID("RendererRightText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .childAlignment = {CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
CLAY(CLAY_ID("RendererRightText"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .childAlignment = {CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
CLAY_TEXT(CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE }));
CLAY({ .id = CLAY_ID("RendererSpacerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) } } }) {}
CLAY(CLAY_ID("RendererSpacerRight"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) } } }) {}
if (ACTIVE_RENDERER_INDEX == 0) {
RendererButtonActive(CLAY_STRING("HTML Renderer"));
RendererButtonInactive(CLAY_STRING("Canvas Renderer"), 1);
@ -290,17 +290,17 @@ void RendererPageDesktop() {
}
void RendererPageMobile() {
CLAY({ .id = CLAY_ID("RendererMobile"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_LIGHT }) {
CLAY({ .id = CLAY_ID("RendererLeftText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("RendererMobile"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_LIGHT }) {
CLAY(CLAY_ID("RendererLeftText"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
CLAY({ .id = CLAY_ID("RendererSpacerLeft"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY(CLAY_ID("RendererSpacerLeft"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY_TEXT(CLAY_STRING("Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
CLAY_TEXT(CLAY_STRING("Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
CLAY_TEXT(CLAY_STRING("There's even an HTML renderer - you're looking at it right now!"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
}
CLAY({ .id = CLAY_ID("RendererRightText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
CLAY(CLAY_ID("RendererRightText"), { .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
CLAY_TEXT(CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE }));
CLAY({ .id = CLAY_ID("RendererSpacerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) }} }) {}
CLAY(CLAY_ID("RendererSpacerRight"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) }} }) {}
if (ACTIVE_RENDERER_INDEX == 0) {
RendererButtonActive(CLAY_STRING("HTML Renderer"));
RendererButtonInactive(CLAY_STRING("Canvas Renderer"), 1);
@ -313,17 +313,17 @@ void RendererPageMobile() {
}
void DebuggerPageDesktop() {
CLAY({ .id = CLAY_ID("DebuggerDesktop"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 82, 82, 32, 32 }, .childGap = 64 }, .backgroundColor = COLOR_RED }) {
CLAY({ .id = CLAY_ID("DebuggerLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY(CLAY_ID("DebuggerDesktop"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 82, 82, 32, 32 }, .childGap = 64 }, .backgroundColor = COLOR_RED }) {
CLAY(CLAY_ID("DebuggerLeftText"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
CLAY_TEXT(CLAY_STRING("Integrated Debug Tools"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
CLAY({ .id = CLAY_ID("DebuggerSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY(CLAY_ID("DebuggerSpacer"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
CLAY_TEXT(CLAY_STRING("Clay includes built in \"Chrome Inspector\"-style debug tooling."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
CLAY_TEXT(CLAY_STRING("View your layout hierarchy and config in real time."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
CLAY({ .id = CLAY_ID("DebuggerPageSpacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
CLAY(CLAY_ID("DebuggerPageSpacer"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
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("DebuggerPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .aspectRatio = { 1620.0 / 1474.0 }, .image = {.imageData = FrameAllocateString(CLAY_STRING("/clay/images/debugger.png")) } }) {}
CLAY(CLAY_ID("DebuggerRightImageOuter"), { .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
CLAY(CLAY_ID("DebuggerPageRightImageInner"), { .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .aspectRatio = { 1620.0 / 1474.0 }, .image = {.imageData = FrameAllocateString(CLAY_STRING("/clay/images/debugger.png")) } }) {}
}
}
}
@ -340,44 +340,55 @@ float animationLerpValue = -1.0f;
Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
Clay_BeginLayout();
CLAY({ .id = CLAY_ID("OuterContainer"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {
CLAY({ .id = CLAY_ID("Header"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = { 32, 32 } } }) {
CLAY(CLAY_ID("OuterContainer"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {
CLAY(CLAY_ID("Header"), { .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = { 32, 32 } } }) {
CLAY_TEXT(CLAY_STRING("Clay"), &headerTextConfig);
CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
CLAY(CLAY_ID("Spacer"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
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_TEXT(CLAY_STRING("Examples"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
CLAY(CLAY_ID("LinkExamplesOuter"), { .layout = { .padding = {8, 8} } }) {
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_TEXT(CLAY_STRING("Docs"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
CLAY(CLAY_ID("LinkDocsOuter"), { .layout = { .padding = {8, 8} } }) {
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_AUTO_ID({
.layout = { .padding = {16, 16, 6, 6} },
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.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_AUTO_ID({
.layout = { .padding = {16, 16, 6, 6} },
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.cornerRadius = CLAY_CORNER_RADIUS(10),
.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({ .id = CLAY_ID("TopBorder1"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_5 }) {}
CLAY({ .id = CLAY_ID("TopBorder2"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_4 }) {}
CLAY({ .id = CLAY_ID("TopBorder3"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_3 }) {}
CLAY({ .id = CLAY_ID("TopBorder4"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_2 }) {}
CLAY({ .id = CLAY_ID("TopBorder5"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_1 }) {}
CLAY({ .id = CLAY_ID("OuterScrollContainer"),
CLAY(CLAY_ID("TopBorder1"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_5 }) {}
CLAY(CLAY_ID("TopBorder2"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_4 }) {}
CLAY(CLAY_ID("TopBorder3"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_3 }) {}
CLAY(CLAY_ID("TopBorder4"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_2 }) {}
CLAY(CLAY_ID("TopBorder5"), { .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_1 }) {}
CLAY(CLAY_ID("OuterScrollContainer"), {
.layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
.border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED }
@ -408,8 +419,7 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
scrollbarColor = (Clay_Color){225, 138, 50, 160};
}
float scrollHeight = scrollData.scrollContainerDimensions.height - 12;
CLAY({
.id = CLAY_ID("ScrollBar"),
CLAY(CLAY_ID("ScrollBar"), {
.floating = { .offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("OuterScrollContainer")).id, .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }, .attachTo = CLAY_ATTACH_TO_PARENT },
.layout = { .sizing = {CLAY_SIZING_FIXED(10), CLAY_SIZING_FIXED((scrollHeight / scrollData.contentDimensions.height) * scrollHeight)} },
.backgroundColor = scrollbarColor,

View file

@ -13,7 +13,7 @@ int main(void) {
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, (char *)malloc(totalMemorySize));
Clay_Initialize(clayMemory, Clay_Dimensions {1024,768}, Clay_ErrorHandler { HandleClayErrors });
Clay_BeginLayout();
CLAY({ .layout = layoutElement, .backgroundColor = {255,255,255,0} }) {
CLAY_AUTO_ID({ .layout = layoutElement, .backgroundColor = {255,255,255,0} }) {
CLAY_TEXT(CLAY_STRING(""), CLAY_TEXT_CONFIG({ .fontId = 0 }));
}
Clay_EndLayout();

View file

@ -18,7 +18,7 @@ Clay_Color COLOR_WHITE = { 255, 255, 255, 255 };
Clay_Color COLOR_BLACK = { 0, 0, 0, 255 };
void RenderHeaderButton(Clay_String text) {
CLAY({
CLAY_AUTO_ID({
.layout = { .padding = { 8, 8, 4, 4 } },
.backgroundColor = COLOR_BLACK,
.cornerRadius = CLAY_CORNER_RADIUS(4)
@ -212,8 +212,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
};
// Build UI here
CLAY({
.id = CLAY_ID("OuterContainer"),
CLAY(CLAY_ID("OuterContainer"), {
.backgroundColor = COLOR_WHITE,
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
@ -223,8 +222,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
}
}) {
// Child elements go inside braces
CLAY({
.id = CLAY_ID("HeaderBar"),
CLAY(CLAY_ID("HeaderBar"), {
.layout = {
.sizing = {
.height = CLAY_SIZING_FIXED(30),
@ -235,8 +233,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
},
}) {
// Header buttons go here
CLAY({
.id = CLAY_ID("FileButton"),
CLAY(CLAY_ID("FileButton"), {
.layout = {
.padding = { 8, 8, 4, 4 }
},
@ -252,18 +249,16 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
);
}
RenderHeaderButton(CLAY_STRING("Edit"));
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {}
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {}
RenderHeaderButton(CLAY_STRING("Upload"));
RenderHeaderButton(CLAY_STRING("Media"));
RenderHeaderButton(CLAY_STRING("Support"));
}
CLAY({
.id = CLAY_ID("LowerContent"),
CLAY(CLAY_ID("LowerContent"), {
.layout = { .sizing = layoutExpand, .childGap = 8 },
}) {
CLAY({
.id = CLAY_ID("Sidebar"),
CLAY(CLAY_ID("Sidebar"), {
.border = contentBorders,
.cornerRadius = CLAY_CORNER_RADIUS(4),
.layout = {
@ -284,7 +279,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
};
if (i == selectedDocumentIndex) {
CLAY({
CLAY_AUTO_ID({
.layout = sidebarButtonLayout,
.backgroundColor = COLOR_BLACK,
.cornerRadius = CLAY_CORNER_RADIUS(4)
@ -298,7 +293,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
);
}
} else {
CLAY({
CLAY_AUTO_ID({
.layout = sidebarButtonLayout,
.backgroundColor = (Clay_Color){ 0, 0, 0, Clay_Hovered() ? 120 : 0 },
.cornerRadius = CLAY_CORNER_RADIUS(4),
@ -316,8 +311,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
}
}
CLAY({
.id = CLAY_ID("MainContent"),
CLAY(CLAY_ID("MainContent"), {
.border = contentBorders,
.cornerRadius = CLAY_CORNER_RADIUS(4),
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
@ -329,7 +323,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
}
}) {
Document selectedDocument = documents.documents[selectedDocumentIndex];
CLAY({
CLAY_AUTO_ID({
.layout = {
.layoutDirection = CLAY_LEFT_TO_RIGHT,
.childGap = 4,
@ -340,7 +334,7 @@ Clay_RenderCommandArray ClayVideoDemoPlaydate_CreateLayout(int selectedDocumentI
selectedDocument.title,
CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY, .textColor = COLOR_BLACK })
);
CLAY({
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_FIXED(32),

File diff suppressed because one or more lines are too long

View file

@ -3,7 +3,7 @@
// NOTE: This file only exists to make sure that clay works when included in multiple translation units.
void SatisfyCompiler(void) {
CLAY({ .id = CLAY_ID("SatisfyCompiler") }) {
CLAY(CLAY_ID("SatisfyCompiler"), { }) {
CLAY_TEXT(CLAY_STRING("Test"), CLAY_TEXT_CONFIG({ .fontId = 0, .fontSize = 24 }));
}
}

View file

@ -5,7 +5,7 @@ const int FONT_ID_BODY_16 = 0;
Clay_Color COLOR_WHITE = { 255, 255, 255, 255};
void RenderHeaderButton(Clay_String text) {
CLAY({
CLAY_AUTO_ID({
.layout = { .padding = { 16, 16, 8, 8 }},
.backgroundColor = { 140, 140, 140, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(5)
@ -19,7 +19,7 @@ void RenderHeaderButton(Clay_String text) {
}
void RenderDropdownMenuItem(Clay_String text) {
CLAY({.layout = { .padding = CLAY_PADDING_ALL(16)}}) {
CLAY_AUTO_ID({.layout = { .padding = CLAY_PADDING_ALL(16)}}) {
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.fontSize = 16,
@ -102,7 +102,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
Clay_Color contentBackgroundColor = { 90, 90, 90, 255 };
// Build UI here
CLAY({ .id = CLAY_ID("OuterContainer"),
CLAY(CLAY_ID("OuterContainer"), {
.backgroundColor = {43, 41, 51, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
@ -112,7 +112,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
}
}) {
// Child elements go inside braces
CLAY({ .id = CLAY_ID("HeaderBar"),
CLAY(CLAY_ID("HeaderBar"), {
.layout = {
.sizing = {
.height = CLAY_SIZING_FIXED(60),
@ -128,7 +128,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
.cornerRadius = CLAY_CORNER_RADIUS(8)
}) {
// Header buttons go here
CLAY({ .id = CLAY_ID("FileButton"),
CLAY(CLAY_ID("FileButton"), {
.layout = { .padding = { 16, 16, 8, 8 }},
.backgroundColor = {140, 140, 140, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(5)
@ -145,7 +145,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileMenu")));
if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap
CLAY({ .id = CLAY_ID("FileMenu"),
CLAY(CLAY_ID("FileMenu"), {
.floating = {
.attachTo = CLAY_ATTACH_TO_PARENT,
.attachPoints = {
@ -156,7 +156,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
.padding = {0, 0, 8, 8 }
}
}) {
CLAY({
CLAY_AUTO_ID({
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = {
@ -175,18 +175,16 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
}
}
RenderHeaderButton(CLAY_STRING("Edit"));
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0) }}}) {}
CLAY_AUTO_ID({ .layout = { .sizing = { CLAY_SIZING_GROW(0) }}}) {}
RenderHeaderButton(CLAY_STRING("Upload"));
RenderHeaderButton(CLAY_STRING("Media"));
RenderHeaderButton(CLAY_STRING("Support"));
}
CLAY({
.id = CLAY_ID("LowerContent"),
CLAY(CLAY_ID("LowerContent"), {
.layout = { .sizing = layoutExpand, .childGap = 16 }
}) {
CLAY({
.id = CLAY_ID("Sidebar"),
CLAY(CLAY_ID("Sidebar"), {
.backgroundColor = contentBackgroundColor,
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
@ -206,7 +204,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
};
if (i == data->selectedDocumentIndex) {
CLAY({
CLAY_AUTO_ID({
.layout = sidebarButtonLayout,
.backgroundColor = {120, 120, 120, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(8)
@ -221,7 +219,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
SidebarClickData *clickData = (SidebarClickData *)(data->frameArena.memory + data->frameArena.offset);
*clickData = (SidebarClickData) { .requestedDocumentIndex = i, .selectedDocumentIndex = &data->selectedDocumentIndex };
data->frameArena.offset += sizeof(SidebarClickData);
CLAY({ .layout = sidebarButtonLayout, .backgroundColor = (Clay_Color) { 120, 120, 120, Clay_Hovered() ? 120 : 0 }, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
CLAY_AUTO_ID({ .layout = sidebarButtonLayout, .backgroundColor = (Clay_Color) { 120, 120, 120, Clay_Hovered() ? 120 : 0 }, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
Clay_OnHover(HandleSidebarInteraction, (intptr_t)clickData);
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
@ -233,7 +231,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
}
}
CLAY({ .id = CLAY_ID("MainContent"),
CLAY(CLAY_ID("MainContent"), {
.backgroundColor = contentBackgroundColor,
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
.layout = {

View file

@ -33,7 +33,7 @@ Clay_RenderCommandArray CornerRadiusTest(){
.width = CLAY_SIZING_GROW(0),
.height = CLAY_SIZING_GROW(0)
};
CLAY({ .id = CLAY_ID("OuterContainer"),
CLAY(CLAY_ID("OuterContainer"), {
.backgroundColor = {43, 41, 51, 255},
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
@ -43,7 +43,7 @@ Clay_RenderCommandArray CornerRadiusTest(){
}
}) {
for(int i = 0; i < 6; ++i){
CLAY({ .id = CLAY_IDI("Row", i),
CLAY(CLAY_IDI("Row", i), {
.layout = {
.layoutDirection = CLAY_LEFT_TO_RIGHT,
.sizing = layoutExpand,
@ -52,7 +52,7 @@ Clay_RenderCommandArray CornerRadiusTest(){
}
}) {
for(int j = 0; j < 6; ++j){
CLAY({ .id = CLAY_IDI("Tile", i*6+j),
CLAY(CLAY_IDI("Tile", i*6+j), {
.backgroundColor = {120, 140, 255, 128},
.cornerRadius = {(i%3)*15, (j%3)*15, (i/2)*15, (j/2)*15},
.border = {

View file

@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.25)
project(clay_examples_termbox2_demo C)
set(CMAKE_C_STANDARD 11)
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare(
termbox2
GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
GIT_TAG "ffd159c2a6106dd5eef338a6702ad15d4d4aa809"
GIT_PROGRESS TRUE
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(termbox2)
FetchContent_Declare(
stb
GIT_REPOSITORY "https://github.com/nothings/stb.git"
GIT_TAG "fede005abaf93d9d7f3a679d1999b2db341b360f"
GIT_PROGRESS TRUE
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(stb)
add_executable(clay_examples_termbox2_demo main.c)
target_compile_options(clay_examples_termbox2_demo PUBLIC)
target_include_directories(clay_examples_termbox2_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})
target_link_libraries(clay_examples_termbox2_demo PRIVATE m) # Used by stb_image.h
add_custom_command(
TARGET clay_examples_termbox2_demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/resources
${CMAKE_CURRENT_BINARY_DIR}/resources)

View file

@ -0,0 +1,794 @@
/*
Unlicense
Copyright (c) 2025 Mivirl
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>
*/
#define CLAY_IMPLEMENTATION
#include "../../clay.h"
#include "../../renderers/termbox2/clay_renderer_termbox2.c"
#define TB_IMPL
#include "termbox2.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize2.h"
// -------------------------------------------------------------------------------------------------
// -- Internal state
// If the program should exit the main render/interaction loop
bool end_loop = false;
// If the debug tools should be displayed
bool debugMode = false;
// -------------------------------------------------------------------------------------------------
// -- Clay components
void component_text_pair(const char *key, const char *value)
{
size_t keylen = strlen(key);
size_t vallen = strlen(value);
Clay_String keytext = (Clay_String) {
.length = keylen,
.chars = key,
};
Clay_String valtext = (Clay_String) {
.length = vallen,
.chars = value,
};
Clay_TextElementConfig *textconfig =
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = {
.size.minMax = {
.min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
}
},
}
},
}) {
CLAY_TEXT(keytext, textconfig);
CLAY_AUTO_ID({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
CLAY_TEXT(valtext, textconfig);
}
}
void component_termbox_settings(void)
{
CLAY(CLAY_ID("Termbox Settings"), {
.floating = {
.attachTo = CLAY_ATTACH_TO_PARENT,
.zIndex = 1,
.attachPoints = { CLAY_ATTACH_POINT_CENTER_CENTER, CLAY_ATTACH_POINT_CENTER_TOP },
.offset = { 0, 0 }
},
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = CLAY_SIZING_FIT(),
.padding = {
6 * Clay_Termbox_Cell_Width(),
6 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Height(),
2 * Clay_Termbox_Cell_Height(),
}
},
.border = {
.width = CLAY_BORDER_ALL(1),
.color = { 0x00, 0x00, 0x00, 0xff }
},
.backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }
}) {
const char *color_mode = NULL;
switch (clay_tb_color_mode) {
case TB_OUTPUT_NORMAL: {
color_mode = "TB_OUTPUT_NORMAL";
break;
}
case TB_OUTPUT_256: {
color_mode = "TB_OUTPUT_256";
break;
}
case TB_OUTPUT_216: {
color_mode = "TB_OUTPUT_216";
break;
}
case TB_OUTPUT_GRAYSCALE: {
color_mode = "TB_OUTPUT_GRAYSCALE";
break;
}
case TB_OUTPUT_TRUECOLOR: {
color_mode = "TB_OUTPUT_TRUECOLOR";
break;
}
case CLAY_TB_OUTPUT_NOCOLOR: {
color_mode = "CLAY_TB_OUTPUT_NOCOLOR";
break;
}
default: {
color_mode = "INVALID";
break;
}
}
const char *border_mode = NULL;
switch (clay_tb_border_mode) {
case CLAY_TB_BORDER_MODE_ROUND: {
border_mode = "CLAY_TB_BORDER_MODE_ROUND";
break;
}
case CLAY_TB_BORDER_MODE_MINIMUM: {
border_mode = "CLAY_TB_BORDER_MODE_MINIMUM";
break;
}
default: {
border_mode = "INVALID";
break;
}
}
const char *border_chars = NULL;
switch (clay_tb_border_chars) {
case CLAY_TB_BORDER_CHARS_ASCII: {
border_chars = "CLAY_TB_BORDER_CHARS_ASCII";
break;
}
case CLAY_TB_BORDER_CHARS_UNICODE: {
border_chars = "CLAY_TB_BORDER_CHARS_UNICODE";
break;
}
case CLAY_TB_BORDER_CHARS_BLANK: {
border_chars = "CLAY_TB_BORDER_CHARS_BLANK";
break;
}
case CLAY_TB_BORDER_CHARS_NONE: {
border_chars = "CLAY_TB_BORDER_CHARS_NONE";
break;
}
default: {
border_chars = "INVALID";
break;
}
}
const char *image_mode = NULL;
switch (clay_tb_image_mode) {
case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {
image_mode = "CLAY_TB_IMAGE_MODE_PLACEHOLDER";
break;
}
case CLAY_TB_IMAGE_MODE_BG: {
image_mode = "CLAY_TB_IMAGE_MODE_BG";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII_FG: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG_FAST";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII_FAST: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FAST";
break;
}
case CLAY_TB_IMAGE_MODE_UNICODE: {
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE";
break;
}
case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE_FAST";
break;
}
default: {
image_mode = "INVALID";
break;
}
}
const char *transparency = NULL;
if (clay_tb_transparency) {
transparency = "true";
} else {
transparency = "false";
}
CLAY_AUTO_ID({
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
}) {
component_text_pair("Color mode", color_mode);
component_text_pair("Border mode", border_mode);
component_text_pair("Border chars", border_chars);
component_text_pair("Image mode", image_mode);
component_text_pair("Transparency", transparency);
}
}
}
}
void component_color_palette(void)
{
CLAY_AUTO_ID({
.layout = {
.childGap = 16,
.padding = {
2 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Height(),
2 * Clay_Termbox_Cell_Height(),
}
},
.border = {
.width = CLAY_BORDER_OUTSIDE(2),
.color = { 0x00, 0x00, 0x00, 0xff }
},
.backgroundColor = { 0x7f, 0x7f, 0x7f, 0xff }
}) {
for (int type = 0; type < 2; ++type) {
CLAY_AUTO_ID({
.layout ={
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = CLAY_SIZING_FIT(),
.childGap = Clay_Termbox_Cell_Height()
},
}) {
for (float ri = 0; ri < 4; ri += 1) {
CLAY_AUTO_ID({
.layout ={
.sizing = CLAY_SIZING_FIT(),
.childGap = Clay_Termbox_Cell_Width()
},
}) {
for (float r = ri * 0x44; r < (ri + 1) * 0x44; r += 0x22) {
CLAY_AUTO_ID({
.layout ={
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = CLAY_SIZING_FIT(),
},
}) {
for (float g = 0; g < 0xff; g += 0x22) {
CLAY_AUTO_ID({
.layout ={
.sizing = CLAY_SIZING_FIT(),
},
}) {
for (float b = 0; b < 0xff; b += 0x22) {
Clay_Color color = { r, g, b, 0x7f };
Clay_LayoutConfig layout = (Clay_LayoutConfig) {
.sizing = {
.width = CLAY_SIZING_FIXED(2 * Clay_Termbox_Cell_Width()),
.height = CLAY_SIZING_FIXED(1 * Clay_Termbox_Cell_Height())
}
};
if (0 == type) {
CLAY_AUTO_ID({
.layout = layout,
.backgroundColor = color
}) {}
} else if (1 == type) {
CLAY_AUTO_ID({
.layout = layout,
}) {
CLAY_TEXT(CLAY_STRING("#"), CLAY_TEXT_CONFIG({ .textColor = color }));
}
}
}
}
}
}
}
}
}
}
}
}
}
void component_unicode_text(void)
{
CLAY_AUTO_ID({
.layout = {
.sizing = CLAY_SIZING_FIT(),
.padding = {
2 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Height(),
2 * Clay_Termbox_Cell_Height(),
}
},
.backgroundColor = { 0xcc, 0xbb, 0xaa, 0xff },
.border = {
// This border should still be displayed in CLAY_TB_BORDER_MODE_ROUND mode
.width = {
0.75 * Clay_Termbox_Cell_Width(),
0.75 * Clay_Termbox_Cell_Width(),
0.75 * Clay_Termbox_Cell_Height(),
0.75 * Clay_Termbox_Cell_Height(),
},
.color = { 0x33, 0x33, 0x33, 0xff },
},
}) {
CLAY_TEXT(
CLAY_STRING("Non-ascii character tests:\n"
"\n"
"(from https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html)\n"
" Mathematics and Sciences:\n"
" ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ∀x∈: ⌈x⌉ = x⌋, α ∧ ¬β = ¬(¬α β),\n"
" ⊆ ℕ₀ ⊂ , ⊥ < a ≠ b ≡ c ≤ d ≪ ⇒ (A ⇔ B),\n"
" 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm\n"
"\n"
" Compact font selection example text:\n"
" ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789\n"
" abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ\n"
" –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд\n"
" ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi<>⑀₂ἠḂӥẄɐː⍎אԱა\n"
"\n"
"(from https://blog.denisbider.com/2015/09/when-monospace-fonts-arent-unicode.html):\n"
" aeioucsz\n"
" áéíóúčšž\n"
" 台北1234\n"
" 12\n"
" アイウ1234\n"
"\n"
"(from https://stackoverflow.com/a/1644280)\n"
" ٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ ٩(-̮̮̃•̃)."
),
CLAY_TEXT_CONFIG({ .textColor = { 0x11, 0x11, 0x11, 0xff } })
);
}
}
void component_keybinds(void)
{
CLAY_AUTO_ID({
.layout = {
.sizing = CLAY_SIZING_FIT(),
.padding = {
4 * Clay_Termbox_Cell_Width(),
4 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Height(),
2 * Clay_Termbox_Cell_Height(),
}
},
.backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }
}) {
CLAY_TEXT(
CLAY_STRING(
"Termbox2 renderer test\n"
"\n"
"Keybinds:\n"
" c/C - Cycle through color modes\n"
" b/B - Cycle through border modes\n"
" h/H - Cycle through border characters\n"
" i/I - Cycle through image modes\n"
" t/T - Toggle transparency\n"
" d/D - Toggle debug mode\n"
" q/Q - Quit\n"
),
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})
);
}
}
void component_image(clay_tb_image *image, int width)
{
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = (0 == width) ? CLAY_SIZING_GROW() : CLAY_SIZING_FIXED(width),
},
},
.image = {
.imageData = image,
},
.aspectRatio = { 512.0 / 406.0 }
}) { }
}
void component_mouse_data(void)
{
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
},
},
}) {
Clay_Context* context = Clay_GetCurrentContext();
Clay_Vector2 mouse_position = context->pointerInfo.position;
Clay_LayoutConfig layout = (Clay_LayoutConfig) {
.sizing = {
.width = CLAY_SIZING_FIXED(2 * Clay_Termbox_Cell_Width()),
.height = CLAY_SIZING_FIXED(1 * Clay_Termbox_Cell_Height())
}
};
float v = 255 * mouse_position.x / Clay_Termbox_Width();
v = (0 > v) ? 0 : v;
v = (255 < v) ? 255 : v;
Clay_Color color = (Clay_Color) { v, v, v, 0xff };
CLAY_AUTO_ID({
.layout = layout,
.backgroundColor = color
}) {}
v = 255 * mouse_position.y / Clay_Termbox_Height();
v = (0 > v) ? 0 : v;
v = (255 < v) ? 255 : v;
color = (Clay_Color) { v, v, v, 0xff };
CLAY_AUTO_ID({
.layout = layout,
.backgroundColor = color
}) {}
}
}
void component_bordered_text(void)
{
CLAY_AUTO_ID({
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = {
.width = CLAY_SIZING_FIT(450),
.height = CLAY_SIZING_FIT(),
},
.padding = CLAY_PADDING_ALL(32)
},
.backgroundColor = { 0x24, 0x55, 0x34, 0xff },
}) {
CLAY_AUTO_ID({
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0xaa, 0x00, 0x00, 0xff } },
}) {
CLAY_TEXT(
CLAY_STRING("Test"), CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
}
CLAY_AUTO_ID({
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0xaa, 0x00, 0xff } },
}) {
CLAY_TEXT(CLAY_STRING("of the border width"),
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
}
CLAY_AUTO_ID({
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0x00, 0xaa, 0xff } },
}) {
CLAY_TEXT(CLAY_STRING("and overlap for multiple lines\nof text"),
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
}
CLAY_AUTO_ID({
.border = { .width = { 1, 1, 1, 1, 1 }, .color = { 0x00, 0x00, 0xaa, 0xff } },
}) {
CLAY_TEXT(CLAY_STRING("this text\nis long enough\nto display all\n borders\naround it"),
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } }));
}
}
}
Clay_RenderCommandArray CreateLayout(clay_tb_image *image1, clay_tb_image *image2)
{
Clay_BeginLayout();
CLAY_AUTO_ID({
.clip = {
.vertical = false,
.horizontal = true,
.childOffset = Clay_GetScrollOffset(),
},
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_GROW()
},
.childAlignment = {
.x = CLAY_ALIGN_X_LEFT,
.y = CLAY_ALIGN_Y_CENTER
},
.childGap = 64
},
.backgroundColor = { 0x24, 0x24, 0x24, 0xff }
}) {
CLAY_AUTO_ID({
.layout = {
.childAlignment = {
.x = CLAY_ALIGN_X_RIGHT,
},
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = CLAY_SIZING_FIT(),
},
}) {
component_keybinds();
component_unicode_text();
}
CLAY_AUTO_ID({
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childGap = 32,
.sizing = CLAY_SIZING_FIT(),
},
}) {
component_termbox_settings();
component_image(image1, 150);
component_image(image2, 0);
component_mouse_data();
component_bordered_text();
}
component_color_palette();
}
return Clay_EndLayout();
}
// -------------------------------------------------------------------------------------------------
// -- Interactive functions
void handle_clay_errors(Clay_ErrorData errorData)
{
Clay_Termbox_Close();
fprintf(stderr, "%s", errorData.errorText.chars);
exit(1);
}
/**
Process events received from termbox2 and handle interaction
*/
void handle_termbox_events(void)
{
// Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing
// If an event is already available, this doesn't wait. Will not wait due to the previous call
// to termbox_waitfor_event. Increasing the wait time reduces load without reducing
// responsiveness (but will of course prevent other code from running on this thread while it's
// waiting)
struct tb_event evt;
int ms_to_wait = 0;
int err = tb_peek_event(&evt, ms_to_wait);
switch (err) {
default:
case TB_ERR_NO_EVENT: {
break;
}
case TB_ERR_POLL: {
if (EINTR != tb_last_errno()) {
Clay_Termbox_Close();
fprintf(stderr, "Failed to read event from TTY\n");
exit(1);
}
break;
}
case TB_OK: {
switch (evt.type) {
case TB_EVENT_RESIZE: {
Clay_SetLayoutDimensions((Clay_Dimensions) {
Clay_Termbox_Width(),
Clay_Termbox_Height()
});
break;
}
case TB_EVENT_KEY: {
if (TB_KEY_CTRL_C == evt.key) {
end_loop = true;
break;
}
switch (evt.ch) {
case 'q':
case 'Q': {
end_loop = true;
break;
}
case 'd':
case 'D': {
debugMode = !debugMode;
Clay_SetDebugModeEnabled(debugMode);
break;
}
case 'c': {
int new_mode = clay_tb_color_mode - 1;
new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;
Clay_Termbox_Set_Color_Mode(new_mode);
break;
}
case 'C': {
int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);
Clay_Termbox_Set_Color_Mode(new_mode);
break;
}
case 'b': {
enum border_mode new_mode = clay_tb_border_mode - 1;
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_BORDER_MODE_MINIMUM;
Clay_Termbox_Set_Border_Mode(new_mode);
break;
}
case 'B': {
enum border_mode new_mode = (clay_tb_border_mode + 1)
% (CLAY_TB_BORDER_MODE_MINIMUM + 1);
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_BORDER_MODE_ROUND;
Clay_Termbox_Set_Border_Mode(new_mode);
break;
}
case 'h': {
enum border_chars new_chars = clay_tb_border_chars - 1;
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
? new_chars
: CLAY_TB_BORDER_CHARS_NONE;
Clay_Termbox_Set_Border_Chars(new_chars);
break;
}
case 'H': {
enum border_chars new_chars
= (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
? new_chars
: CLAY_TB_BORDER_CHARS_ASCII;
Clay_Termbox_Set_Border_Chars(new_chars);
break;
}
case 'i': {
enum image_mode new_mode = clay_tb_image_mode - 1;
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_IMAGE_MODE_UNICODE_FAST;
Clay_Termbox_Set_Image_Mode(new_mode);
break;
}
case 'I': {
enum image_mode new_mode = (clay_tb_image_mode + 1)
% (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_IMAGE_MODE_PLACEHOLDER;
Clay_Termbox_Set_Image_Mode(new_mode);
break;
}
case 't':
case 'T': {
Clay_Termbox_Set_Transparency(!clay_tb_transparency);
}
}
break;
}
case TB_EVENT_MOUSE: {
Clay_Vector2 mousePosition = {
(float)evt.x * Clay_Termbox_Cell_Width(),
(float)evt.y * Clay_Termbox_Cell_Height()
};
// Mouse release events may not be produced by all terminals, and will
// be sent during hover, so can't be used to detect when the mouse has
// been released
switch (evt.key) {
case TB_KEY_MOUSE_LEFT: {
Clay_SetPointerState(mousePosition, true);
break;
}
case TB_KEY_MOUSE_RIGHT: {
Clay_SetPointerState(mousePosition, false);
break;
}
case TB_KEY_MOUSE_MIDDLE: {
Clay_SetPointerState(mousePosition, false);
break;
}
case TB_KEY_MOUSE_RELEASE: {
Clay_SetPointerState(mousePosition, false);
break;
}
case TB_KEY_MOUSE_WHEEL_UP: {
Clay_Vector2 scrollDelta = { 0.5 * Clay_Termbox_Cell_Width(), 0 };
Clay_UpdateScrollContainers(false, scrollDelta, 1);
break;
}
case TB_KEY_MOUSE_WHEEL_DOWN: {
Clay_Vector2 scrollDelta = { -0.5 * Clay_Termbox_Cell_Width(), 0 };
Clay_UpdateScrollContainers(false, scrollDelta, 1);
break;
}
default: {
break;
}
}
break;
}
default: {
break;
}
}
break;
}
}
}
int main(void)
{
clay_tb_image shark_image1 = Clay_Termbox_Image_Load_File("resources/512px-Shark_antwerp_zoo.jpeg");
clay_tb_image shark_image2 = Clay_Termbox_Image_Load_File("resources/512px-Shark_antwerp_zoo.jpeg");
if (NULL == shark_image1.pixel_data) { exit(1); }
if (NULL == shark_image2.pixel_data) { exit(1); }
int num_elements = 3 * 8192;
Clay_SetMaxElementCount(num_elements);
uint64_t size = Clay_MinMemorySize();
void *memory = malloc(size);
if (NULL == memory) { exit(1); }
Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);
Clay_Termbox_Initialize(
TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);
Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },
(Clay_ErrorHandler) { handle_clay_errors, NULL });
Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);
// Initial render before waiting for events
Clay_RenderCommandArray commands = CreateLayout(&shark_image1, &shark_image2);
Clay_Termbox_Render(commands);
tb_present();
while (!end_loop) {
// Block until event is available. Optional, but reduces load since this demo is purely
// synchronous to user input.
Clay_Termbox_Waitfor_Event();
handle_termbox_events();
commands = CreateLayout(&shark_image1, &shark_image2);
tb_clear();
Clay_Termbox_Render(commands);
tb_present();
}
Clay_Termbox_Close();
Clay_Termbox_Image_Free(&shark_image1);
Clay_Termbox_Image_Free(&shark_image2);
free(memory);
return 0;
}

View file

@ -0,0 +1,72 @@
# Termbox2 renderer demo
Terminal-based renderer using [termbox2](https://github.com/termbox/termbox2)
This demo shows a color palette and a few different components. It allows
changing configuration settings for colors, border size rounding mode,
characters used for borders, and transparency.
```
Keybinds:
c/C - Cycle through color modes
b/B - Cycle through border modes
h/H - Cycle through border characters
i/I - Cycle through image modes
t/T - Toggle transparency
d/D - Toggle debug mode
q/Q - Quit
```
Configuration can be also be overriden by environment variables:
- `CLAY_TB_COLOR_MODE`
- `NORMAL`
- `256`
- `216`
- `GRAYSCALE`
- `TRUECOLOR`
- `NOCOLOR`
- `CLAY_TB_BORDER_CHARS`
- `DEFAULT`
- `ASCII`
- `UNICODE`
- `NONE`
- `CLAY_TB_IMAGE_MODE`
- `DEFAULT`
- `PLACEHOLDER`
- `BG`
- `ASCII_FG`
- `ASCII`
- `UNICODE`
- `ASCII_FG_FAST`
- `ASCII_FAST`
- `UNICODE_FAST`
- `CLAY_TB_TRANSPARENCY`
- `1`
- `0`
- `CLAY_TB_CELL_PIXELS`
- `widthxheight`
## Building
Build the binary with cmake
```sh
mkdir build
cd build
cmake ..
make
```
Then run the executable:
```sh
./clay_examples_termbox2_demo
```
## Attributions
Resources used:
- `512px-Shark_antwerp_zoo.jpeg`
- Retrieved from <https://commons.wikimedia.org/wiki/File:Shark_antwerp_zoo.jpg>
- License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/deed.en)
- No changes made

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View file

@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.25)
project(clay_examples_termbox2_image_demo C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_C_FLAGS_RELEASE "-O3")
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare(
termbox2
GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
GIT_TAG "ffd159c2a6106dd5eef338a6702ad15d4d4aa809"
GIT_PROGRESS TRUE
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(termbox2)
FetchContent_Declare(
stb
GIT_REPOSITORY "https://github.com/nothings/stb.git"
GIT_TAG "fede005abaf93d9d7f3a679d1999b2db341b360f"
GIT_PROGRESS TRUE
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(stb)
add_executable(clay_examples_termbox2_image_demo main.c)
target_compile_options(clay_examples_termbox2_image_demo PUBLIC)
target_include_directories(clay_examples_termbox2_image_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})
target_link_libraries(clay_examples_termbox2_image_demo PRIVATE m) # Used by stb_image.h
add_custom_command(
TARGET clay_examples_termbox2_image_demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/resources
${CMAKE_CURRENT_BINARY_DIR}/resources)

View file

@ -0,0 +1,707 @@
/*
Unlicense
Copyright (c) 2025 Mivirl
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>
*/
#define CLAY_IMPLEMENTATION
#include "../../clay.h"
#include "../../renderers/termbox2/clay_renderer_termbox2.c"
#define TB_IMPL
#include "termbox2.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize2.h"
// -------------------------------------------------------------------------------------------------
// -- Data structures
struct img_group {
clay_tb_image thumbnail;
clay_tb_image image;
clay_tb_image image_1;
clay_tb_image image_2;
int width;
int height;
};
typedef struct img_group img_group;
// -------------------------------------------------------------------------------------------------
// -- Internal state
// If the program should exit the main render/interaction loop
bool end_loop = false;
// If the debug tools should be displayed
bool debugMode = false;
// -------------------------------------------------------------------------------------------------
// -- Internal utility functions
img_group img_group_load(const char *filename)
{
img_group rv;
rv.thumbnail = Clay_Termbox_Image_Load_File(filename);
rv.image = Clay_Termbox_Image_Load_File(filename);
rv.image_1 = Clay_Termbox_Image_Load_File(filename);
rv.image_2 = Clay_Termbox_Image_Load_File(filename);
if (NULL == rv.thumbnail.pixel_data
|| NULL == rv.image.pixel_data
|| NULL == rv.image_1.pixel_data
|| NULL == rv.image_2.pixel_data) {
exit(1);
}
rv.width = rv.image.pixel_width;
rv.height = rv.image.pixel_height;
return rv;
}
void img_group_free(img_group *img)
{
Clay_Termbox_Image_Free(&img->thumbnail);
Clay_Termbox_Image_Free(&img->image);
Clay_Termbox_Image_Free(&img->image_1);
Clay_Termbox_Image_Free(&img->image_2);
}
// -------------------------------------------------------------------------------------------------
// -- Clay components
void component_text_pair(const char *key, const char *value)
{
size_t keylen = strlen(key);
size_t vallen = strlen(value);
Clay_String keytext = (Clay_String) {
.length = keylen,
.chars = key,
};
Clay_String valtext = (Clay_String) {
.length = vallen,
.chars = value,
};
Clay_TextElementConfig *textconfig =
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = {
.size.minMax = {
.min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
}
},
}
},
}) {
CLAY_TEXT(keytext, textconfig);
CLAY_AUTO_ID({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
CLAY_TEXT(valtext, textconfig);
}
}
void component_termbox_settings(void)
{
CLAY_AUTO_ID({
.layout = {
.sizing = CLAY_SIZING_FIT(),
.padding = {
6 * Clay_Termbox_Cell_Width(),
6 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Height(),
2 * Clay_Termbox_Cell_Height(),
}
},
.border = {
.width = CLAY_BORDER_ALL(1),
.color = { 0x00, 0x00, 0x00, 0xff }
},
.backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }
}) {
const char *color_mode = NULL;
switch (clay_tb_color_mode) {
case TB_OUTPUT_NORMAL: {
color_mode = "TB_OUTPUT_NORMAL";
break;
}
case TB_OUTPUT_256: {
color_mode = "TB_OUTPUT_256";
break;
}
case TB_OUTPUT_216: {
color_mode = "TB_OUTPUT_216";
break;
}
case TB_OUTPUT_GRAYSCALE: {
color_mode = "TB_OUTPUT_GRAYSCALE";
break;
}
case TB_OUTPUT_TRUECOLOR: {
color_mode = "TB_OUTPUT_TRUECOLOR";
break;
}
case CLAY_TB_OUTPUT_NOCOLOR: {
color_mode = "CLAY_TB_OUTPUT_NOCOLOR";
break;
}
default: {
color_mode = "INVALID";
break;
}
}
const char *border_mode = NULL;
switch (clay_tb_border_mode) {
case CLAY_TB_BORDER_MODE_ROUND: {
border_mode = "CLAY_TB_BORDER_MODE_ROUND";
break;
}
case CLAY_TB_BORDER_MODE_MINIMUM: {
border_mode = "CLAY_TB_BORDER_MODE_MINIMUM";
break;
}
default: {
border_mode = "INVALID";
break;
}
}
const char *border_chars = NULL;
switch (clay_tb_border_chars) {
case CLAY_TB_BORDER_CHARS_ASCII: {
border_chars = "CLAY_TB_BORDER_CHARS_ASCII";
break;
}
case CLAY_TB_BORDER_CHARS_UNICODE: {
border_chars = "CLAY_TB_BORDER_CHARS_UNICODE";
break;
}
case CLAY_TB_BORDER_CHARS_BLANK: {
border_chars = "CLAY_TB_BORDER_CHARS_BLANK";
break;
}
case CLAY_TB_BORDER_CHARS_NONE: {
border_chars = "CLAY_TB_BORDER_CHARS_NONE";
break;
}
default: {
border_chars = "INVALID";
break;
}
}
const char *image_mode = NULL;
switch (clay_tb_image_mode) {
case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {
image_mode = "CLAY_TB_IMAGE_MODE_PLACEHOLDER";
break;
}
case CLAY_TB_IMAGE_MODE_BG: {
image_mode = "CLAY_TB_IMAGE_MODE_BG";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII_FG: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG_FAST";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII";
break;
}
case CLAY_TB_IMAGE_MODE_ASCII_FAST: {
image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FAST";
break;
}
case CLAY_TB_IMAGE_MODE_UNICODE: {
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE";
break;
}
case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {
image_mode = "CLAY_TB_IMAGE_MODE_UNICODE_FAST";
break;
}
default: {
image_mode = "INVALID";
break;
}
}
const char *transparency = NULL;
if (clay_tb_transparency) {
transparency = "true";
} else {
transparency = "false";
}
CLAY_AUTO_ID({
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
}) {
component_text_pair("Color mode", color_mode);
component_text_pair("Border mode", border_mode);
component_text_pair("Border chars", border_chars);
component_text_pair("Image mode", image_mode);
component_text_pair("Transparency", transparency);
}
}
}
void component_keybinds(void)
{
CLAY_AUTO_ID({
.layout = {
.sizing = CLAY_SIZING_FIT(),
.padding = {
4 * Clay_Termbox_Cell_Width(),
4 * Clay_Termbox_Cell_Width(),
2 * Clay_Termbox_Cell_Height(),
2 * Clay_Termbox_Cell_Height(),
}
},
.backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }
}) {
CLAY_TEXT(
CLAY_STRING(
"Termbox2 renderer test\n"
"\n"
"Keybinds:\n"
" up/down arrows - Change selected image\n"
" c/C - Cycle through color modes\n"
" b/B - Cycle through border modes\n"
" h/H - Cycle through border characters\n"
" i/I - Cycle through image modes\n"
" t/T - Toggle transparency\n"
" d/D - Toggle debug mode\n"
" q/Q - Quit\n"
),
CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})
);
}
}
void component_image(img_group *img_pair)
{
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_GROW()
},
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childAlignment = {
.x = CLAY_ALIGN_X_CENTER,
.y = CLAY_ALIGN_Y_CENTER
},
.childGap = 1 * Clay_Termbox_Cell_Height()
},
.backgroundColor = { 0x24, 0x24, 0x24, 0xff }
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
},
},
.image = {
.imageData = &img_pair->image,
},
.aspectRatio = { (float)img_pair->width / img_pair->height }
}) { }
component_keybinds();
}
}
void component_image_small(img_group **img_pairs, int count, int selected_index)
{
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_PERCENT(0.25),
},
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childGap = 20,
.childAlignment = {
.x = CLAY_ALIGN_X_CENTER,
.y = CLAY_ALIGN_Y_CENTER
},
},
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_PERCENT(0.7),
},
},
.image = {
.imageData = &img_pairs[selected_index]->image_1,
},
.aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }
}) { }
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
},
},
.image = {
.imageData = &img_pairs[selected_index]->image_2,
},
.aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }
}) { }
component_termbox_settings();
}
}
void component_thumbnails(img_group **img_pairs, int count, int selected_index)
{
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_PERCENT(0.1),
.height = CLAY_SIZING_GROW()
},
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childGap = 20
},
.backgroundColor = { 0x42, 0x42, 0x42, 0xff }
}) {
for (int i = 0; i < count; ++i) {
Clay_BorderElementConfig border;
if (i == selected_index) {
border = (Clay_BorderElementConfig) {
.width =CLAY_BORDER_OUTSIDE(10),
.color = { 0x00, 0x30, 0xc0, 0x8f }
};
} else {
border = (Clay_BorderElementConfig) {
.width = CLAY_BORDER_OUTSIDE(0),
};
}
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
},
},
.border = border,
.image = {
.imageData = &img_pairs[i]->thumbnail,
},
.aspectRatio = { (float)img_pairs[i]->width / img_pairs[i]->height }
}) { }
}
}
}
int selected_thumbnail = 0;
const int thumbnail_count = 5;
Clay_RenderCommandArray CreateLayout(struct img_group **imgs)
{
Clay_BeginLayout();
CLAY_AUTO_ID({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_GROW()
},
.childAlignment = {
.x = CLAY_ALIGN_X_LEFT,
.y = CLAY_ALIGN_Y_CENTER
},
.childGap = 64
},
.backgroundColor = { 0x24, 0x24, 0x24, 0xff }
}) {
component_thumbnails(imgs, thumbnail_count, selected_thumbnail);
component_image_small(imgs, thumbnail_count, selected_thumbnail);
component_image(imgs[selected_thumbnail]);
}
return Clay_EndLayout();
}
// -------------------------------------------------------------------------------------------------
// -- Interactive functions
void handle_clay_errors(Clay_ErrorData errorData)
{
Clay_Termbox_Close();
fprintf(stderr, "%s", errorData.errorText.chars);
exit(1);
}
/**
Process events received from termbox2 and handle interaction
*/
void handle_termbox_events(void)
{
// Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing
// If an event is already available, this doesn't wait. Will not wait due to the previous call
// to termbox_waitfor_event. Increasing the wait time reduces load without reducing
// responsiveness (but will of course prevent other code from running on this thread while it's
// waiting)
struct tb_event evt;
int ms_to_wait = 0;
int err = tb_peek_event(&evt, ms_to_wait);
switch (err) {
default:
case TB_ERR_NO_EVENT: {
break;
}
case TB_ERR_POLL: {
if (EINTR != tb_last_errno()) {
Clay_Termbox_Close();
fprintf(stderr, "Failed to read event from TTY\n");
exit(1);
}
break;
}
case TB_OK: {
switch (evt.type) {
case TB_EVENT_RESIZE: {
Clay_SetLayoutDimensions((Clay_Dimensions) {
Clay_Termbox_Width(),
Clay_Termbox_Height()
});
break;
}
case TB_EVENT_KEY: {
if (TB_KEY_CTRL_C == evt.key) {
end_loop = true;
break;
}
if (0 != evt.ch) {
switch (evt.ch) {
case 'q':
case 'Q': {
end_loop = true;
break;
}
case 'd':
case 'D': {
debugMode = !debugMode;
Clay_SetDebugModeEnabled(debugMode);
break;
}
case 'c': {
int new_mode = clay_tb_color_mode - 1;
new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;
Clay_Termbox_Set_Color_Mode(new_mode);
break;
}
case 'C': {
int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);
Clay_Termbox_Set_Color_Mode(new_mode);
break;
}
case 'b': {
enum border_mode new_mode = clay_tb_border_mode - 1;
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_BORDER_MODE_MINIMUM;
Clay_Termbox_Set_Border_Mode(new_mode);
break;
}
case 'B': {
enum border_mode new_mode = (clay_tb_border_mode + 1)
% (CLAY_TB_BORDER_MODE_MINIMUM + 1);
new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_BORDER_MODE_ROUND;
Clay_Termbox_Set_Border_Mode(new_mode);
break;
}
case 'h': {
enum border_chars new_chars = clay_tb_border_chars - 1;
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
? new_chars
: CLAY_TB_BORDER_CHARS_NONE;
Clay_Termbox_Set_Border_Chars(new_chars);
break;
}
case 'H': {
enum border_chars new_chars
= (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);
new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
? new_chars
: CLAY_TB_BORDER_CHARS_ASCII;
Clay_Termbox_Set_Border_Chars(new_chars);
break;
}
case 'i': {
enum image_mode new_mode = clay_tb_image_mode - 1;
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_IMAGE_MODE_UNICODE_FAST;
Clay_Termbox_Set_Image_Mode(new_mode);
break;
}
case 'I': {
enum image_mode new_mode = (clay_tb_image_mode + 1)
% (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);
new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
? new_mode
: CLAY_TB_IMAGE_MODE_PLACEHOLDER;
Clay_Termbox_Set_Image_Mode(new_mode);
break;
}
case 't':
case 'T': {
Clay_Termbox_Set_Transparency(!clay_tb_transparency);
}
}
} else if (0 != evt.key) {
switch (evt.key) {
case TB_KEY_ARROW_UP: {
selected_thumbnail = (selected_thumbnail > 0) ? selected_thumbnail - 1 : 0;
break;
}
case TB_KEY_ARROW_DOWN: {
selected_thumbnail = (selected_thumbnail < thumbnail_count - 1) ? selected_thumbnail + 1 : thumbnail_count - 1;
break;
}
}
}
break;
}
case TB_EVENT_MOUSE: {
Clay_Vector2 mousePosition = {
(float)evt.x * Clay_Termbox_Cell_Width(),
(float)evt.y * Clay_Termbox_Cell_Height()
};
// Mouse release events may not be produced by all terminals, and will
// be sent during hover, so can't be used to detect when the mouse has
// been released
switch (evt.key) {
case TB_KEY_MOUSE_LEFT: {
Clay_SetPointerState(mousePosition, true);
break;
}
case TB_KEY_MOUSE_RIGHT: {
Clay_SetPointerState(mousePosition, false);
break;
}
case TB_KEY_MOUSE_MIDDLE: {
Clay_SetPointerState(mousePosition, false);
break;
}
case TB_KEY_MOUSE_RELEASE: {
Clay_SetPointerState(mousePosition, false);
break;
}
case TB_KEY_MOUSE_WHEEL_UP: {
Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() };
Clay_UpdateScrollContainers(false, scrollDelta, 1);
break;
}
case TB_KEY_MOUSE_WHEEL_DOWN: {
Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() };
Clay_UpdateScrollContainers(false, scrollDelta, 1);
break;
}
default: {
break;
}
}
break;
}
default: {
break;
}
}
break;
}
}
}
int main(void)
{
img_group *imgs[thumbnail_count];
img_group img_shark = img_group_load("resources/512px-Shark_antwerp_zoo.jpeg");
img_group img_castle = img_group_load("resources/512px-Balmoral_Castle_30_July_2011.jpeg");
img_group img_dog = img_group_load("resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg");
img_group img_rosa = img_group_load("resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg");
img_group img_vorderer = img_group_load("resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg");
imgs[0] = &img_shark;
imgs[1] = &img_castle;
imgs[2] = &img_dog;
imgs[3] = &img_rosa;
imgs[4] = &img_vorderer;
int num_elements = 3 * 8192;
Clay_SetMaxElementCount(num_elements);
uint64_t size = Clay_MinMemorySize();
void *memory = malloc(size);
if (NULL == memory) { exit(1); }
Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);
Clay_Termbox_Initialize(
TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);
Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },
(Clay_ErrorHandler) { handle_clay_errors, NULL });
Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);
// Initial render before waiting for events
Clay_RenderCommandArray commands = CreateLayout(imgs);
Clay_Termbox_Render(commands);
tb_present();
while (!end_loop) {
// Block until event is available. Optional, but reduces load since this demo is purely
// synchronous to user input.
Clay_Termbox_Waitfor_Event();
handle_termbox_events();
commands = CreateLayout(imgs);
Clay_Termbox_Render(commands);
tb_present();
}
Clay_Termbox_Close();
img_group_free(&img_shark);
img_group_free(&img_castle);
img_group_free(&img_dog);
img_group_free(&img_rosa);
img_group_free(&img_vorderer);
free(memory);
return 0;
}

View file

@ -0,0 +1,88 @@
# Termbox2 renderer demo
Terminal-based renderer using [termbox2](https://github.com/termbox/termbox2)
This demo shows a color palette and a few different components. It allows
changing configuration settings for colors, border size rounding mode,
characters used for borders, and transparency.
```
Keybinds:
c/C - Cycle through color modes
b/B - Cycle through border modes
h/H - Cycle through border characters
i/I - Cycle through image modes
t/T - Toggle transparency
d/D - Toggle debug mode
q/Q - Quit
```
Configuration can be also be overriden by environment variables:
- `CLAY_TB_COLOR_MODE`
- `NORMAL`
- `256`
- `216`
- `GRAYSCALE`
- `TRUECOLOR`
- `NOCOLOR`
- `CLAY_TB_BORDER_CHARS`
- `DEFAULT`
- `ASCII`
- `UNICODE`
- `NONE`
- `CLAY_TB_IMAGE_MODE`
- `DEFAULT`
- `PLACEHOLDER`
- `BG`
- `ASCII_FG`
- `ASCII`
- `UNICODE`
- `ASCII_FG_FAST`
- `ASCII_FAST`
- `UNICODE_FAST`
- `CLAY_TB_TRANSPARENCY`
- `1`
- `0`
- `CLAY_TB_CELL_PIXELS`
- `widthxheight`
## Building
Build the binary with cmake
```sh
mkdir build
cd build
cmake ..
make
```
Then run the executable:
```sh
./clay_examples_termbox2_demo
```
## Attributions
Resources used:
- `512px-Shark_antwerp_zoo.jpeg`
- Retrieved from <https://commons.wikimedia.org/wiki/File:Shark_antwerp_zoo.jpg>
- License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/)
- No changes made
- `512px-Balmoral_Castle_30_July_2011.jpg`
- Retrieved from <https://commons.wikimedia.org/wiki/File:Balmoral_Castle_30_July_2011.jpg>
- License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)
- No changes made
- `512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg`
- Retrieved from <https://commons.wikimedia.org/wiki/File:German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Sch%C3%A4ferhund_(Folder_(IV)_22.JPG>
- License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)
- No changes made
- `512px-Rosa_Cubana_2018-09-21_1610.jpeg`
- Retrieved from <https://commons.wikimedia.org/wiki/File:Rosa_Cubana_2018-09-21_1610.jpg>
- License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)
- No changes made
- `512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg`
- Retrieved from <https://commons.wikimedia.org/wiki/File:Vorderer_Graben_10_Bamberg_20190830_001.jpg>
- License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)
- No changes made

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -23,6 +23,7 @@ static Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementC
SDL2_Font *fonts = (SDL2_Font*)userData;
TTF_Font *font = fonts[config->fontId].font;
TTF_SetFontSize(font, config->fontSize);
char *chars = (char *)calloc(text.length + 1, 1);
memcpy(chars, text.chars, text.length);
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);
memcpy(cloned, config->stringContents.chars, config->stringContents.length);
TTF_Font* font = fonts[config->fontId].font;
TTF_SetFontSize(font, config->fontSize);
SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) {
.r = (Uint8)config->textColor.r,
.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: {
Clay_TextRenderData *config = &rcmd->renderData.text;
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_SetTextColor(text, config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a);
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) {
const float starting_y = rect.y + clampedRadii.topLeft;
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);
}
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 length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight;
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) {
const float starting_x = rect.x + clampedRadii.topLeft;
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);
}
if (config->width.bottom > 0) {
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;
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);
@ -211,25 +212,25 @@ static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Cla
//corners
if (config->cornerRadius.topLeft > 0) {
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,
180.0f, 270.0f, config->width.top, config->color);
}
if (config->cornerRadius.topRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.topRight -1;
const float centerY = rect.y + clampedRadii.topRight;
const float centerX = rect.x + rect.w - clampedRadii.topRight;
const float centerY = rect.y + clampedRadii.topRight - 1;
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,
270.0f, 360.0f, config->width.top, config->color);
}
if (config->cornerRadius.bottomLeft > 0) {
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,
90.0f, 180.0f, config->width.bottom, config->color);
}
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 centerY = rect.y + rect.h - clampedRadii.bottomRight -1;
const float centerX = rect.x + rect.w - clampedRadii.bottomRight;
const float centerY = rect.y + rect.h - clampedRadii.bottomRight;
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,
0.0f, 90.0f, config->width.bottom, config->color);
}

View file

@ -91,6 +91,15 @@
Unfortunately Clay does not currently provide feedback on whether a mouse
click was handled or not.
--- if you want to use images with clay, you should pass a pointer to a
sclay_image to the CLAY macro, like this:
CLAY({
...
.image = { .imageData = &(sclay_image){ .view = view, .sampler = 0 } },
})
Using 0 as a sampler uses the sokol default sampler with linear interpolation.
The image should be created using sg_make_image from sokol_gfx.
--- finally, on application shutdown, call
sclay_shutdown()
@ -101,6 +110,15 @@
typedef int sclay_font_t;
typedef struct sclay_image {
sg_view view;
sg_sampler sampler;
struct {
float u0, v0, u1, v1;
} uv;
} sclay_image;
void sclay_setup();
void sclay_shutdown();
@ -227,13 +245,14 @@ Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig
sclay_font_t *fonts = (sclay_font_t *)userData;
if(!fonts) return (Clay_Dimensions){ 0 };
fonsSetFont(_sclay.fonts, fonts[config->fontId]);
fonsSetSize(_sclay.fonts, config->fontSize);
fonsSetSpacing(_sclay.fonts, config->letterSpacing);
fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale);
fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale);
fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);
float ascent, descent, lineh;
fonsVertMetrics(_sclay.fonts, &ascent, &descent, &lineh);
return (Clay_Dimensions) {
.width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL),
.height = ascent - descent
.width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL) / _sclay.dpi_scale,
.height = (ascent - descent) / _sclay.dpi_scale
};
}
@ -246,6 +265,15 @@ static void _draw_rect(float x, float y, float w, float h){
sgl_v2f(x+w, y+h);
}
static void _draw_rect_textured(float x, float y, float w, float h, float u0, float v0, float u1, float v1){
sgl_v2f_t2f(x, y, u0, v0);
sgl_v2f_t2f(x, y, u0, v0);
sgl_v2f_t2f(x+w, y, u1, v0);
sgl_v2f_t2f(x, y+h, u0, v1);
sgl_v2f_t2f(x+w, y+h, u1, v1);
sgl_v2f_t2f(x+w, y+h, u1, v1);
}
static float _SIN[16] = {
0.000000f, 0.104528f, 0.207912f, 0.309017f,
0.406737f, 0.500000f, 0.587785f, 0.669131f,
@ -265,6 +293,23 @@ static void _draw_corner(float x, float y, float rx, float ry){
sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));
}
static void _draw_corner_textured(float x, float y, float rx, float ry, float bx, float by, float bw, float bh, float u0, float v0, float u1, float v1) {
x -= rx;
y -= ry;
#define MAP_U(x) (u0+(((x)-bx)/bw)*(u1-u0))
#define MAP_V(y) (v0+(((y)-by)/bh)*(v1-v0))
sgl_v2f_t2f(x, y, MAP_U(x), MAP_V(y));
for(int i = 0; i < 16; ++i){
sgl_v2f_t2f(x, y, MAP_U(x), MAP_V(y));
float px = x+(rx*_SIN[15-i]);
float py = y+(ry*_SIN[i]);
sgl_v2f_t2f(px, py, MAP_U(px), MAP_V(py));
}
sgl_v2f_t2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]), MAP_U(x+(rx*_SIN[0])), MAP_V(y+(ry*_SIN[15])));
#undef MAP_U
#undef MAP_V
}
/* rx,ry = radius ix,iy = inner radius */
static void _draw_corner_border(float x, float y, float rx, float ry, float ix, float iy){
x -= rx;
@ -389,7 +434,108 @@ void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) {
break;
}
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
//TODO
Clay_ImageRenderData *config = &renderCommand->renderData.image;
sclay_image* img = (sclay_image*)config->imageData;
// by default, u1 and v1 are 1. if we pass 0.
// note, we are modifying a copy !
float u0 = img->uv.u0;
float v0 = img->uv.v0;
float u1 = img->uv.u1;
float v1 = img->uv.v1;
if (u1 == 0.f) {
u1 = 1.f;
}
if (v1 == 0.f) {
v1 = 1.f;
}
int untinted = config->backgroundColor.r == 0 && config->backgroundColor.g == 0 && config->backgroundColor.b == 0 && config->backgroundColor.a == 0;
float cr = untinted ? 1.f : (config->backgroundColor.r / 255.0f);
float gr = untinted ? 1.f : (config->backgroundColor.g / 255.0f);
float br = untinted ? 1.f : (config->backgroundColor.b / 255.0f);
float ar = untinted ? 1.f : (config->backgroundColor.a / 255.0f);
sgl_c4f(cr, gr, br, ar);
Clay_CornerRadius r = config->cornerRadius;
sgl_enable_texture();
sgl_texture(img->view, img->sampler);
sgl_begin_triangle_strip();
if(r.topLeft > 0 || r.topRight > 0){
_draw_corner_textured(bbox.x, bbox.y, -r.topLeft, -r.topLeft, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);
_draw_corner_textured(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);
_draw_rect_textured(bbox.x+r.topLeft, bbox.y,
bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight),
u0 + (r.topLeft/bbox.width)*(u1-u0), v0, u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (CLAY__MAX(r.topLeft, r.topRight)/bbox.height)*(v1-v0));
}
if(r.bottomLeft > 0 || r.bottomRight > 0){
_draw_corner_textured(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);
_draw_corner_textured(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1);
_draw_rect_textured(bbox.x+r.bottomLeft,
bbox.y+bbox.height-CLAY__MAX(r.bottomLeft, r.bottomRight),
bbox.width-r.bottomLeft-r.bottomRight, CLAY__MAX(r.bottomLeft, r.bottomRight),
u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (CLAY__MAX(r.bottomLeft, r.bottomRight)/bbox.height)*(v1-v0), u1 - (r.bottomRight/bbox.width)*(u1-u0), v1);
}
if(r.topLeft < r.bottomLeft){
if(r.topLeft < r.topRight){
_draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft,
u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));
_draw_rect_textured(bbox.x+r.topLeft, bbox.y+r.topRight,
r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft,
u0 + (r.topLeft/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));
} else {
_draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft,
u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));
}
} else {
if(r.bottomLeft < r.bottomRight){
_draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft,
u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));
_draw_rect_textured(bbox.x+r.bottomLeft, bbox.y+r.topLeft,
r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight,
u0 + (r.bottomLeft/bbox.width)*(u1-u0), v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomRight/bbox.height)*(v1-v0));
} else {
_draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft,
u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));
}
}
if(r.topRight < r.bottomRight){
if(r.topRight < r.topLeft){
_draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft,
r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight,
u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topLeft/bbox.height)*(v1-v0), u1 - (r.topRight/bbox.width)*(u1-u0), v1 - (r.bottomRight/bbox.height)*(v1-v0));
_draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
r.topRight, bbox.height-r.topRight-r.bottomRight,
u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));
} else {
_draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,
r.bottomRight, bbox.height-r.topRight-r.bottomRight,
u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));
}
} else {
if(r.bottomRight < r.bottomLeft){
_draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft,
u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1 - (r.bottomRight/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0));
_draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,
r.bottomRight, bbox.height-r.topRight-r.bottomRight,
u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));
} else {
_draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
r.topRight, bbox.height-r.topRight-r.bottomRight,
u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0));
}
}
_draw_rect_textured(bbox.x+CLAY__MAX(r.topLeft, r.bottomLeft),
bbox.y+CLAY__MAX(r.topLeft, r.topRight),
bbox.width-CLAY__MAX(r.topLeft, r.bottomLeft)-CLAY__MAX(r.topRight, r.bottomRight),
bbox.height-CLAY__MAX(r.topLeft, r.topRight)-CLAY__MAX(r.bottomLeft, r.bottomRight),
u0+CLAY__MAX(r.topLeft,r.bottomLeft)/bbox.width*(u1-u0), v0+CLAY__MAX(r.topLeft,r.topRight)/bbox.height*(v1-v0),
u1-CLAY__MAX(r.topRight,r.bottomRight)/bbox.width*(u1-u0), v1-CLAY__MAX(r.bottomLeft,r.bottomRight)/bbox.height*(v1-v0));
sgl_end();
sgl_disable_texture();
break;
}
case CLAY_RENDER_COMMAND_TYPE_BORDER: {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff