Compare commits

...

6 commits

Author SHA1 Message Date
Paul-Marie Masschelier 1ff2e1d274
Merge 752675fe11 into fd97d8179e 2025-10-26 11:00:38 -07:00
Daniel Mayovskiy fd97d8179e
[Renderers/termbox] fixed horizontal text culling bug (#525)
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / check_changes (push) Has been cancelled
Odin Bindings Update / build (macos-latest) (push) Has been cancelled
Odin Bindings Update / build (ubuntu-latest) (push) Has been cancelled
Odin Bindings Update / commit (push) Has been cancelled
2025-10-23 12:58:39 +11:00
Daniel Mayovskiy 7216815536
Fixed termbox2 demo build, added scroll functionality (#523) 2025-10-23 12:57:11 +11:00
Thomas Anderson 83129995f7
[Examples/official-website] updated paths in build.sh 2025-10-23 12:56:20 +11:00
PM 752675fe11 SDL_Clay_RenderFillRoundedRect should not draw overlapping triangles 2025-09-07 01:03:25 -03:00
PM 76875fd298 Add separate rounded corners for SDL2 and SDL3 2025-09-06 17:55:48 -03:00
6 changed files with 229 additions and 149 deletions

View file

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

View file

@ -8,7 +8,7 @@ set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare( FetchContent_Declare(
termbox2 termbox2
GIT_REPOSITORY "https://github.com/termbox/termbox2.git" GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
GIT_TAG "9c9281a9a4c971a2be57f8645e828ec99fd555e8" GIT_TAG "ffd159c2a6106dd5eef338a6702ad15d4d4aa809"
GIT_PROGRESS TRUE GIT_PROGRESS TRUE
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
) )
@ -17,7 +17,7 @@ FetchContent_MakeAvailable(termbox2)
FetchContent_Declare( FetchContent_Declare(
stb stb
GIT_REPOSITORY "https://github.com/nothings/stb.git" GIT_REPOSITORY "https://github.com/nothings/stb.git"
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c" GIT_TAG "fede005abaf93d9d7f3a679d1999b2db341b360f"
GIT_PROGRESS TRUE GIT_PROGRESS TRUE
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
) )

View file

@ -90,7 +90,7 @@ void component_text_pair(const char *key, const char *value)
void component_termbox_settings(void) void component_termbox_settings(void)
{ {
CLAY_AUTO_ID({ CLAY(CLAY_ID("Termbox Settings"), {
.floating = { .floating = {
.attachTo = CLAY_ATTACH_TO_PARENT, .attachTo = CLAY_ATTACH_TO_PARENT,
.zIndex = 1, .zIndex = 1,
@ -509,13 +509,18 @@ Clay_RenderCommandArray CreateLayout(clay_tb_image *image1, clay_tb_image *image
{ {
Clay_BeginLayout(); Clay_BeginLayout();
CLAY_AUTO_ID({ CLAY_AUTO_ID({
.clip = {
.vertical = false,
.horizontal = true,
.childOffset = Clay_GetScrollOffset(),
},
.layout = { .layout = {
.sizing = { .sizing = {
.width = CLAY_SIZING_GROW(), .width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_GROW() .height = CLAY_SIZING_GROW()
}, },
.childAlignment = { .childAlignment = {
.x = CLAY_ALIGN_X_CENTER, .x = CLAY_ALIGN_X_LEFT,
.y = CLAY_ALIGN_Y_CENTER .y = CLAY_ALIGN_Y_CENTER
}, },
.childGap = 64 .childGap = 64
@ -714,12 +719,12 @@ void handle_termbox_events(void)
break; break;
} }
case TB_KEY_MOUSE_WHEEL_UP: { case TB_KEY_MOUSE_WHEEL_UP: {
Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() }; Clay_Vector2 scrollDelta = { 0.5 * Clay_Termbox_Cell_Width(), 0 };
Clay_UpdateScrollContainers(false, scrollDelta, 1); Clay_UpdateScrollContainers(false, scrollDelta, 1);
break; break;
} }
case TB_KEY_MOUSE_WHEEL_DOWN: { case TB_KEY_MOUSE_WHEEL_DOWN: {
Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() }; Clay_Vector2 scrollDelta = { -0.5 * Clay_Termbox_Cell_Width(), 0 };
Clay_UpdateScrollContainers(false, scrollDelta, 1); Clay_UpdateScrollContainers(false, scrollDelta, 1);
break; break;
} }

View file

@ -43,8 +43,17 @@ static Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementC
* no AA or low resolution might make it appear as jagged curves) */ * no AA or low resolution might make it appear as jagged curves) */
static int NUM_CIRCLE_SEGMENTS = 16; static int NUM_CIRCLE_SEGMENTS = 16;
static inline void SDL_Clay_AddQuadIndices(int* indices, int* indexCount, int topLeft, int topRight, int bottomRight, int bottomLeft) {
indices[(*indexCount)++] = bottomLeft;
indices[(*indexCount)++] = topLeft;
indices[(*indexCount)++] = topRight;
indices[(*indexCount)++] = bottomRight;
indices[(*indexCount)++] = bottomLeft;
indices[(*indexCount)++] = topRight;
}
//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles. //all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.
static void SDL_RenderFillRoundedRect(SDL_Renderer* renderer, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) { static void SDL_RenderFillRoundedRect(SDL_Renderer* renderer, const SDL_FRect rect, const Clay_CornerRadius cornerRadius, const Clay_Color _color) {
const SDL_Color color = (SDL_Color) { const SDL_Color color = (SDL_Color) {
.r = (Uint8)_color.r, .r = (Uint8)_color.r,
.g = (Uint8)_color.g, .g = (Uint8)_color.g,
@ -54,48 +63,81 @@ static void SDL_RenderFillRoundedRect(SDL_Renderer* renderer, const SDL_FRect re
int indexCount = 0, vertexCount = 0; int indexCount = 0, vertexCount = 0;
const float maxRadius = SDL_min(rect.w, rect.h) / 2.0f; enum corner_e {
const float clampedRadius = SDL_min(cornerRadius, maxRadius); TOP_LEFT,
TOP_RIGHT,
BOTTOM_RIGHT,
BOTTOM_LEFT,
};
const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int)clampedRadius * 0.5f); const float maxRadius = SDL_min(rect.w, rect.h) / 2.f;
const float clampedRadius[4] = {
SDL_min(cornerRadius.topLeft, maxRadius),
SDL_min(cornerRadius.topRight, maxRadius),
SDL_min(cornerRadius.bottomRight, maxRadius),
SDL_min(cornerRadius.bottomLeft, maxRadius),
};
SDL_Vertex vertices[512]; int numCircleSegments[4];
int indices[512]; int totalVertices = 4 + 4 + 2 * 4;
int totalIndices = 6 + 6*8;
for(unsigned i = 0; i < 4; i++) {
const int n = SDL_max(NUM_CIRCLE_SEGMENTS, (int) clampedRadius[i] * 0.5f);
numCircleSegments[i] = n;
totalVertices += n * 2;
totalIndices += n * 3;
}
SDL_Vertex* vertices = SDL_malloc(totalVertices * sizeof(SDL_Vertex));
int* indices = SDL_malloc(totalIndices * sizeof(int));
const float innerLeft = rect.x + SDL_max(clampedRadius[TOP_LEFT], clampedRadius[BOTTOM_LEFT]);
const float innerTop = rect.y + SDL_max(clampedRadius[TOP_LEFT], clampedRadius[TOP_RIGHT]);
const float innerRight = rect.x + rect.w - SDL_max(clampedRadius[TOP_RIGHT], clampedRadius[BOTTOM_RIGHT]);
const float innerBottom = rect.y + rect.h - SDL_max(clampedRadius[BOTTOM_RIGHT], clampedRadius[BOTTOM_LEFT]);
//define center rectangle //define center rectangle
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0} }; //0 center TL vertices[vertexCount++] = (SDL_Vertex){ {innerLeft, innerTop}, color, {0, 0} }; //0 center TL
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0} }; //1 center TR vertices[vertexCount++] = (SDL_Vertex){ {innerRight, innerTop}, color, {1, 0} }; //1 center TR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //2 center BR vertices[vertexCount++] = (SDL_Vertex){ {innerRight, innerBottom}, color, {1, 1} }; //2 center BR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //3 center BL vertices[vertexCount++] = (SDL_Vertex){ {innerLeft, innerBottom}, color, {0, 1} }; //3 center BL
SDL_Clay_AddQuadIndices(indices, &indexCount, 0, 1, 2, 3);
indices[indexCount++] = 0; const SDL_FPoint cornerCenter[4] = {
indices[indexCount++] = 1; { rect.x + clampedRadius[TOP_LEFT], rect.y + clampedRadius[TOP_LEFT] },
indices[indexCount++] = 3; { rect.x + rect.w - clampedRadius[TOP_RIGHT], rect.y + clampedRadius[TOP_RIGHT]},
indices[indexCount++] = 1; { rect.x + rect.w - clampedRadius[BOTTOM_RIGHT], rect.y + rect.h - clampedRadius[BOTTOM_RIGHT] },
indices[indexCount++] = 2; { rect.x + clampedRadius[BOTTOM_LEFT], rect.y + rect.h - clampedRadius[BOTTOM_LEFT]},
indices[indexCount++] = 3; };
//define corner centers
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[TOP_LEFT], color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[TOP_RIGHT], color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[BOTTOM_RIGHT], color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[BOTTOM_LEFT], color, {0, 0} };
int cornerStartIndex[4];
//define rounded corners as triangle fans //define rounded corners as triangle fans
const float step = (M_PI / 2) / numCircleSegments; for (int i = 0; i < 4; i++) { // Iterate over four corners
for (int i = 0; i < numCircleSegments; i++) { const float step = (M_PI/2) / numCircleSegments[i];
const float angle1 = (float)i * step; SDL_FPoint signedRadius;
const float angle2 = ((float)i + 1.0f) * step;
for (int j = 0; j < 4; j++) { // Iterate over four corners switch (i) {
float cx, cy, signX, signY; case TOP_LEFT: signedRadius = (SDL_FPoint){ -clampedRadius[i], -clampedRadius[i]}; break; // Top-left
case TOP_RIGHT: signedRadius = (SDL_FPoint){ clampedRadius[i], -clampedRadius[i]}; break; // Top-right
switch (j) { case BOTTOM_RIGHT: signedRadius = (SDL_FPoint){ clampedRadius[i], clampedRadius[i]}; break; // Bottom-right
case 0: cx = rect.x + clampedRadius; cy = rect.y + clampedRadius; signX = -1; signY = -1; break; // Top-left case BOTTOM_LEFT: signedRadius = (SDL_FPoint){ -clampedRadius[i], clampedRadius[i]}; break; // Bottom-left
case 1: cx = rect.x + rect.w - clampedRadius; cy = rect.y + clampedRadius; signX = 1; signY = -1; break; // Top-right
case 2: cx = rect.x + rect.w - clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = 1; signY = 1; break; // Bottom-right
case 3: cx = rect.x + clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = -1; signY = 1; break; // Bottom-left
default: return; default: return;
} }
cornerStartIndex[i] = vertexCount;
vertices[vertexCount++] = (SDL_Vertex) { { cornerCenter[i].x + signedRadius.x, cornerCenter[i].y }, color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle1) * clampedRadius * signX, cy + SDL_sinf(angle1) * clampedRadius * signY}, color, {0, 0} }; for (int j = 0; j < numCircleSegments[i]; j++) {
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle2) * clampedRadius * signX, cy + SDL_sinf(angle2) * clampedRadius * signY}, color, {0, 0} }; const float angle = ((float)j + 1.0f) * step;
vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[i].x + SDL_cosf(angle) * signedRadius.x, cornerCenter[i].y + SDL_sinf(angle) * signedRadius.y}, color, {0, 0} };
indices[indexCount++] = j; // Connect to corresponding central rectangle vertex indices[indexCount++] = 4 + i; // Connect to corresponding corner center
indices[indexCount++] = vertexCount - 2; indices[indexCount++] = vertexCount - 2;
indices[indexCount++] = vertexCount - 1; indices[indexCount++] = vertexCount - 1;
} }
@ -103,48 +145,37 @@ static void SDL_RenderFillRoundedRect(SDL_Renderer* renderer, const SDL_FRect re
//Define edge rectangles //Define edge rectangles
// Top edge // Top edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y}, color, {0, 0} }; //TL vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[TOP_RIGHT].x, innerTop}, color, {1, 0} }; //BR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0} }; //TR vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[TOP_LEFT].x, innerTop}, color, {0, 0} }; //BL
int cornerTLLastIndex = cornerStartIndex[TOP_LEFT] + numCircleSegments[TOP_LEFT];
int cornerTRLastIndex = cornerStartIndex[TOP_RIGHT] + numCircleSegments[TOP_RIGHT];
SDL_Clay_AddQuadIndices(indices, &indexCount, cornerTLLastIndex, cornerTRLastIndex, vertexCount - 2, vertexCount - 1);
indices[indexCount++] = 0;
indices[indexCount++] = vertexCount - 2; //TL
indices[indexCount++] = vertexCount - 1; //TR
indices[indexCount++] = 1;
indices[indexCount++] = 0;
indices[indexCount++] = vertexCount - 1; //TR
// Right edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0} }; //RT
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //RB
indices[indexCount++] = 1;
indices[indexCount++] = vertexCount - 2; //RT
indices[indexCount++] = vertexCount - 1; //RB
indices[indexCount++] = 2;
indices[indexCount++] = 1;
indices[indexCount++] = vertexCount - 1; //RB
// Bottom edge // Bottom edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1} }; //BR vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[BOTTOM_LEFT].x, innerBottom}, color, {0, 1} }; //BL
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1} }; //BL vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[BOTTOM_RIGHT].x, innerBottom}, color, {1, 1} }; //BR
int cornerBRLastIndex = cornerStartIndex[BOTTOM_RIGHT] + numCircleSegments[BOTTOM_RIGHT];
int cornerBLLastIndex = cornerStartIndex[BOTTOM_LEFT] + numCircleSegments[BOTTOM_LEFT];
SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 2, vertexCount - 1, cornerBRLastIndex, cornerBLLastIndex);
// Right edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, innerTop}, color, {1, 0} }; //TR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, innerBottom}, color, {1, 1} }; //BR
SDL_Clay_AddQuadIndices(indices, &indexCount, 1, vertexCount - 2, vertexCount - 1, 2);
SDL_Clay_AddQuadIndices(indices, &indexCount, 4 + TOP_RIGHT, cornerStartIndex[TOP_RIGHT], vertexCount - 2, vertexCount - 6);
SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 3, vertexCount - 1, cornerStartIndex[BOTTOM_RIGHT], 4 + BOTTOM_RIGHT);
indices[indexCount++] = 2;
indices[indexCount++] = vertexCount - 2; //BR
indices[indexCount++] = vertexCount - 1; //BL
indices[indexCount++] = 3;
indices[indexCount++] = 2;
indices[indexCount++] = vertexCount - 1; //BL
// Left edge // Left edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //LB vertices[vertexCount++] = (SDL_Vertex){ {rect.x, innerTop}, color, {0, 1} }; //LB
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + clampedRadius}, color, {0, 0} }; //LT vertices[vertexCount++] = (SDL_Vertex){ {rect.x, innerBottom}, color, {0, 0} }; //LT
SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 2, 0, 3, vertexCount - 1);
indices[indexCount++] = 3; SDL_Clay_AddQuadIndices(indices, &indexCount, cornerStartIndex[TOP_LEFT], 4 + TOP_LEFT, vertexCount - 7, vertexCount - 2);
indices[indexCount++] = vertexCount - 2; //LB SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 1, vertexCount - 6, 4 + BOTTOM_LEFT, cornerStartIndex[BOTTOM_LEFT]);
indices[indexCount++] = vertexCount - 1; //LT
indices[indexCount++] = 0;
indices[indexCount++] = 3;
indices[indexCount++] = vertexCount - 1; //LT
// Render everything // Render everything
SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount); SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);
SDL_free(vertices);
SDL_free(indices);
} }
//all rendering is performed by a single SDL call, using twi sets of arcing triangles, inner and outer, that fit together; along with two tringles to fill the end gaps. //all rendering is performed by a single SDL call, using twi sets of arcing triangles, inner and outer, that fit together; along with two tringles to fill the end gaps.
@ -282,8 +313,8 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
.w = boundingBox.width, .w = boundingBox.width,
.h = boundingBox.height, .h = boundingBox.height,
}; };
if (config->cornerRadius.topLeft > 0) { if (config->cornerRadius.topLeft > 1.f || config->cornerRadius.topRight > 1.f || config->cornerRadius.bottomLeft > 1.f || config->cornerRadius.bottomRight > 1.f) {
SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius.topLeft, color); SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius, color);
} }
else { else {
SDL_RenderFillRectF(renderer, &rect); SDL_RenderFillRectF(renderer, &rect);

View file

@ -14,57 +14,96 @@ typedef struct {
* no AA or low resolution might make it appear as jagged curves) */ * no AA or low resolution might make it appear as jagged curves) */
static int NUM_CIRCLE_SEGMENTS = 16; static int NUM_CIRCLE_SEGMENTS = 16;
static inline void SDL_Clay_AddQuadIndices(int* indices, int* indexCount, int topLeft, int topRight, int bottomRight, int bottomLeft) {
indices[(*indexCount)++] = bottomLeft;
indices[(*indexCount)++] = topLeft;
indices[(*indexCount)++] = topRight;
indices[(*indexCount)++] = bottomRight;
indices[(*indexCount)++] = bottomLeft;
indices[(*indexCount)++] = topRight;
}
//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles. //all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.
static void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) { static void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData, const SDL_FRect rect, const Clay_CornerRadius cornerRadius, const Clay_Color _color) {
const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 }; const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 };
int indexCount = 0, vertexCount = 0; int indexCount = 0, vertexCount = 0;
const float minRadius = SDL_min(rect.w, rect.h) / 2.0f; enum corner_e {
const float clampedRadius = SDL_min(cornerRadius, minRadius); TOP_LEFT,
TOP_RIGHT,
BOTTOM_RIGHT,
BOTTOM_LEFT,
};
const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int) clampedRadius * 0.5f); const float maxRadius = SDL_min(rect.w, rect.h) / 2.f;
const float clampedRadius[4] = {
SDL_min(cornerRadius.topLeft, maxRadius),
SDL_min(cornerRadius.topRight, maxRadius),
SDL_min(cornerRadius.bottomRight, maxRadius),
SDL_min(cornerRadius.bottomLeft, maxRadius),
};
int totalVertices = 4 + (4 * (numCircleSegments * 2)) + 2*4; int numCircleSegments[4];
int totalIndices = 6 + (4 * (numCircleSegments * 3)) + 6*4; int totalVertices = 4 + 4 + 2 * 4;
int totalIndices = 6 + 6*8;
for(unsigned i = 0; i < 4; i++) {
const int n = SDL_max(NUM_CIRCLE_SEGMENTS, (int) clampedRadius[i] * 0.5f);
numCircleSegments[i] = n;
totalVertices += n + 1;
totalIndices += n * 3;
}
SDL_Vertex vertices[totalVertices]; SDL_Vertex* vertices = SDL_malloc(totalVertices * sizeof(SDL_Vertex));
int indices[totalIndices]; int* indices = SDL_malloc(totalIndices * sizeof(int));
const float innerLeft = rect.x + SDL_max(clampedRadius[TOP_LEFT], clampedRadius[BOTTOM_LEFT]);
const float innerTop = rect.y + SDL_max(clampedRadius[TOP_LEFT], clampedRadius[TOP_RIGHT]);
const float innerRight = rect.x + rect.w - SDL_max(clampedRadius[TOP_RIGHT], clampedRadius[BOTTOM_RIGHT]);
const float innerBottom = rect.y + rect.h - SDL_max(clampedRadius[BOTTOM_RIGHT], clampedRadius[BOTTOM_LEFT]);
//define center rectangle //define center rectangle
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0} }; //0 center TL vertices[vertexCount++] = (SDL_Vertex){ {innerLeft, innerTop}, color, {0, 0} }; //0 center TL
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0} }; //1 center TR vertices[vertexCount++] = (SDL_Vertex){ {innerRight, innerTop}, color, {1, 0} }; //1 center TR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //2 center BR vertices[vertexCount++] = (SDL_Vertex){ {innerRight, innerBottom}, color, {1, 1} }; //2 center BR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //3 center BL vertices[vertexCount++] = (SDL_Vertex){ {innerLeft, innerBottom}, color, {0, 1} }; //3 center BL
SDL_Clay_AddQuadIndices(indices, &indexCount, 0, 1, 2, 3);
indices[indexCount++] = 0; const SDL_FPoint cornerCenter[4] = {
indices[indexCount++] = 1; { rect.x + clampedRadius[TOP_LEFT], rect.y + clampedRadius[TOP_LEFT] },
indices[indexCount++] = 3; { rect.x + rect.w - clampedRadius[TOP_RIGHT], rect.y + clampedRadius[TOP_RIGHT]},
indices[indexCount++] = 1; { rect.x + rect.w - clampedRadius[BOTTOM_RIGHT], rect.y + rect.h - clampedRadius[BOTTOM_RIGHT] },
indices[indexCount++] = 2; { rect.x + clampedRadius[BOTTOM_LEFT], rect.y + rect.h - clampedRadius[BOTTOM_LEFT]},
indices[indexCount++] = 3; };
//define corner centers
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[TOP_LEFT], color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[TOP_RIGHT], color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[BOTTOM_RIGHT], color, {0, 0} };
vertices[vertexCount++] = (SDL_Vertex){ cornerCenter[BOTTOM_LEFT], color, {0, 0} };
int cornerStartIndex[4];
//define rounded corners as triangle fans //define rounded corners as triangle fans
const float step = (SDL_PI_F/2) / numCircleSegments; for (int i = 0; i < 4; i++) { // Iterate over four corners
for (int i = 0; i < numCircleSegments; i++) { const float step = (SDL_PI_F/2) / numCircleSegments[i];
const float angle1 = (float)i * step; SDL_FPoint signedRadius;
const float angle2 = ((float)i + 1.0f) * step;
for (int j = 0; j < 4; j++) { // Iterate over four corners switch (i) {
float cx, cy, signX, signY; case TOP_LEFT: signedRadius = (SDL_FPoint){ -clampedRadius[i], -clampedRadius[i]}; break; // Top-left
case TOP_RIGHT: signedRadius = (SDL_FPoint){ clampedRadius[i], -clampedRadius[i]}; break; // Top-right
case BOTTOM_RIGHT: signedRadius = (SDL_FPoint){ clampedRadius[i], clampedRadius[i]}; break; // Bottom-right
case BOTTOM_LEFT: signedRadius = (SDL_FPoint){ -clampedRadius[i], clampedRadius[i]}; break; // Bottom-left
default: return;
}
cornerStartIndex[i] = vertexCount;
vertices[vertexCount++] = (SDL_Vertex) { { cornerCenter[i].x + signedRadius.x, cornerCenter[i].y }, color, {0, 0} };
switch (j) { for (int j = 0; j < numCircleSegments[i]; j++) {
case 0: cx = rect.x + clampedRadius; cy = rect.y + clampedRadius; signX = -1; signY = -1; break; // Top-left const float angle = ((float)j + 1.0f) * step;
case 1: cx = rect.x + rect.w - clampedRadius; cy = rect.y + clampedRadius; signX = 1; signY = -1; break; // Top-right vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[i].x + SDL_cosf(angle) * signedRadius.x, cornerCenter[i].y + SDL_sinf(angle) * signedRadius.y}, color, {0, 0} };
case 2: cx = rect.x + rect.w - clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = 1; signY = 1; break; // Bottom-right
case 3: cx = rect.x + clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = -1; signY = 1; break; // Bottom-left
default: return;
}
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle1) * clampedRadius * signX, cy + SDL_sinf(angle1) * clampedRadius * signY}, color, {0, 0} }; indices[indexCount++] = 4 + i; // Connect to corresponding corner center
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle2) * clampedRadius * signX, cy + SDL_sinf(angle2) * clampedRadius * signY}, color, {0, 0} };
indices[indexCount++] = j; // Connect to corresponding central rectangle vertex
indices[indexCount++] = vertexCount - 2; indices[indexCount++] = vertexCount - 2;
indices[indexCount++] = vertexCount - 1; indices[indexCount++] = vertexCount - 1;
} }
@ -72,48 +111,37 @@ static void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData,
//Define edge rectangles //Define edge rectangles
// Top edge // Top edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y}, color, {0, 0} }; //TL vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[TOP_RIGHT].x, innerTop}, color, {1, 0} }; //BR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0} }; //TR vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[TOP_LEFT].x, innerTop}, color, {0, 0} }; //BL
int cornerTLLastIndex = cornerStartIndex[TOP_LEFT] + numCircleSegments[TOP_LEFT];
int cornerTRLastIndex = cornerStartIndex[TOP_RIGHT] + numCircleSegments[TOP_RIGHT];
SDL_Clay_AddQuadIndices(indices, &indexCount, cornerTLLastIndex, cornerTRLastIndex, vertexCount - 2, vertexCount - 1);
indices[indexCount++] = 0;
indices[indexCount++] = vertexCount - 2; //TL
indices[indexCount++] = vertexCount - 1; //TR
indices[indexCount++] = 1;
indices[indexCount++] = 0;
indices[indexCount++] = vertexCount - 1; //TR
// Right edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0} }; //RT
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //RB
indices[indexCount++] = 1;
indices[indexCount++] = vertexCount - 2; //RT
indices[indexCount++] = vertexCount - 1; //RB
indices[indexCount++] = 2;
indices[indexCount++] = 1;
indices[indexCount++] = vertexCount - 1; //RB
// Bottom edge // Bottom edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1} }; //BR vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[BOTTOM_LEFT].x, innerBottom}, color, {0, 1} }; //BL
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1} }; //BL vertices[vertexCount++] = (SDL_Vertex){ {cornerCenter[BOTTOM_RIGHT].x, innerBottom}, color, {1, 1} }; //BR
int cornerBRLastIndex = cornerStartIndex[BOTTOM_RIGHT] + numCircleSegments[BOTTOM_RIGHT];
int cornerBLLastIndex = cornerStartIndex[BOTTOM_LEFT] + numCircleSegments[BOTTOM_LEFT];
SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 2, vertexCount - 1, cornerBRLastIndex, cornerBLLastIndex);
// Right edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, innerTop}, color, {1, 0} }; //TR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, innerBottom}, color, {1, 1} }; //BR
SDL_Clay_AddQuadIndices(indices, &indexCount, 1, vertexCount - 2, vertexCount - 1, 2);
SDL_Clay_AddQuadIndices(indices, &indexCount, 4 + TOP_RIGHT, cornerStartIndex[TOP_RIGHT], vertexCount - 2, vertexCount - 6);
SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 3, vertexCount - 1, cornerStartIndex[BOTTOM_RIGHT], 4 + BOTTOM_RIGHT);
indices[indexCount++] = 2;
indices[indexCount++] = vertexCount - 2; //BR
indices[indexCount++] = vertexCount - 1; //BL
indices[indexCount++] = 3;
indices[indexCount++] = 2;
indices[indexCount++] = vertexCount - 1; //BL
// Left edge // Left edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //LB vertices[vertexCount++] = (SDL_Vertex){ {rect.x, innerTop}, color, {0, 1} }; //LB
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + clampedRadius}, color, {0, 0} }; //LT vertices[vertexCount++] = (SDL_Vertex){ {rect.x, innerBottom}, color, {0, 0} }; //LT
SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 2, 0, 3, vertexCount - 1);
indices[indexCount++] = 3; SDL_Clay_AddQuadIndices(indices, &indexCount, cornerStartIndex[TOP_LEFT], 4 + TOP_LEFT, vertexCount - 7, vertexCount - 2);
indices[indexCount++] = vertexCount - 2; //LB SDL_Clay_AddQuadIndices(indices, &indexCount, vertexCount - 1, vertexCount - 6, 4 + BOTTOM_LEFT, cornerStartIndex[BOTTOM_LEFT]);
indices[indexCount++] = vertexCount - 1; //LT
indices[indexCount++] = 0;
indices[indexCount++] = 3;
indices[indexCount++] = vertexCount - 1; //LT
// Render everything // Render everything
SDL_RenderGeometry(rendererData->renderer, NULL, vertices, vertexCount, indices, indexCount); SDL_RenderGeometry(rendererData->renderer, NULL, vertices, vertexCount, indices, indexCount);
SDL_free(vertices);
SDL_free(indices);
} }
static void SDL_Clay_RenderArc(Clay_SDL3RendererData *rendererData, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) { static void SDL_Clay_RenderArc(Clay_SDL3RendererData *rendererData, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) {
@ -155,8 +183,8 @@ static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Cla
Clay_RectangleRenderData *config = &rcmd->renderData.rectangle; Clay_RectangleRenderData *config = &rcmd->renderData.rectangle;
SDL_SetRenderDrawBlendMode(rendererData->renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawBlendMode(rendererData->renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(rendererData->renderer, config->backgroundColor.r, config->backgroundColor.g, config->backgroundColor.b, config->backgroundColor.a); SDL_SetRenderDrawColor(rendererData->renderer, config->backgroundColor.r, config->backgroundColor.g, config->backgroundColor.b, config->backgroundColor.a);
if (config->cornerRadius.topLeft > 0) { if (config->cornerRadius.topLeft > 1.f || config->cornerRadius.topRight > 1.f || config->cornerRadius.bottomLeft > 1.f || config->cornerRadius.bottomRight > 1.f) {
SDL_Clay_RenderFillRoundedRect(rendererData, rect, config->cornerRadius.topLeft, config->backgroundColor); SDL_Clay_RenderFillRoundedRect(rendererData, rect, config->cornerRadius, config->backgroundColor);
} else { } else {
SDL_RenderFillRect(rendererData->renderer, &rect); SDL_RenderFillRect(rendererData->renderer, &rect);
} }

View file

@ -3,6 +3,8 @@
Copyright (c) 2025 Mivirl Copyright (c) 2025 Mivirl
altered by Godje (Sep 2025)
This software is provided 'as-is', without any express or implied warranty. This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the In no event will the authors be held liable for any damages arising from the
use of this software. use of this software.
@ -1616,6 +1618,20 @@ void Clay_Termbox_Render(Clay_RenderCommandArray commands)
Clay_StringSlice *text = &render_data.stringContents; Clay_StringSlice *text = &render_data.stringContents;
int32_t i = 0; int32_t i = 0;
// culling text characters that are outside of the layout
int h_clip = 0 - cell_box.x;
while(h_clip > 0 && i < text->length){
uint32_t ch = ' ';
int codepoint_length = tb_utf8_char_to_unicode(&ch, text->chars + i);
if (0 > codepoint_length) {
clay_tb_assert(false, "Invalid utf8");
}
i += codepoint_length;
h_clip -= 1;
}
// printing the rest of the characters
for (int y = box_begin_y; y < box_end_y; ++y) { for (int y = box_begin_y; y < box_end_y; ++y) {
for (int x = box_begin_x; x < box_end_x;) { for (int x = box_begin_x; x < box_end_x;) {
uint32_t ch = ' '; uint32_t ch = ' ';