Merge branch 'nicbarker:main' into element_interaction_state

This commit is contained in:
fgungor 2025-07-25 22:43:54 +02:00 committed by GitHub
commit af04a21e5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 6606 additions and 78 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

@ -413,7 +413,8 @@ foreign Clay {
@(link_prefix = "Clay_", default_calling_convention = "c", private) @(link_prefix = "Clay_", default_calling_convention = "c", private)
foreign Clay { foreign Clay {
_ConfigureOpenElement :: proc(config: ElementDeclaration) --- _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) --- _OpenTextElement :: proc(text: String, textConfig: ^TextElementConfig) ---
_StoreTextElementConfig :: proc(config: TextElementConfig) -> ^TextElementConfig --- _StoreTextElementConfig :: proc(config: TextElementConfig) -> ^TextElementConfig ---
_GetParentElementId :: proc() -> u32 --- _GetParentElementId :: proc() -> u32 ---
@ -460,11 +461,11 @@ CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
return CornerRadius{radius, radius, radius, radius} return CornerRadius{radius, radius, radius, radius}
} }
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis { SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax = {}) -> SizingAxis {
return SizingAxis{type = SizingType.Fit, constraints = {sizeMinMax = sizeMinMax}} 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}} return SizingAxis{type = SizingType.Grow, constraints = {sizeMinMax = sizeMinMax}}
} }
@ -481,9 +482,9 @@ MakeString :: proc(label: string) -> String {
} }
ID :: proc(label: string, index: u32 = 0) -> ElementId { ID :: proc(label: string, index: u32 = 0) -> ElementId {
return _HashString(MakeString(label), index, 0) return _HashString(MakeString(label), 0)
} }
ID_LOCAL :: proc(label: string, index: u32 = 0) -> ElementId { 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

@ -82,11 +82,11 @@ LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Colo
LandingPageDesktop :: proc() { LandingPageDesktop :: proc() {
if clay.UI()({ if clay.UI()({
id = clay.ID("LandingPage1Desktop"), 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 } }, layout = { sizing = { width = clay.SizingGrow(), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
}) { }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("LandingPage1"), id = clay.ID("LandingPage1"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 }, layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
border = { COLOR_RED, { left = 2, right = 2 } }, 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()({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingPercent(0.55) }, layoutDirection = .TopToBottom, childGap = 8 } }) {
@ -119,13 +119,13 @@ LandingPageMobile :: proc() {
id = clay.ID("LandingPage1Mobile"), id = clay.ID("LandingPage1Mobile"),
layout = { layout = {
layoutDirection = .TopToBottom, 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 }, childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 }, padding = { 16, 16, 32, 32 },
childGap = 32, childGap = 32,
}, },
}) { }) {
if clay.UI()({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingGrow({ }) }, layoutDirection = .TopToBottom, childGap = 8 } }) { if clay.UI()({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingGrow() }, layoutDirection = .TopToBottom, childGap = 8 } }) {
clay.Text( clay.Text(
"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.", "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}), clay.TextConfig({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}),
@ -138,7 +138,7 @@ LandingPageMobile :: proc() {
} }
if clay.UI()({ if clay.UI()({
id = clay.ID("HeroImageOuter"), id = clay.ID("HeroImageOuter"),
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow({ }) }, childAlignment = { x = .Center }, childGap = 16 }, 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(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) LandingPageBlob(2, 24, FONT_ID_BODY_24, COLOR_BLOB_BORDER_4, "Flexbox-style responsive layout", &checkImage4)
@ -175,7 +175,7 @@ FeatureBlocksDesktop :: proc() {
if clay.UI()({ id = clay.ID("FeatureBlocksOuter"), layout = { sizing = { width = clay.SizingGrow({}) } } }) { if clay.UI()({ id = clay.ID("FeatureBlocksOuter"), layout = { sizing = { width = clay.SizingGrow({}) } } }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("FeatureBlocksInner"), id = clay.ID("FeatureBlocksInner"),
layout = { sizing = { width = clay.SizingGrow({ }) }, childAlignment = { y = .Center } }, layout = { sizing = { width = clay.SizingGrow() }, childAlignment = { y = .Center } },
border = { width = { betweenChildren = 2}, color = COLOR_RED }, border = { width = { betweenChildren = 2}, color = COLOR_RED },
}) { }) {
FeatureBlocks(clay.SizingPercent(0.5), 50) FeatureBlocks(clay.SizingPercent(0.5), 50)
@ -186,7 +186,7 @@ FeatureBlocksDesktop :: proc() {
FeatureBlocksMobile :: proc() { FeatureBlocksMobile :: proc() {
if clay.UI()({ if clay.UI()({
id = clay.ID("FeatureBlocksInner"), id = clay.ID("FeatureBlocksInner"),
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow({ }) } }, layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow() } },
border = { width = { betweenChildren = 2}, color = COLOR_RED }, border = { width = { betweenChildren = 2}, color = COLOR_RED },
}) { }) {
FeatureBlocks(clay.SizingGrow({}), 16) FeatureBlocks(clay.SizingGrow({}), 16)
@ -223,11 +223,11 @@ DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizi
DeclarativeSyntaxPageDesktop :: proc() { DeclarativeSyntaxPageDesktop :: proc() {
if clay.UI()({ if clay.UI()({
id = clay.ID("SyntaxPageDesktop"), id = clay.ID("SyntaxPageDesktop"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } }, layout = { sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
}) { }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("SyntaxPage"), id = clay.ID("SyntaxPage"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 }, layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
border = border2pxRed, border = border2pxRed,
}) { }) {
DeclarativeSyntaxPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5)) DeclarativeSyntaxPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5))
@ -240,7 +240,7 @@ DeclarativeSyntaxPageMobile :: proc() {
id = clay.ID("SyntaxPageMobile"), id = clay.ID("SyntaxPageMobile"),
layout = { layout = {
layoutDirection = .TopToBottom, 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 }, childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 }, padding = { 16, 16, 32, 32 },
childGap = 16, childGap = 16,
@ -276,19 +276,19 @@ HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementCon
if clay.UI()({ id = clay.ID("PerformanceRightImageOuter"), layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) { if clay.UI()({ id = clay.ID("PerformanceRightImageOuter"), layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("PerformanceRightBorder"), id = clay.ID("PerformanceRightBorder"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(400) } }, layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(400) } },
border = { COLOR_LIGHT, {2, 2, 2, 2, 2} }, border = { COLOR_LIGHT, {2, 2, 2, 2, 2} },
}) { }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("AnimationDemoContainerLeft"), id = clay.ID("AnimationDemoContainerLeft"),
layout = { sizing = { clay.SizingPercent(0.35 + 0.3 * lerpValue), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) }, 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), backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue),
}) { }) {
clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT})) clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT}))
} }
if clay.UI()({ if clay.UI()({
id = clay.ID("AnimationDemoContainerRight"), id = clay.ID("AnimationDemoContainerRight"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) }, layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue), backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue),
}) { }) {
clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT})) clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT}))
@ -300,7 +300,7 @@ HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementCon
HighPerformancePageDesktop :: proc(lerpValue: f32) { HighPerformancePageDesktop :: proc(lerpValue: f32) {
if clay.UI()({ if clay.UI()({
id = clay.ID("PerformanceDesktop"), 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 }, layout = { sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { 82, 82, 32, 32 }, childGap = 64 },
backgroundColor = COLOR_RED, backgroundColor = COLOR_RED,
}) { }) {
HighPerformancePage(lerpValue, {fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_LIGHT}, clay.SizingPercent(0.5)) HighPerformancePage(lerpValue, {fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_LIGHT}, clay.SizingPercent(0.5))
@ -312,7 +312,7 @@ HighPerformancePageMobile :: proc(lerpValue: f32) {
id = clay.ID("PerformanceMobile"), id = clay.ID("PerformanceMobile"),
layout = { layout = {
layoutDirection = .TopToBottom, 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 }, childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 }, padding = { 16, 16, 32, 32 },
childGap = 32, childGap = 32,
@ -376,11 +376,11 @@ RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.
RendererPageDesktop :: proc() { RendererPageDesktop :: proc() {
if clay.UI()({ if clay.UI()({
id = clay.ID("RendererPageDesktop"), id = clay.ID("RendererPageDesktop"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } }, layout = { sizing = { clay.SizingGrow(), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
}) { }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("RendererPage"), id = clay.ID("RendererPage"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 }, layout = { sizing = { clay.SizingGrow(), clay.SizingGrow() }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
border = { COLOR_RED, { left = 2, right = 2 } }, border = { COLOR_RED, { left = 2, right = 2 } },
}) { }) {
RendererPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5)) RendererPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5))
@ -393,7 +393,7 @@ RendererPageMobile :: proc() {
id = clay.ID("RendererMobile"), id = clay.ID("RendererMobile"),
layout = { layout = {
layoutDirection = .TopToBottom, 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 }, childAlignment = { x = .Center, y = .Center },
padding = { 16, 16, 32, 32 }, padding = { 16, 16, 32, 32 },
childGap = 32, childGap = 32,
@ -418,15 +418,15 @@ createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
clay.BeginLayout() clay.BeginLayout()
if clay.UI()({ if clay.UI()({
id = clay.ID("OuterContainer"), id = clay.ID("OuterContainer"),
layout = { layoutDirection = .TopToBottom, sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) } }, layout = { layoutDirection = .TopToBottom, sizing = { clay.SizingGrow(), clay.SizingGrow() } },
backgroundColor = COLOR_LIGHT, backgroundColor = COLOR_LIGHT,
}) { }) {
if clay.UI()({ if clay.UI()({
id = clay.ID("Header"), id = clay.ID("Header"),
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(50) }, childAlignment = { y = .Center }, childGap = 24, padding = { left = 32, right = 32 } }, layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(50) }, childAlignment = { y = .Center }, childGap = 24, padding = { left = 32, right = 32 } },
}) { }) {
clay.Text("Clay", &headerTextConfig) clay.Text("Clay", &headerTextConfig)
if clay.UI()({ layout = { sizing = { width = clay.SizingGrow({ }) } } }) {} if clay.UI()({ layout = { sizing = { width = clay.SizingGrow() } } }) {}
if (!mobileScreen) { if (!mobileScreen) {
if clay.UI()({ id = clay.ID("LinkExamplesOuter"), backgroundColor = {0, 0, 0, 0} }) { if clay.UI()({ id = clay.ID("LinkExamplesOuter"), backgroundColor = {0, 0, 0, 0} }) {
@ -446,15 +446,15 @@ 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}})) 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("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("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("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("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("TopBorder5"), layout = { sizing = { clay.SizingGrow(), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_1 } ) {}
if clay.UI()({ if clay.UI()({
id = clay.ID("ScrollContainerBackgroundRectangle"), id = clay.ID("ScrollContainerBackgroundRectangle"),
clip = { vertical = true, childOffset = clay.GetScrollOffset() }, 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, backgroundColor = COLOR_LIGHT,
border = { COLOR_RED, { betweenChildren = 2} }, border = { COLOR_RED, { betweenChildren = 2} },
}) { }) {

90
clay.h
View file

@ -73,24 +73,24 @@
#define CLAY_SIZING_PERCENT(percentOfParent) (CLAY__INIT(Clay_SizingAxis) { .size = { .percent = (percentOfParent) }, .type = CLAY__SIZING_TYPE_PERCENT }) #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. // 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. // 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_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. // 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, index) 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. // 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_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])) #define CLAY__STRING_LENGTH(s) ((sizeof(s) / sizeof((s)[0])) - sizeof((s)[0]))
@ -920,7 +920,8 @@ CLAY_DLL_EXPORT void Clay__OpenElement(void);
CLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config); 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__ConfigureOpenElementPtr(const Clay_ElementDeclaration *config);
CLAY_DLL_EXPORT void Clay__CloseElement(void); 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 void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
CLAY_DLL_EXPORT Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config); CLAY_DLL_EXPORT Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);
CLAY_DLL_EXPORT uint32_t Clay__GetParentElementId(void); CLAY_DLL_EXPORT uint32_t Clay__GetParentElementId(void);
@ -1361,7 +1362,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" 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 hash = 0;
uint32_t base = seed; uint32_t base = seed;
@ -1644,7 +1660,10 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
char current = text->chars[end]; char current = text->chars[end];
if (current == ' ' || current == '\n') { if (current == ' ' || current == '\n') {
int32_t length = end - start; int32_t length = end - start;
Clay_Dimensions dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = length, .chars = &text->chars[start], .baseChars = text->chars }, config, context->measureTextUserData); Clay_Dimensions dimensions = {};
if (length > 0) {
dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) {.length = length, .chars = &text->chars[start], .baseChars = text->chars}, config, context->measureTextUserData);
}
measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth); measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
measuredHeight = CLAY__MAX(measuredHeight, dimensions.height); measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
if (current == ' ') { if (current == ' ') {
@ -2091,10 +2110,10 @@ void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(parentItem->layoutElement - context->layoutElements.internalArray)); clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(parentItem->layoutElement - context->layoutElements.internalArray));
} }
} else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ROOT) { } else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ROOT) {
floatingConfig.parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0, 0).id; floatingConfig.parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0).id;
} }
if (!openLayoutElementId.id) { if (!openLayoutElementId.id) {
openLayoutElementId = Clay__HashString(CLAY_STRING("Clay__FloatingContainer"), context->layoutElementTreeRoots.length, 0); openLayoutElementId = Clay__HashStringWithOffset(CLAY_STRING("Clay__FloatingContainer"), context->layoutElementTreeRoots.length, 0);
} }
if (declaration->floating.clipTo == CLAY_CLIP_TO_NONE) { if (declaration->floating.clipTo == CLAY_CLIP_TO_NONE) {
clipElementId = 0; clipElementId = 0;
@ -2230,17 +2249,37 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId); Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId);
if (parentItem && parentItem != &Clay_LayoutElementHashMapItem_DEFAULT) { if (parentItem && parentItem != &Clay_LayoutElementHashMapItem_DEFAULT) {
Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement; Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement;
if (rootElement->layoutConfig->sizing.width.type == CLAY__SIZING_TYPE_GROW) { switch (rootElement->layoutConfig->sizing.width.type) {
case CLAY__SIZING_TYPE_GROW: {
rootElement->dimensions.width = parentLayoutElement->dimensions.width; rootElement->dimensions.width = parentLayoutElement->dimensions.width;
break;
} }
if (rootElement->layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_GROW) { case CLAY__SIZING_TYPE_PERCENT: {
rootElement->dimensions.width = parentLayoutElement->dimensions.width * rootElement->layoutConfig->sizing.width.size.percent;
break;
}
default: break;
}
switch (rootElement->layoutConfig->sizing.height.type) {
case CLAY__SIZING_TYPE_GROW: {
rootElement->dimensions.height = parentLayoutElement->dimensions.height; rootElement->dimensions.height = parentLayoutElement->dimensions.height;
break;
}
case CLAY__SIZING_TYPE_PERCENT: {
rootElement->dimensions.height = parentLayoutElement->dimensions.height * rootElement->layoutConfig->sizing.height.size.percent;
break;
}
default: break;
} }
} }
} }
if (rootElement->layoutConfig->sizing.width.type != CLAY__SIZING_TYPE_PERCENT) {
rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.size.minMax.min), rootElement->layoutConfig->sizing.width.size.minMax.max); 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); 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) { for (int32_t i = 0; i < bfsBuffer.length; ++i) {
int32_t parentIndex = Clay__int32_tArray_GetValue(&bfsBuffer, i); int32_t parentIndex = Clay__int32_tArray_GetValue(&bfsBuffer, i);
@ -2929,6 +2968,7 @@ void Clay__CalculateFinalLayout(void) {
default: break; default: break;
} }
currentElementTreeNode->nextChildOffset.x += extraSpace; currentElementTreeNode->nextChildOffset.x += extraSpace;
extraSpace = CLAY__MAX(0, extraSpace);
} else { } else {
for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) { for (int32_t i = 0; i < currentElement->childrenOrTextContent.children.length; ++i) {
Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]); Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, currentElement->childrenOrTextContent.children.elements[i]);
@ -2942,6 +2982,7 @@ void Clay__CalculateFinalLayout(void) {
case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break; case CLAY_ALIGN_Y_CENTER: extraSpace /= 2; break;
default: break; default: break;
} }
extraSpace = CLAY__MAX(0, extraSpace);
currentElementTreeNode->nextChildOffset.y += extraSpace; currentElementTreeNode->nextChildOffset.y += extraSpace;
} }
@ -3276,7 +3317,7 @@ Clay__RenderDebugLayoutData Clay__RenderDebugLayoutElementsList(int32_t initialR
} }
if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { 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--) { for (int32_t i = (int)context->pointerOverIds.length - 1; i >= 0; i--) {
Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i); Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);
if (elementId->baseId == collapseButtonId.baseId) { if (elementId->baseId == collapseButtonId.baseId) {
@ -3379,7 +3420,7 @@ void HandleDebugViewCloseButtonInteraction(Clay_ElementId elementId, Clay_Pointe
void Clay__RenderDebugView(void) { void Clay__RenderDebugView(void) {
Clay_Context* context = Clay_GetCurrentContext(); 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) { if (context->pointerInfo.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
for (int32_t i = 0; i < context->pointerOverIds.length; ++i) { for (int32_t i = 0; i < context->pointerOverIds.length; ++i) {
Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i); Clay_ElementId *elementId = Clay_ElementIdArray_Get(&context->pointerOverIds, i);
@ -3394,7 +3435,7 @@ void Clay__RenderDebugView(void) {
uint32_t initialElementsLength = context->layoutElements.length; 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 *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_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; float scrollYOffset = 0;
bool pointerInDebugView = context->pointerInfo.position.y < context->layoutDimensions.height - 300; bool pointerInDebugView = context->pointerInfo.position.y < context->layoutDimensions.height - 300;
for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) { for (int32_t i = 0; i < context->scrollContainerDatas.length; ++i) {
@ -3437,7 +3478,7 @@ void Clay__RenderDebugView(void) {
CLAY({ .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(1)} }, .backgroundColor = CLAY__DEBUGVIEW_COLOR_3 } ) {} 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({ .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({ .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_ElementId panelContentsId = Clay__HashString(CLAY_STRING("Clay__DebugViewPaneOuter"), 0);
// Element list // 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({ .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({ .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 } }) {
@ -3609,7 +3650,16 @@ void Clay__RenderDebugView(void) {
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoAspectRatioBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) { CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoAspectRatioBody"), .layout = { .padding = attributeConfigPadding, .childGap = 8, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
CLAY_TEXT(CLAY_STRING("Aspect Ratio"), infoTitleConfig); CLAY_TEXT(CLAY_STRING("Aspect Ratio"), infoTitleConfig);
// Aspect Ratio // Aspect Ratio
CLAY({ .id = CLAY_ID("Clay__DebugViewElementInfoAspectRatio") }) {
CLAY_TEXT(Clay__IntToString(aspectRatioConfig->aspectRatio), infoTextConfig); 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; break;
} }
@ -3940,7 +3990,7 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
Clay_BoundingBox elementBox = mapItem->boundingBox; Clay_BoundingBox elementBox = mapItem->boundingBox;
elementBox.x -= root->pointerOffset.x; elementBox.x -= root->pointerOffset.x;
elementBox.y -= root->pointerOffset.y; elementBox.y -= root->pointerOffset.y;
if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)))) { if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)) || context->externalScrollHandlingEnabled)) {
if (mapItem->onHoverFunction) { if (mapItem->onHoverFunction) {
mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData); mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData);
} }
@ -4212,12 +4262,12 @@ Clay_RenderCommandArray Clay_EndLayout(void) {
CLAY_WASM_EXPORT("Clay_GetElementId") CLAY_WASM_EXPORT("Clay_GetElementId")
Clay_ElementId Clay_GetElementId(Clay_String idString) { Clay_ElementId Clay_GetElementId(Clay_String idString) {
return Clay__HashString(idString, 0, 0); return Clay__HashString(idString, 0);
} }
CLAY_WASM_EXPORT("Clay_GetElementIdWithIndex") CLAY_WASM_EXPORT("Clay_GetElementIdWithIndex")
Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index) { 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) { 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]; TTF_Font *font = fonts[config->fontId];
int width, height; int width, height;
TTF_SetFontSize(font, config->fontSize);
if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) { if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s", SDL_GetError());
} }

View file

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

View file

@ -69,7 +69,7 @@ Clay_RenderCommandArray CreateLayout(void) {
}) })
{ {
CLAY({ .id = CLAY_ID("FloatingContainer"), CLAY({ .id = CLAY_ID("FloatingContainer"),
.layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = { 16, 16, 16, 16 }}, .layout = { .sizing = { .width = CLAY_SIZING_PERCENT(0.5), .height = CLAY_SIZING_FIXED(300) }, .padding = { 16, 16, 16, 16 }},
.backgroundColor = { 140, 80, 200, 200 }, .backgroundColor = { 140, 80, 200, 200 },
.floating = { .attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1, .attachPoints = { CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP }, .offset = {0, 0} }, .floating = { .attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1, .attachPoints = { CLAY_ATTACH_POINT_CENTER_TOP, CLAY_ATTACH_POINT_CENTER_TOP }, .offset = {0, 0} },
.border = { .width = CLAY_BORDER_OUTSIDE(2), .color = {80, 80, 80, 255} }, .border = { .width = CLAY_BORDER_OUTSIDE(2), .color = {80, 80, 80, 255} },
@ -108,8 +108,8 @@ Clay_RenderCommandArray CreateLayout(void) {
CLAY({ .id = CLAY_ID("Blob4Floating2"), .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("SidebarBlob4")).id } }) { CLAY({ .id = CLAY_ID("Blob4Floating2"), .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("SidebarBlob4")).id } }) {
CLAY({ .id = CLAY_ID("ScrollContainer"), .layout = { .sizing = { .height = CLAY_SIZING_FIXED(200) }, .childGap = 2 }, .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) { CLAY({ .id = CLAY_ID("ScrollContainer"), .layout = { .sizing = { .height = CLAY_SIZING_FIXED(200) }, .childGap = 2 }, .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
CLAY({ .id = CLAY_ID("FloatingContainer2"), .floating = { .attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1 } }) { CLAY({ .id = CLAY_ID("FloatingContainer2"), .layout.sizing.height = CLAY_SIZING_GROW(), .floating = { .attachTo = CLAY_ATTACH_TO_PARENT, .zIndex = 1 } }) {
CLAY({ .id = CLAY_ID("FloatingContainerInner"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) }, .padding = {16, 16, 16, 16} }, .backgroundColor = {140,80, 200, 200} }) { CLAY({ .id = CLAY_ID("FloatingContainerInner"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW() }, .padding = {16, 16, 16, 16} }, .backgroundColor = {140,80, 200, 200} }) {
CLAY_TEXT(CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255,255,255,255} })); CLAY_TEXT(CLAY_STRING("I'm an inline floating container."), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255,255,255,255} }));
} }
} }
@ -133,7 +133,7 @@ Clay_RenderCommandArray CreateLayout(void) {
}) { }) {
CLAY({ .id = CLAY_ID("ScrollBarButton"), CLAY({ .id = CLAY_ID("ScrollBarButton"),
.layout = { .sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height) }}, .layout = { .sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height) }},
.backgroundColor = Clay_PointerOver(Clay__HashString(CLAY_STRING("ScrollBar"), 0, 0)) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150} , .backgroundColor = Clay_PointerOver(Clay_GetElementId(CLAY_STRING("ScrollBar"))) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150} ,
.cornerRadius = CLAY_CORNER_RADIUS(6) .cornerRadius = CLAY_CORNER_RADIUS(6)
}) {} }) {}
} }
@ -172,13 +172,13 @@ void UpdateDrawFrame(Font* fonts)
scrollbarData.mouseDown = false; scrollbarData.mouseDown = false;
} }
if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(Clay__HashString(CLAY_STRING("ScrollBar"), 0, 0))) { if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(Clay_GetElementId(CLAY_STRING("ScrollBar")))) {
Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay__HashString(CLAY_STRING("MainContent"), 0, 0)); Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("MainContent")));
scrollbarData.clickOrigin = mousePosition; scrollbarData.clickOrigin = mousePosition;
scrollbarData.positionOrigin = *scrollContainerData.scrollPosition; scrollbarData.positionOrigin = *scrollContainerData.scrollPosition;
scrollbarData.mouseDown = true; scrollbarData.mouseDown = true;
} else if (scrollbarData.mouseDown) { } else if (scrollbarData.mouseDown) {
Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay__HashString(CLAY_STRING("MainContent"), 0, 0)); Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay_GetElementId(CLAY_STRING("MainContent")));
if (scrollContainerData.contentDimensions.height > 0) { if (scrollContainerData.contentDimensions.height > 0) {
Clay_Vector2 ratio = (Clay_Vector2) { Clay_Vector2 ratio = (Clay_Vector2) {
scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width, scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width,

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({
.layout = {
.sizing = {
.width = {
.size.minMax = {
.min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
}
},
}
},
}) {
CLAY_TEXT(keytext, textconfig);
CLAY({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
CLAY_TEXT(valtext, textconfig);
}
}
void component_termbox_settings(void)
{
CLAY({
.floating = {
.attachTo = CLAY_ATTACH_TO_PARENT,
.zIndex = 1,
.attachPoints = { CLAY_ATTACH_POINT_CENTER_CENTER, CLAY_ATTACH_POINT_CENTER_TOP },
.offset = { 0, 0 }
},
}) {
CLAY({
.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({
.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({
.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({
.layout ={
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = CLAY_SIZING_FIT(),
.childGap = Clay_Termbox_Cell_Height()
},
}) {
for (float ri = 0; ri < 4; ri += 1) {
CLAY({
.layout ={
.sizing = CLAY_SIZING_FIT(),
.childGap = Clay_Termbox_Cell_Width()
},
}) {
for (float r = ri * 0x44; r < (ri + 1) * 0x44; r += 0x22) {
CLAY({
.layout ={
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = CLAY_SIZING_FIT(),
},
}) {
for (float g = 0; g < 0xff; g += 0x22) {
CLAY({
.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({
.layout = layout,
.backgroundColor = color
}) {}
} else if (1 == type) {
CLAY({
.layout = layout,
}) {
CLAY_TEXT(CLAY_STRING("#"), CLAY_TEXT_CONFIG({ .textColor = color }));
}
}
}
}
}
}
}
}
}
}
}
}
}
void component_unicode_text(void)
{
CLAY({
.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({
.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({
.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({
.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({
.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({
.layout = layout,
.backgroundColor = color
}) {}
}
}
void component_bordered_text(void)
{
CLAY({
.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({
.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({
.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({
.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({
.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({
.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({
.layout = {
.childAlignment = {
.x = CLAY_ALIGN_X_RIGHT,
},
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = CLAY_SIZING_FIT(),
},
}) {
component_keybinds();
component_unicode_text();
}
CLAY({
.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({
.layout = {
.sizing = {
.width = {
.size.minMax = {
.min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
}
},
}
},
}) {
CLAY_TEXT(keytext, textconfig);
CLAY({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
CLAY_TEXT(valtext, textconfig);
}
}
void component_termbox_settings(void)
{
CLAY({
.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({
.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({
.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({
.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({
.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({
.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({
.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({
.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({
.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({
.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({
.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; SDL2_Font *fonts = (SDL2_Font*)userData;
TTF_Font *font = fonts[config->fontId].font; TTF_Font *font = fonts[config->fontId].font;
TTF_SetFontSize(font, config->fontSize);
char *chars = (char *)calloc(text.length + 1, 1); char *chars = (char *)calloc(text.length + 1, 1);
memcpy(chars, text.chars, text.length); memcpy(chars, text.chars, text.length);
int width = 0; int width = 0;
@ -294,6 +295,7 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
char *cloned = (char *)calloc(config->stringContents.length + 1, 1); char *cloned = (char *)calloc(config->stringContents.length + 1, 1);
memcpy(cloned, config->stringContents.chars, config->stringContents.length); memcpy(cloned, config->stringContents.chars, config->stringContents.length);
TTF_Font* font = fonts[config->fontId].font; TTF_Font* font = fonts[config->fontId].font;
TTF_SetFontSize(font, config->fontSize);
SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) { SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) {
.r = (Uint8)config->textColor.r, .r = (Uint8)config->textColor.r,
.g = (Uint8)config->textColor.g, .g = (Uint8)config->textColor.g,

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff