Compare commits

...

15 Commits

Author SHA1 Message Date
Shivam7-1
9665efda42
Merge 63a74a92a8 into 5fae7a6249 2025-01-29 15:31:43 +01:00
Nic Barker
5fae7a6249 [Core] Compensate for OSes that don't return 64b aligned memory from malloc
Some checks are pending
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Waiting to run
2025-01-29 21:16:24 +13:00
Nic Barker
1bcf256e4d
[Core] Replace generated arrays with macro declarations, align cache lines to 64 bytes (#235)
Some checks are pending
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Waiting to run
2025-01-29 17:14:01 +13:00
Martin Evald
e9f2e6c4f1
[Renderers/SDL2] Don't take addresses of temporaries. (#232) 2025-01-29 13:09:41 +13:00
noflashbang
34f2dab9e8
Normalized usage of Clay__defaultMaxElementCount and Clay__defaultMaxMeasureTextWordCacheCount (#233) 2025-01-29 13:09:07 +13:00
Nic Barker
951d785deb [Documentation] Fix incorrect type information in README for CLAY_IDI
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
2025-01-26 15:43:55 +13:00
Nic Barker
4612481d25 [Documentation] Fix some typos in the README example code 2025-01-26 15:35:30 +13:00
Nic Barker
0a703de69a
[Core] Add z-index and string base to Render Commands (#227) 2025-01-26 15:28:35 +13:00
Nic Barker
cb62db77e3 [Documentation] Combine quick start steps into a single code block in main README 2025-01-26 15:22:46 +13:00
Cory
ea6109bd0b
[CMake] Make Examples Optional in CMAKE (#216) 2025-01-26 15:05:45 +13:00
arnauNau
c0dac38c87
[Renderers/SDL3] Add borders and rounded borders functionality. (#220) 2025-01-26 14:39:34 +13:00
Timothy Hoyt
c3fcf6ce12
[Bindings/C++] Link and information for ClayMan, a C++ wrapper library for clay (#218)
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
2025-01-24 20:51:00 +13:00
arnauNau
aba846a446
[Renderers/SDL3] Add rounded corners rectangle functionality (#219)
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
2025-01-23 09:30:24 +13:00
Shivam7-1
63a74a92a8
Rename fuzzing_target.cc to fuzzing_target.c 2024-12-22 21:10:58 +05:30
Shivam7-1
8d3cadc52e
initial fuzzing support 2024-12-21 17:04:07 +05:30
35 changed files with 707 additions and 1160 deletions

View File

@ -3,17 +3,38 @@ project(clay)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
option(CLAY_INCLUDE_ALL_EXAMPLES "Build all examples" ON)
option(CLAY_INCLUDE_DEMOS "Build video demo and website" OFF)
option(CLAY_INCLUDE_CPP_EXAMPLE "Build C++ example" OFF)
option(CLAY_INCLUDE_RAYLIB_EXAMPLES "Build raylib examples" OFF)
option(CLAY_INCLUDE_SDL2_EXAMPLES "Build SDL 2 examples" OFF)
option(CLAY_INCLUDE_SDL3_EXAMPLES "Build SDL 3 examples" OFF)
message(STATUS "CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}")
if(APPLE)
enable_language(OBJC)
endif()
add_subdirectory("examples/cpp-project-example")
add_subdirectory("examples/raylib-multi-context")
add_subdirectory("examples/raylib-sidebar-scrolling-container")
# add_subdirectory("examples/cairo-pdf-rendering") Some issue with github actions populating cairo, disable for now
if(NOT MSVC)
add_subdirectory("examples/clay-official-website")
add_subdirectory("examples/SDL3-simple-demo")
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_CPP_EXAMPLE)
add_subdirectory("examples/cpp-project-example")
endif()
add_subdirectory("examples/introducing-clay-video-demo")
add_subdirectory("examples/SDL2-video-demo")
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_DEMOS)
if(NOT MSVC)
add_subdirectory("examples/clay-official-website")
endif()
add_subdirectory("examples/introducing-clay-video-demo")
endif ()
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_RAYLIB_EXAMPLES)
add_subdirectory("examples/raylib-multi-context")
add_subdirectory("examples/raylib-sidebar-scrolling-container")
endif ()
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL2_EXAMPLES)
add_subdirectory("examples/SDL2-video-demo")
endif ()
if(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES))
add_subdirectory("examples/SDL3-simple-demo")
endif()
# add_subdirectory("examples/cairo-pdf-rendering") Some issue with github actions populating cairo, disable for now

155
README.md
View File

@ -20,26 +20,17 @@ _An example GUI application built with clay_
## Quick Start
1. Download or clone clay.h and include it after defining `CLAY_IMPLEMENTATION` in one file.
Download or clone clay.h and include it after defining `CLAY_IMPLEMENTATION` in one file.
```C
// Must be defined in one file, _before_ #include "clay.h"
#define CLAY_IMPLEMENTATION
#include "clay.h"
```
#include "../../clay.h"
2. Ask clay for how much static memory it needs using [Clay_MinMemorySize()](#clay_minmemorysize), create an Arena for it to use with [Clay_CreateArenaWithCapacityAndMemory(size, void *memory)](#clay_createarenawithcapacityandmemory).
const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};
const Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};
const Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};
```C
// Note: malloc is only used here as an example, any allocator that provides
// a pointer to addressable memory of at least totalMemorySize will work
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
```
3. Create an [ErrorHandler](#clay_errorhandler) for Clay to call when an internal error occurs, and initialize Clay with the Arena and handler by calling [Clay_Initialize(arena, dimensions, errorHandler)](#clay_initialize).
```C
void HandleClayErrors(Clay_ErrorData errorData) {
// See the Clay_ErrorData struct for more information
printf("%s", errorData.errorText.chars);
@ -48,51 +39,16 @@ void HandleClayErrors(Clay_ErrorData errorData) {
}
}
// In your startup function
Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });
```
4. Provide a `MeasureText(text, config)` function pointer with [Clay_SetMeasureTextFunction(function)](#clay_setmeasuretextfunction) so that clay can measure and wrap text.
```C
// Example measure text function
static inline Clay_Dimensions MeasureText(Clay_String *text, Clay_TextElementConfig *config) {
static inline Clay_Dimensions MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData) {
// Clay_TextElementConfig contains members such as fontId, fontSize, letterSpacing etc
// Note: Clay_String->chars is not guaranteed to be null terminated
return (Clay_Dimensions) {
.width = text.length * config->fontSize, // <- this will only work for monospace fonts, see the renderers/ directory for more advanced text measurement
.height = config->fontSize
};
}
// Tell clay how to measure text
Clay_SetMeasureTextFunction(MeasureText);
```
4. **Optional** - Call [Clay_SetLayoutDimensions(dimensions)](#clay_setlayoutdimensions) if the window size of your application has changed.
```C
// Update internal layout dimensions
Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight });
```
5. **Optional** - Call [Clay_SetPointerState(pointerPosition, isPointerDown)](#clay_setpointerstate) if you want to use mouse interactions.
```C
// Update internal pointer position for handling mouseover / click / touch events
Clay_SetPointerState((Clay_Vector2) { mousePositionX, mousePositionY }, isMouseDown);
```
6. **Optional** - Call [Clay_UpdateScrollContainers(enableDragScrolling, scrollDelta, deltaTime)](#clay_updatescrollcontainers) if you want to use clay's built in scrolling containers.
```C
// Update internal pointer position for handling mouseover / click / touch events
Clay_UpdateScrollContainers(true, (Clay_Vector2) { mouseWheelX, mouseWheelY }, deltaTime);
```
7. Call [Clay_BeginLayout()](#clay_beginlayout) and declare your layout using the provided macros.
```C
const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};
const Clay_Color COLOR_RED = (Clay_Color) {168, 66, 28, 255};
const Clay_Color COLOR_ORANGE = (Clay_Color) {225, 138, 50, 255};
// Layout config is just a struct that can be declared statically, or inline
Clay_LayoutConfig sidebarItemLayout = (Clay_LayoutConfig) {
.sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(50) },
@ -103,48 +59,61 @@ void SidebarItemComponent() {
CLAY(CLAY_LAYOUT(sidebarItemLayout), CLAY_RECTANGLE({ .color = COLOR_ORANGE })) {}
}
// An example function to begin the "root" of your layout tree
Clay_RenderCommandArray CreateLayout() {
Clay_BeginLayout();
int main() {
// Note: malloc is only used here as an example, any allocator that provides
// a pointer to addressable memory of at least totalMemorySize will work
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
// An example of laying out a UI with a fixed width sidebar and flexible width main content
CLAY(CLAY_ID("OuterContainer"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }), CLAY_RECTANGLE({ .color = {250,250,255,255} })) {
CLAY(CLAY_ID("SideBar"),
CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }),
CLAY_RECTANGLE({ .color = COLOR_LIGHT })
) {
CLAY(CLAY_ID("ProfilePictureOuter"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }), CLAY_RECTANGLE({ .color = COLOR_RED })) {
CLAY(CLAY_ID("ProfilePicture"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {60, 60} })) {}
CLAY_TEXT(CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} }));
Clay_Initialize(arena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });
while(renderLoop()) { // Will be different for each renderer / environment
// Optional: Update internal layout dimensions to support resizing
Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight });
// Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling & debug tools
Clay_SetPointerState((Clay_Vector2) { mousePositionX, mousePositionY }, isMouseDown);
// Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling and debug tools
Clay_UpdateScrollContainers(true, (Clay_Vector2) { mouseWheelX, mouseWheelY }, deltaTime);
// All clay layouts are declared between Clay_BeginLayout and Clay_EndLayout
Clay_BeginLayout();
// An example of laying out a UI with a fixed width sidebar and flexible width main content
CLAY(CLAY_ID("OuterContainer"), CLAY_LAYOUT({ .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }), CLAY_RECTANGLE({ .color = {250,250,255,255} })) {
CLAY(CLAY_ID("SideBar"),
CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }),
CLAY_RECTANGLE({ .color = COLOR_LIGHT })
) {
CLAY(CLAY_ID("ProfilePictureOuter"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }), CLAY_RECTANGLE({ .color = COLOR_RED })) {
CLAY(CLAY_ID("ProfilePicture"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}), CLAY_IMAGE({ .imageData = &profilePicture, .sourceDimensions = {60, 60} })) {}
CLAY_TEXT(CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} }));
}
// Standard C code like loops etc work inside components
for (int i = 0; i < 5; i++) {
SidebarItemComponent();
}
CLAY(CLAY_ID("MainContent"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }}), CLAY_RECTANGLE({ .color = COLOR_LIGHT })) {}
}
// Standard C code like loops etc work inside components
for (int i = 0; i < 5; i++) {
SidebarItemComponent();
// All clay layouts are declared between Clay_BeginLayout and Clay_EndLayout
Clay_RenderCommandArray renderCommands = Clay_EndLayout();
// More comprehensive rendering examples can be found in the renderers/ directory
for (int i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = &renderCommands.internalArray[i];
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
DrawRectangle(
renderCommand->boundingBox,
renderCommand->config.rectangleElementConfig->color);
}
// ... Implement handling of other command types
}
}
}
CLAY(CLAY_ID("MainContent"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }}), CLAY_RECTANGLE({ .color = COLOR_LIGHT })) {}
}
// ...
});
```
8. Call [Clay_EndLayout()](#clay_endlayout) and process the resulting [Clay_RenderCommandArray](#clay_rendercommandarray) in your choice of renderer.
```C
Clay_RenderCommandArray renderCommands = Clay_EndLayout();
for (int i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = &renderCommands.internalArray[i];
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
DrawRectangle(
renderCommand->boundingBox,
renderCommand->config.rectangleElementConfig->color);
}
// ... Implement handling of other command types
}
}
```
@ -819,7 +788,7 @@ if (buttonIsHovered && leftMouseButtonPressed) {
### CLAY_IDI()
`Clay_ElementId CLAY_IDI(char *label, int index)`
`Clay_ElementId CLAY_IDI(char *label, int32_t index)`
An offset version of [CLAY_ID](#clay_id). Generates a [Clay_ElementId](#clay_elementid) string id from the provided `char *label`, combined with the `int index`. Used for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime.
@ -866,7 +835,7 @@ for (int i = 0; i < headerButtons.length; i++) {
### CLAY_IDI_LOCAL()
`Clay_ElementId CLAY_IDI_LOCAL(char *label, int index)`
`Clay_ElementId CLAY_IDI_LOCAL(char *label, int32_t index)`
An offset version of [CLAY_ID_LOCAL](#clay_local_id). Generates a [Clay_ElementId](#clay_elementid) string id from the provided `char *label`, combined with the `int index`. Used for generating ids for sequential elements (such as in a `for` loop) without having to construct dynamic strings at runtime.

1
bindings/cpp/README.md Normal file
View File

@ -0,0 +1 @@
https://github.com/TimothyHoytBSME/ClayMan

View File

@ -184,7 +184,8 @@ ElementConfigUnion :: struct #raw_union {
RenderCommand :: struct {
boundingBox: BoundingBox,
config: ElementConfigUnion,
text: String,
text: StringSlice,
zIndex: i32,
id: u32,
commandType: RenderCommandType,
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

1197
clay.h

File diff suppressed because it is too large Load Diff

View File

@ -33,9 +33,13 @@ static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextEl
return (Clay_Dimensions) { (float) width, (float) height };
}
static void Label(Clay_String text)
static void Label(const Clay_String text, const int cornerRadius)
{
CLAY(CLAY_LAYOUT({ .padding = {16, 8} }), CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE })) {
CLAY(CLAY_LAYOUT({ .padding = {8, 8} }),
CLAY_RECTANGLE({
.color = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE,
.cornerRadius = cornerRadius,
})) {
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID,
@ -44,6 +48,24 @@ static void Label(Clay_String text)
}
}
static void LabelBorder(const Clay_String text, const int cornerRadius, const int thickness)
{
CLAY(
CLAY_LAYOUT({
.padding = {16, 16, 8, 8 } }),
CLAY_BORDER_OUTSIDE_RADIUS(
thickness,
COLOR_BLUE,
cornerRadius)
){
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID,
.fontSize = 24,
}));
}
}
static Clay_RenderCommandArray Clay_CreateLayout()
{
Clay_BeginLayout();
@ -61,13 +83,21 @@ static Clay_RenderCommandArray Clay_CreateLayout()
.padding = { 10, 10 },
.layoutDirection = CLAY_TOP_TO_BOTTOM,
}),
CLAY_BORDER({
.left = { 20, COLOR_BLUE },
.right = { 20, COLOR_BLUE },
.bottom = { 20, COLOR_BLUE }
}),
CLAY_RECTANGLE({
.color = COLOR_LIGHT,
})
) {
Label(CLAY_STRING("Button 1"));
Label(CLAY_STRING("Button 2"));
Label(CLAY_STRING("Button 3"));
Label(CLAY_STRING("Rounded - Button 1"), 10);
Label(CLAY_STRING("Straight - Button 2") , 0);
Label(CLAY_STRING("Rounded+ - Button 3") , 20);
LabelBorder(CLAY_STRING("Border - Button 4"), 0, 5);
LabelBorder(CLAY_STRING("RoundedBorder - Button 5"), 10, 5);
LabelBorder(CLAY_STRING("RoundedBorder - Button 6"), 40, 15);
}
return Clay_EndLayout();
}

View File

@ -96,6 +96,11 @@
{name: 'length', type: 'uint32_t' },
{name: 'chars', type: 'uint32_t' },
]};
let stringSliceDefinition = { type: 'struct', members: [
{name: 'length', type: 'uint32_t' },
{name: 'chars', type: 'uint32_t' },
{name: 'baseChars', type: 'uint32_t' },
]};
let borderDefinition = { type: 'struct', members: [
{name: 'width', type: 'uint32_t'},
{name: 'color', ...colorDefinition},
@ -155,7 +160,8 @@
{ name: 'height', type: 'float' },
]},
{ name: 'config', type: 'uint32_t'},
{ name: 'text', ...stringDefinition },
{ name: 'text', ...stringSliceDefinition },
{ name: 'zIndex', type: 'int32_t' },
{ name: 'id', type: 'uint32_t' },
{ name: 'commandType', type: 'uint32_t', },
]

View File

@ -96,6 +96,11 @@
{name: 'length', type: 'uint32_t' },
{name: 'chars', type: 'uint32_t' },
]};
let stringSliceDefinition = { type: 'struct', members: [
{name: 'length', type: 'uint32_t' },
{name: 'chars', type: 'uint32_t' },
{name: 'baseChars', type: 'uint32_t' },
]};
let borderDefinition = { type: 'struct', members: [
{name: 'width', type: 'uint32_t'},
{name: 'color', ...colorDefinition},
@ -155,7 +160,8 @@
{ name: 'height', type: 'float' },
]},
{ name: 'config', type: 'uint32_t'},
{ name: 'text', ...stringDefinition },
{ name: 'text', ...stringSliceDefinition },
{ name: 'zIndex', type: 'int32_t' },
{ name: 'id', type: 'uint32_t' },
{ name: 'commandType', type: 'uint32_t', },
]

View File

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

View File

@ -25,7 +25,7 @@ target_include_directories(clay_examples_raylib_multi_context PUBLIC .)
target_link_libraries(clay_examples_raylib_multi_context PUBLIC raylib)
set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -Wno-error=missing-braces -DCLAY_DEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS_RELEASE "-O3")
add_custom_command(

View File

@ -10,7 +10,7 @@ void RenderHeaderButton(Clay_String text) {
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
CLAY_RECTANGLE({
.color = { 140, 140, 140, 255 },
.cornerRadius = 5
.cornerRadius = CLAY_CORNER_RADIUS(5)
})
) {
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
@ -79,7 +79,7 @@ typedef struct {
intptr_t memory;
} Arena;
Arena frameArena = {};
Arena frameArena = {0};
Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32_t* documentIndex) {
Clay_SetCurrentContext(context);
@ -104,13 +104,13 @@ Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32
);
Clay_Sizing layoutExpand = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_GROW()
.width = CLAY_SIZING_GROW(0),
.height = CLAY_SIZING_GROW(0)
};
Clay_RectangleElementConfig contentBackgroundConfig = {
.color = { 90, 90, 90, 255 },
.cornerRadius = 8
.cornerRadius = CLAY_CORNER_RADIUS(8)
};
Clay_BeginLayout();
@ -138,14 +138,14 @@ Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32
.childGap = 8,
.sizing = {
.width = CLAY_SIZING_FIXED(250),
.height = CLAY_SIZING_GROW()
.height = CLAY_SIZING_GROW(0)
}
})
) {
for (int i = 0; i < documents.length; i++) {
Document document = documents.documents[i];
Clay_LayoutConfig sidebarButtonLayout = {
.sizing = { .width = CLAY_SIZING_GROW() },
.sizing = { .width = CLAY_SIZING_GROW(0) },
.padding = CLAY_PADDING_ALL(16)
};
@ -154,7 +154,7 @@ Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32
CLAY_LAYOUT(sidebarButtonLayout),
CLAY_RECTANGLE({
.color = { 120, 120, 120, 255 },
.cornerRadius = 8,
.cornerRadius = CLAY_CORNER_RADIUS(8),
})
) {
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
@ -173,9 +173,9 @@ Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32
Clay_Hovered()
? CLAY_RECTANGLE({
.color = { 120, 120, 120, 120 },
.cornerRadius = 8
.cornerRadius = CLAY_CORNER_RADIUS(8)
})
: 0
: (void)0
) {
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,

View File

@ -27,7 +27,7 @@ target_link_libraries(clay_examples_raylib_sidebar_scrolling_container PUBLIC ra
if(MSVC)
set(CMAKE_C_FLAGS_DEBUG "/D CLAY_DEBUG")
else()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Werror -DCLAY_DEBUG -fsanitize=address")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
endif()

24
fuzz/fuzzing_target.c Normal file
View File

@ -0,0 +1,24 @@
#include "clay.h"
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < sizeof(Clay_String)) return 0;
Clay_String testString = { .length = size, .chars = (const char *)data };
Clay_Dimensions dimensions = MeasureText(&testString, NULL);
// Call other critical functions
Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(1024, (void*)data);
Clay_Initialize(arena, (Clay_Dimensions){1024, 768});
Clay_SetPointerState((Clay_Vector2){0, 0}, false);
Clay_BeginLayout();
Clay_EndLayout();
// Handle pointer state changes
Clay_SetPointerState((Clay_Vector2){1, 1}, true);
Clay_SetPointerState((Clay_Vector2){2, 2}, false);
return 0;
}

View File

@ -1,7 +0,0 @@
$TYPE$ *$NAME$_Add($NAME$ *array, $TYPE$ item) {
if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1];
}
return $DEFAULT_VALUE$;
}

View File

@ -1,5 +0,0 @@
void $NAME$_Add($NAME$ *array, $TYPE$ item) {
if (Clay__Array_AddCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item;
}
}

View File

@ -1,3 +0,0 @@
$NAME$ $NAME$_Allocate_Arena(int32_t capacity, Clay_Arena *arena) {
return CLAY__INIT($NAME$){.capacity = capacity, .length = 0, .internalArray = ($TYPE$ *)Clay__Array_Allocate_Arena(capacity, sizeof($TYPE$), CLAY__ALIGNMENT($TYPE$), arena)};
}

View File

@ -1,3 +0,0 @@
$NAME$ $NAME$_Allocate_Arena(int32_t capacity, Clay_Arena *arena) {
return CLAY__INIT($NAME$){.capacity = capacity, .length = 0, .internalArray = ($TYPE$ *)Clay__Array_Allocate_Arena(capacity, sizeof($TYPE$), CLAY__POINTER_ALIGNMENT, arena)};
}

View File

@ -1,6 +0,0 @@
CLAY__TYPEDEF($NAME$, struct
{
int32_t capacity;
int32_t length;
$TYPE$ *internalArray;
});

View File

@ -1,5 +0,0 @@
CLAY__TYPEDEF($NAME$Slice, struct
{
int32_t length;
$TYPE$ *internalArray;
});

View File

@ -1,3 +0,0 @@
$TYPE$ *$NAME$_Get($NAME$ *array, int32_t index) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : $DEFAULT_VALUE$;
}

View File

@ -1,3 +0,0 @@
$TYPE$ *$NAME$Slice_Get($NAME$Slice *slice, int32_t index) {
return Clay__Array_RangeCheck(index, slice->length) ? &slice->internalArray[index] : $DEFAULT_VALUE$;
}

View File

@ -1,3 +0,0 @@
$TYPE$ $NAME$_Get($NAME$ *array, int32_t index) {
return Clay__Array_RangeCheck(index, array->length) ? array->internalArray[index] : $DEFAULT_VALUE$;
}

View File

@ -1,9 +0,0 @@
$TYPE$ $NAME$_RemoveSwapback($NAME$ *array, int32_t index) {
if (Clay__Array_RangeCheck(index, array->length)) {
array->length--;
$TYPE$ removed = array->internalArray[index];
array->internalArray[index] = array->internalArray[array->length];
return removed;
}
return $DEFAULT_VALUE$;
}

View File

@ -1,6 +0,0 @@
void $NAME$_Set($NAME$ *array, int32_t index, $TYPE$ value) {
if (Clay__Array_RangeCheck(index, array->capacity)) {
array->internalArray[index] = value;
array->length = index < array->length ? array->length : index + 1;
}
}

View File

@ -1,69 +0,0 @@
const fs = require('fs');
const path = require('path');
let files = ['../clay.h'];
let templates = ['./'];
function readCTemplatesRecursive(directory) {
fs.readdirSync(directory).forEach(template => {
const absolute = path.join(directory, template);
if (fs.statSync(absolute).isDirectory()) return readCTemplatesRecursive(absolute);
else if (template.endsWith('template.c')) {
return templates.push(absolute);
}
});
}
readCTemplatesRecursive(__dirname);
for (const file of files) {
const contents = fs.readFileSync(file, 'utf8');
const lines = contents.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('// __GENERATED__ template')) {
const [comment, generated, templateOpen, templateNames, ...args] = line.split(" ");
let matchingEndingLine = -1;
for (let j = i + 1; j < lines.length; j++) {
if (lines[j].startsWith('// __GENERATED__ template')) {
matchingEndingLine = j;
break;
}
}
if (matchingEndingLine !== -1) {
i++;
lines.splice(i, matchingEndingLine - (i));
lines.splice(i, 0, ['#pragma region generated']);
i++;
for (const templateName of templateNames.split(',')) {
var matchingTemplate = templates.find(t => t.endsWith(`${templateName}.template.c`));
if (matchingTemplate) {
let templateContents = fs.readFileSync(matchingTemplate, 'utf8');
for (const arg of args) {
[argName, argValue] = arg.split('=');
templateContents = templateContents.replaceAll(`\$${argName}\$`, argValue);
}
let remainingTokens = templateContents.split('$');
if (remainingTokens.length > 1) {
console.log(`Error at ${file}:${i}: Template is missing parameter ${remainingTokens[1]}`)
process.exit();
} else {
templateContents = templateContents.split('\n');
lines.splice(i, 0, ...templateContents);
i += templateContents.length;
}
} else {
console.log(`Error at ${file}:${i + 1}: no template with name ${templateName}.template.c was found.`);
process.exit();
}
}
lines.splice(i, 0, ['#pragma endregion']);
i++;
} else {
console.log(`Error at ${file}:${i + 1}: template was opened and not closed again.`);
process.exit();
}
}
}
fs.writeFileSync(file, lines.join('\n'));
}

View File

@ -58,7 +58,7 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
}
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextElementConfig *config = renderCommand->config.textElementConfig;
Clay_String text = renderCommand->text;
Clay_StringSlice text = renderCommand->text;
char *cloned = (char *)calloc(text.length + 1, 1);
memcpy(cloned, text.chars, text.length);
TTF_Font* font = fonts[config->fontId].font;
@ -117,27 +117,32 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
if (config->left.width > 0) {
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->left.color));
SDL_RenderFillRectF(renderer, &(SDL_FRect){ boundingBox.x, boundingBox.y + config->cornerRadius.topLeft, config->left.width, boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft });
SDL_FRect rect = { boundingBox.x, boundingBox.y + config->cornerRadius.topLeft, config->left.width, boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft };
SDL_RenderFillRectF(renderer, &rect);
}
if (config->right.width > 0) {
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->right.color));
SDL_RenderFillRectF(renderer, &(SDL_FRect){ boundingBox.x + boundingBox.width - config->right.width, boundingBox.y + config->cornerRadius.topRight, config->right.width, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight });
SDL_FRect rect = { boundingBox.x + boundingBox.width - config->right.width, boundingBox.y + config->cornerRadius.topRight, config->right.width, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight };
SDL_RenderFillRectF(renderer, &rect);
}
if (config->right.width > 0) {
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->right.color));
SDL_RenderFillRectF(renderer, &(SDL_FRect){ boundingBox.x + boundingBox.width - config->right.width, boundingBox.y + config->cornerRadius.topRight, config->right.width, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight });
SDL_FRect rect = { boundingBox.x + boundingBox.width - config->right.width, boundingBox.y + config->cornerRadius.topRight, config->right.width, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight };
SDL_RenderFillRectF(renderer, &rect);
}
if (config->top.width > 0) {
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->right.color));
SDL_RenderFillRectF(renderer, &(SDL_FRect){ boundingBox.x + config->cornerRadius.topLeft, boundingBox.y, boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight, config->top.width });
SDL_FRect rect = { boundingBox.x + config->cornerRadius.topLeft, boundingBox.y, boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight, config->top.width };
SDL_RenderFillRectF(renderer, &rect);
}
if (config->bottom.width > 0) {
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->bottom.color));
SDL_RenderFillRectF(renderer, &(SDL_FRect){ boundingBox.x + config->cornerRadius.bottomLeft, boundingBox.y + boundingBox.height - config->bottom.width, boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight, config->bottom.width });
SDL_FRect rect = { boundingBox.x + config->cornerRadius.bottomLeft, boundingBox.y + boundingBox.height - config->bottom.width, boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight, config->bottom.width };
SDL_RenderFillRectF(renderer, &rect);
}
break;

View File

@ -1,6 +1,4 @@
Please note, the SDL3 renderer is not 100% feature complete. It is currently missing:
- Rounded rectangle corners
- Borders
- Images
- Scroll / Scissor handling

View File

@ -6,25 +6,160 @@
/* This needs to be global because the "MeasureText" callback doesn't have a
* user data parameter */
static TTF_Font *gFonts[1];
/* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with
* no AA or low resolution might make it appear as jagged curves) */
static int NUM_CIRCLE_SEGMENTS = 16;
//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) {
const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 };
int indexCount = 0, vertexCount = 0;
const float minRadius = SDL_min(rect.w, rect.h) / 2.0f;
const float clampedRadius = SDL_min(cornerRadius, minRadius);
const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int) clampedRadius * 0.5f);
int totalVertices = 4 + (4 * (numCircleSegments * 2)) + 2*4;
int totalIndices = 6 + (4 * (numCircleSegments * 3)) + 6*4;
SDL_Vertex vertices[totalVertices];
int indices[totalIndices];
//define center rectangle
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + clampedRadius}, 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){ {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //2 center BR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //3 center BL
indices[indexCount++] = 0;
indices[indexCount++] = 1;
indices[indexCount++] = 3;
indices[indexCount++] = 1;
indices[indexCount++] = 2;
indices[indexCount++] = 3;
//define rounded corners as triangle fans
const float step = (SDL_PI_F/2) / numCircleSegments;
for (int i = 0; i < numCircleSegments; i++) {
const float angle1 = (float)i * step;
const float angle2 = ((float)i + 1.0f) * step;
for (int j = 0; j < 4; j++) { // Iterate over four corners
float cx, cy, signX, signY;
switch (j) {
case 0: cx = rect.x + clampedRadius; cy = rect.y + clampedRadius; signX = -1; signY = -1; break; // Top-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;
}
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle1) * clampedRadius * signX, cy + SDL_sinf(angle1) * clampedRadius * signY}, color, {0, 0} };
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 - 1;
}
}
//Define edge rectangles
// Top edge
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y}, color, {0, 0} }; //TL
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0} }; //TR
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
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1} }; //BR
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1} }; //BL
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
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //LB
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + clampedRadius}, color, {0, 0} }; //LT
indices[indexCount++] = 3;
indices[indexCount++] = vertexCount - 2; //LB
indices[indexCount++] = vertexCount - 1; //LT
indices[indexCount++] = 0;
indices[indexCount++] = 3;
indices[indexCount++] = vertexCount - 1; //LT
// Render everything
SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);
}
static void SDL_RenderArc(SDL_Renderer *renderer, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) {
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
const float radStart = startAngle * (SDL_PI_F / 180.0f);
const float radEnd = endAngle * (SDL_PI_F / 180.0f);
const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int)(radius * 1.5f)); //increase circle segments for larger circles, 1.5 is arbitrary.
const float angleStep = (radEnd - radStart) / (float)numCircleSegments;
const float thicknessStep = 0.4f; //arbitrary value to avoid overlapping lines. Changing THICKNESS_STEP or numCircleSegments might cause artifacts.
for (float t = thicknessStep; t < thickness - thicknessStep; t += thicknessStep) {
SDL_FPoint points[numCircleSegments + 1];
const float clampedRadius = SDL_max(radius - t, 1.0f);
for (int i = 0; i <= numCircleSegments; i++) {
const float angle = radStart + i * angleStep;
points[i] = (SDL_FPoint){
SDL_roundf(center.x + SDL_cosf(angle) * clampedRadius),
SDL_roundf(center.y + SDL_sinf(angle) * clampedRadius) };
}
SDL_RenderLines(renderer, points, numCircleSegments + 1);
}
}
static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArray *rcommands)
{
for (size_t i = 0; i < rcommands->length; i++) {
Clay_RenderCommand *rcmd = Clay_RenderCommandArray_Get(rcommands, i);
Clay_BoundingBox bounding_box = rcmd->boundingBox;
const Clay_BoundingBox bounding_box = rcmd->boundingBox;
const SDL_FRect rect = { bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height };
switch (rcmd->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleElementConfig *config = rcmd->config.rectangleElementConfig;
Clay_Color color = config->color;
const Clay_RectangleElementConfig *config = rcmd->config.rectangleElementConfig;
const Clay_Color color = config->color;
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderFillRect(renderer, &rect);
if (config->cornerRadius.topLeft > 0) {
SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius.topLeft, color);
} else {
SDL_RenderFillRect(renderer, &rect);
}
} break;
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextElementConfig *config = rcmd->config.textElementConfig;
Clay_String *text = &rcmd->text;
SDL_Color color = { config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a };
const Clay_TextElementConfig *config = rcmd->config.textElementConfig;
const Clay_StringSlice *text = &rcmd->text;
const SDL_Color color = { config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a };
TTF_Font *font = gFonts[config->fontId];
SDL_Surface *surface = TTF_RenderText_Blended(font, text->chars, text->length, color);
@ -34,6 +169,72 @@ static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArr
SDL_DestroySurface(surface);
SDL_DestroyTexture(texture);
} break;
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
const Clay_BorderElementConfig *config = rcmd->config.borderElementConfig;
const float minRadius = SDL_min(rect.w, rect.h) / 2.0f;
const Clay_CornerRadius clampedRadii = {
.topLeft = SDL_min(config->cornerRadius.topLeft, minRadius),
.topRight = SDL_min(config->cornerRadius.topRight, minRadius),
.bottomLeft = SDL_min(config->cornerRadius.bottomLeft, minRadius),
.bottomRight = SDL_min(config->cornerRadius.bottomRight, minRadius)
};
//edges
SDL_SetRenderDrawColor(renderer, config->left.color.r, config->left.color.g, config->left.color.b, config->left.color.a);
if (config->left.width > 0) {
const float starting_y = rect.y + clampedRadii.topLeft;
const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft;
SDL_FRect line = { rect.x, starting_y, config->left.width, length };
SDL_RenderFillRect(renderer, &line);
}
if (config->right.width > 0) {
const float starting_x = rect.x + rect.w - (float)config->right.width;
const float starting_y = rect.y + clampedRadii.topRight;
const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight;
SDL_FRect line = { starting_x, starting_y, config->right.width, length };
SDL_RenderFillRect(renderer, &line);
}
if (config->top.width > 0) {
const float starting_x = rect.x + clampedRadii.topLeft;
const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight;
SDL_FRect line = { starting_x, rect.y, length, config->top.width };
SDL_RenderFillRect(renderer, &line);
}
if (config->bottom.width > 0) {
const float starting_x = rect.x + clampedRadii.bottomLeft;
const float starting_y = rect.y + rect.h - (float)config->bottom.width;
const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight;
SDL_FRect line = { starting_x, starting_y, length, config->bottom.width };
SDL_SetRenderDrawColor(renderer, config->bottom.color.r, config->bottom.color.g, config->bottom.color.b, config->bottom.color.a);
SDL_RenderFillRect(renderer, &line);
}
//corners
if (config->cornerRadius.topLeft > 0) {
const float centerX = rect.x + clampedRadii.topLeft -1;
const float centerY = rect.y + clampedRadii.topLeft;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft,
180.0f, 270.0f, config->top.width, config->top.color);
}
if (config->cornerRadius.topRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.topRight -1;
const float centerY = rect.y + clampedRadii.topRight;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,
270.0f, 360.0f, config->top.width, config->top.color);
}
if (config->cornerRadius.bottomLeft > 0) {
const float centerX = rect.x + clampedRadii.bottomLeft -1;
const float centerY = rect.y + rect.h - clampedRadii.bottomLeft -1;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft,
90.0f, 180.0f, config->bottom.width, config->bottom.color);
}
if (config->cornerRadius.bottomRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.bottomRight -1; //TODO: why need to -1 in all calculations???
const float centerY = rect.y + rect.h - clampedRadii.bottomRight -1;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,
0.0f, 90.0f, config->bottom.width, config->bottom.color);
}
} break;
default:
SDL_Log("Unknown render command type: %d", rcmd->commandType);
}

View File

@ -35,7 +35,7 @@ typedef struct
CustomLayoutElementType type;
union {
CustomLayoutElement_3DModel model;
};
} customData;
} CustomLayoutElement;
// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
@ -138,7 +138,7 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands)
{
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
Clay_String text = renderCommand->text;
Clay_StringSlice text = renderCommand->text;
char *cloned = (char *)malloc(text.length + 1);
memcpy(cloned, text.chars, text.length);
cloned[text.length] = '\0';
@ -216,7 +216,7 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands)
float scaleValue = CLAY__MIN(CLAY__MIN(1, 768 / rootBox.height) * CLAY__MAX(1, rootBox.width / 1024), 1.5f);
Ray positionRay = GetScreenToWorldPointWithZDistance((Vector2) { renderCommand->boundingBox.x + renderCommand->boundingBox.width / 2, renderCommand->boundingBox.y + (renderCommand->boundingBox.height / 2) + 20 }, Raylib_camera, (int)roundf(rootBox.width), (int)roundf(rootBox.height), 140);
BeginMode3D(Raylib_camera);
DrawModel(customElement->model.model, positionRay.position, customElement->model.scale * scaleValue, WHITE); // Draw 3d model with texture
DrawModel(customElement->customData.model.model, positionRay.position, customElement->customData.model.scale * scaleValue, WHITE); // Draw 3d model with texture
EndMode3D();
break;
}