Compare commits

...

8 Commits

Author SHA1 Message Date
caleb-snow-cbm
44c7e0c834
Merge 94a3d57236 into f824ddfd25 2025-03-10 18:44:27 -05:00
Nic Barker
f824ddfd25
Merge pull request #320 from shakkar23/patch-1
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
[Renderers/SDL2] Enable live resizing of layout during window resize in SDL2
2025-03-11 10:29:34 +13:00
Nic Barker
82bb48a235
Merge pull request #300 from joshuahhh/patch-1
[Documentation] Update README.md: it's gotten bigger
2025-03-11 09:50:28 +13:00
Nic Barker
c06e01c1af
Merge pull request #319 from emoon/pass-declaration-by-pointer
Support passing declaration by pointer as well
2025-03-11 09:39:40 +13:00
Jesus Coca
e856136a8e
add resizing while the window is being resized 2025-03-08 17:37:02 -08:00
Daniel Collin
33b8e76903 Support passing declaration by pointer as well 2025-03-08 15:17:36 +01:00
Caleb Snow
94a3d57236 [Core] Add right click support
Adds the Clay_SetPointerStateEx function to optionally capture
right click info.
2025-03-04 08:48:18 -05:00
Joshua Horowitz
adc31f82e8
Update README.md: it's gotten bigger 2025-03-03 21:38:25 -08:00
3 changed files with 103 additions and 29 deletions

View File

@ -4,7 +4,7 @@
### Major Features
- Microsecond layout performance
- Flex-box like layout model for complex, responsive layouts including text wrapping, scrolling containers and aspect ratio scaling
- Single ~2k LOC **clay.h** file with **zero** dependencies (including no standard library)
- Single ~4k LOC **clay.h** file with **zero** dependencies (including no standard library)
- Wasm support: compile with clang to a 15kb uncompressed **.wasm** file for use in the browser
- Static arena based memory use with no malloc / free, and low total memory overhead (e.g. ~3.5mb for 8192 layout elements).
- React-like nested declarative syntax

83
clay.h
View File

@ -705,7 +705,8 @@ typedef struct {
// CLAY_POINTER_DATA_PRESSED - The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.
// CLAY_POINTER_DATA_RELEASED_THIS_FRAME - The left mouse button click or touch was released this frame.
// CLAY_POINTER_DATA_RELEASED - The left mouse button click or touch is not currently down / was released at some point in the past.
Clay_PointerDataInteractionState state;
Clay_PointerDataInteractionState state : 4;
Clay_PointerDataInteractionState right_state : 4;
} Clay_PointerData;
typedef struct {
@ -798,6 +799,8 @@ CLAY_DLL_EXPORT Clay_Arena Clay_CreateArenaWithCapacityAndMemory(size_t capacity
// Sets the state of the "pointer" (i.e. the mouse or touch) in Clay's internal data. Used for detecting and responding to mouse events in the debug view,
// as well as for Clay_Hovered() and scroll element handling.
CLAY_DLL_EXPORT void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown);
// Same as `Clay_SetPointerState` but includes right click info
CLAY_DLL_EXPORT void Clay_SetPointerStateEx(Clay_Vector2 position, bool pointerDown, bool rightPointerDown);
// Initialize Clay's internal arena and setup required data before layout can begin. Only needs to be called once.
// - arena can be created using Clay_CreateArenaWithCapacityAndMemory()
// - layoutDimensions are the initial bounding dimensions of the layout (i.e. the screen width and height for a full screen layout)
@ -880,6 +883,7 @@ CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
CLAY_DLL_EXPORT void Clay__OpenElement(void);
CLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
CLAY_DLL_EXPORT void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *config);
CLAY_DLL_EXPORT void Clay__CloseElement(void);
CLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed);
CLAY_DLL_EXPORT void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
@ -1865,58 +1869,58 @@ Clay_ElementId Clay__AttachId(Clay_ElementId elementId) {
return elementId;
}
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
Clay_Context* context = Clay_GetCurrentContext();
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
openLayoutElement->layoutConfig = Clay__StoreLayoutConfig(declaration.layout);
if ((declaration.layout.sizing.width.type == CLAY__SIZING_TYPE_PERCENT && declaration.layout.sizing.width.size.percent > 1) || (declaration.layout.sizing.height.type == CLAY__SIZING_TYPE_PERCENT && declaration.layout.sizing.height.size.percent > 1)) {
openLayoutElement->layoutConfig = Clay__StoreLayoutConfig(declaration->layout);
if ((declaration->layout.sizing.width.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.width.size.percent > 1) || (declaration->layout.sizing.height.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.height.size.percent > 1)) {
context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
.errorType = CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,
.errorText = CLAY_STRING("An element was configured with CLAY_SIZING_PERCENT, but the provided percentage value was over 1.0. Clay expects a value between 0 and 1, i.e. 20% is 0.2."),
.userData = context->errorHandler.userData });
}
Clay_ElementId openLayoutElementId = declaration.id;
Clay_ElementId openLayoutElementId = declaration->id;
openLayoutElement->elementConfigs.internalArray = &context->elementConfigs.internalArray[context->elementConfigs.length];
Clay_SharedElementConfig *sharedConfig = NULL;
if (declaration.backgroundColor.a > 0) {
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .backgroundColor = declaration.backgroundColor });
if (declaration->backgroundColor.a > 0) {
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .backgroundColor = declaration->backgroundColor });
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
}
if (!Clay__MemCmp((char *)(&declaration.cornerRadius), (char *)(&Clay__CornerRadius_DEFAULT), sizeof(Clay_CornerRadius))) {
if (!Clay__MemCmp((char *)(&declaration->cornerRadius), (char *)(&Clay__CornerRadius_DEFAULT), sizeof(Clay_CornerRadius))) {
if (sharedConfig) {
sharedConfig->cornerRadius = declaration.cornerRadius;
sharedConfig->cornerRadius = declaration->cornerRadius;
} else {
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .cornerRadius = declaration.cornerRadius });
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .cornerRadius = declaration->cornerRadius });
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
}
}
if (declaration.userData != 0) {
if (declaration->userData != 0) {
if (sharedConfig) {
sharedConfig->userData = declaration.userData;
sharedConfig->userData = declaration->userData;
} else {
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .userData = declaration.userData });
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .userData = declaration->userData });
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
}
}
if (declaration.image.imageData) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration.image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);
if (declaration->image.imageData) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration->image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);
Clay__int32_tArray_Add(&context->imageElementPointers, context->layoutElements.length - 1);
}
if (declaration.floating.attachTo != CLAY_ATTACH_TO_NONE) {
Clay_FloatingElementConfig floatingConfig = declaration.floating;
if (declaration->floating.attachTo != CLAY_ATTACH_TO_NONE) {
Clay_FloatingElementConfig floatingConfig = declaration->floating;
// This looks dodgy but because of the auto generated root element the depth of the tree will always be at least 2 here
Clay_LayoutElement *hierarchicalParent = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));
if (hierarchicalParent) {
uint32_t clipElementId = 0;
if (declaration.floating.attachTo == CLAY_ATTACH_TO_PARENT) {
if (declaration->floating.attachTo == CLAY_ATTACH_TO_PARENT) {
// Attach to the element's direct hierarchical parent
floatingConfig.parentId = hierarchicalParent->id;
if (context->openClipElementStack.length > 0) {
clipElementId = Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1);
}
} else if (declaration.floating.attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {
} else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {
Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig.parentId);
if (!parentItem) {
context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
@ -1926,7 +1930,7 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
} else {
clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, 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;
}
if (!openLayoutElementId.id) {
@ -1941,8 +1945,8 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .floatingElementConfig = Clay__StoreFloatingElementConfig(floatingConfig) }, CLAY__ELEMENT_CONFIG_TYPE_FLOATING);
}
}
if (declaration.custom.customData) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration.custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);
if (declaration->custom.customData) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration->custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);
}
if (openLayoutElementId.id != 0) {
@ -1951,8 +1955,8 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
openLayoutElementId = Clay__GenerateIdForAnonymousElement(openLayoutElement);
}
if (declaration.scroll.horizontal | declaration.scroll.vertical) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .scrollElementConfig = Clay__StoreScrollElementConfig(declaration.scroll) }, CLAY__ELEMENT_CONFIG_TYPE_SCROLL);
if (declaration->scroll.horizontal | declaration->scroll.vertical) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .scrollElementConfig = Clay__StoreScrollElementConfig(declaration->scroll) }, CLAY__ELEMENT_CONFIG_TYPE_SCROLL);
Clay__int32_tArray_Add(&context->openClipElementStack, (int)openLayoutElement->id);
// Retrieve or create cached data to track scroll position across frames
Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL;
@ -1971,11 +1975,15 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
scrollOffset->scrollPosition = Clay__QueryScrollOffset(scrollOffset->elementId, context->queryScrollOffsetUserData);
}
}
if (!Clay__MemCmp((char *)(&declaration.border.width), (char *)(&Clay__BorderWidth_DEFAULT), sizeof(Clay_BorderWidth))) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(declaration.border) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER);
if (!Clay__MemCmp((char *)(&declaration->border.width), (char *)(&Clay__BorderWidth_DEFAULT), sizeof(Clay_BorderWidth))) {
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(declaration->border) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER);
}
}
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
Clay__ConfigureOpenElementPtr(&declaration);
}
void Clay__InitializeEphemeralMemory(Clay_Context* context) {
int32_t maxElementCount = context->maxElementCount;
// Ephemeral Memory - reset every frame
@ -3659,8 +3667,8 @@ void Clay_SetLayoutDimensions(Clay_Dimensions dimensions) {
Clay_GetCurrentContext()->layoutDimensions = dimensions;
}
CLAY_WASM_EXPORT("Clay_SetPointerState")
void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
CLAY_WASM_EXPORT("Clay_SetPointerStateEx")
void Clay_SetPointerStateEx(Clay_Vector2 position, bool isPointerDown, bool isRightPointerDown) {
Clay_Context* context = Clay_GetCurrentContext();
if (context->booleanWarnings.maxElementsExceeded) {
return;
@ -3732,6 +3740,25 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
context->pointerInfo.state = CLAY_POINTER_DATA_RELEASED_THIS_FRAME;
}
}
if (isRightPointerDown) {
if (context->pointerInfo.right_state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
context->pointerInfo.right_state = CLAY_POINTER_DATA_PRESSED;
} else if (context->pointerInfo.right_state != CLAY_POINTER_DATA_PRESSED) {
context->pointerInfo.right_state = CLAY_POINTER_DATA_PRESSED_THIS_FRAME;
}
} else {
if (context->pointerInfo.right_state == CLAY_POINTER_DATA_RELEASED_THIS_FRAME) {
context->pointerInfo.right_state = CLAY_POINTER_DATA_RELEASED;
} else if (context->pointerInfo.right_state != CLAY_POINTER_DATA_RELEASED) {
context->pointerInfo.right_state = CLAY_POINTER_DATA_RELEASED_THIS_FRAME;
}
}
}
CLAY_WASM_EXPORT("Clay_SetPointerState")
void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown)
{
Clay_SetPointerStateEx(position, isPointerDown, false);
}
CLAY_WASM_EXPORT("Clay_Initialize")

View File

@ -17,6 +17,41 @@ void HandleClayErrors(Clay_ErrorData errorData) {
printf("%s", errorData.errorText.chars);
}
struct ResizeRenderData_ {
SDL_Window* window;
int windowWidth;
int windowHeight;
ClayVideoDemo_Data demoData;
SDL_Renderer* renderer;
SDL2_Font* fonts;
};
typedef struct ResizeRenderData_ ResizeRenderData;
int resizeRendering(void* userData, SDL_Event* event) {
ResizeRenderData *actualData = userData;
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_EXPOSED) {
SDL_Window* window = actualData->window;
int windowWidth = actualData->windowWidth;
int windowHeight = actualData->windowHeight;
ClayVideoDemo_Data demoData = actualData->demoData;
SDL_Renderer* renderer = actualData->renderer;
SDL2_Font* fonts = actualData->fonts;
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
Clay_SetLayoutDimensions((Clay_Dimensions) { (float)windowWidth, (float)windowHeight });
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
Clay_SDL2_Render(renderer, renderCommands, fonts);
SDL_RenderPresent(renderer);
}
return 0;
}
int main(int argc, char *argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Error: could not initialize SDL: %s\n", SDL_GetError());
@ -73,6 +108,18 @@ int main(int argc, char *argv[]) {
double deltaTime = 0;
ClayVideoDemo_Data demoData = ClayVideoDemo_Initialize();
ResizeRenderData userData = {
window, // SDL_Window*
windowWidth, // int
windowHeight, // int
demoData, // CustomShit
renderer, // SDL_Renderer*
fonts // SDL2_Font[1]
};
// add an event watcher that will render the screen while youre dragging the window to different sizes
SDL_AddEventWatch(resizeRendering, &userData);
while (true) {
Clay_Vector2 scrollDelta = {};
SDL_Event event;