Compare commits

..

37 commits
v0.14 ... main

Author SHA1 Message Date
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
40 changed files with 7169 additions and 477 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

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
@ -161,8 +161,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)

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

@ -186,7 +186,7 @@ FloatingElementConfig :: struct {
attachment: FloatingAttachPoints,
pointerCaptureMode: PointerCaptureMode,
attachTo: FloatingAttachToElement,
clipTo: FloatingClipToElement,
clipTo: FloatingClipToElement,
}
TextRenderData :: struct {
@ -338,11 +338,10 @@ ClayArray :: struct($type: typeid) {
}
ElementDeclaration :: struct {
id: ElementId,
layout: LayoutConfig,
backgroundColor: Color,
cornerRadius: CornerRadius,
aspectRatio: AspectRatioElementConfig,
aspectRatio: AspectRatioElementConfig,
image: ImageElementConfig,
floating: FloatingElementConfig,
custom: CustomElementConfig,
@ -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} },
}) {

377
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);
@ -1173,7 +1182,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 +1291,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 +1367,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 +1665,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 +1715,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 +1730,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;
@ -1756,7 +1778,7 @@ Clay_ElementId Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayou
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);
openLayoutElement->id = elementId.id;
Clay__AddHashMapItem(elementId, openLayoutElement, 0);
Clay__AddHashMapItem(elementId, openLayoutElement);
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
return elementId;
}
@ -1795,13 +1817,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,18 +1848,20 @@ 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->minDimensions.width += childGap;
openLayoutElement->dimensions.width += childGap;
if (!elementHasClipHorizontal) {
openLayoutElement->minDimensions.width += childGap;
}
}
else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
openLayoutElement->dimensions.height = topBottomPadding;
@ -1843,18 +1871,20 @@ 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->minDimensions.height += childGap;
openLayoutElement->dimensions.height += childGap;
if (!elementHasClipVertical) {
openLayoutElement->minDimensions.height += childGap;
}
}
context->layoutElementChildrenBuffer.length -= openLayoutElement->childrenOrTextContent.children.length;
@ -1967,8 +1997,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 +2046,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 +2060,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 +2071,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 +2123,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 +2144,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 +2217,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 +2253,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) {
rootElement->dimensions.width = parentLayoutElement->dimensions.width;
switch (rootElement->layoutConfig->sizing.width.type) {
case CLAY__SIZING_TYPE_GROW: {
rootElement->dimensions.width = parentLayoutElement->dimensions.width;
break;
}
case CLAY__SIZING_TYPE_PERCENT: {
rootElement->dimensions.width = parentLayoutElement->dimensions.width * rootElement->layoutConfig->sizing.width.size.percent;
break;
}
default: break;
}
if (rootElement->layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_GROW) {
rootElement->dimensions.height = parentLayoutElement->dimensions.height;
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;
}
}
}
rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.size.minMax.min), rootElement->layoutConfig->sizing.width.size.minMax.max);
rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.size.minMax.min), rootElement->layoutConfig->sizing.height.size.minMax.max);
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 +2558,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 +2736,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 +2792,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 +2966,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 +2980,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 +3189,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 +3220,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 +3231,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 +3260,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 +3274,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 +3285,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 +3314,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 +3326,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 +3368,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 +3388,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 +3417,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 +3432,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 +3453,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 +3472,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 +3494,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 +3521,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 +3541,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 +3567,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 +3593,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 +3605,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 +3644,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_TEXT(Clay__IntToString(aspectRatioConfig->aspectRatio), infoTextConfig);
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 +3666,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 +3687,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 +3699,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 +3715,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 +3788,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 +3813,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 +3867,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 +3987,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 +4031,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,10 +4216,9 @@ 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)} }
.layout = { .sizing = {CLAY_SIZING_FIXED((rootDimensions.width)), CLAY_SIZING_FIXED(rootDimensions.height)} }
});
Clay__int32_tArray_Add(&context->openLayoutElementStack, 0);
Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) { .layoutElementIndex = 0 });
@ -4198,20 +4246,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

@ -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';
element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';
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';
element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';
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 "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
GIT_PROGRESS TRUE
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(termbox2)
FetchContent_Declare(
stb
GIT_REPOSITORY "https://github.com/nothings/stb.git"
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
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,789 @@
/*
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_AUTO_ID({
.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({
.layout = {
.sizing = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_GROW()
},
.childAlignment = {
.x = CLAY_ALIGN_X_CENTER,
.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, 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)
{
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 "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
GIT_PROGRESS TRUE
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(termbox2)
FetchContent_Declare(
stb
GIT_REPOSITORY "https://github.com/nothings/stb.git"
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
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