diff --git a/README.md b/README.md index 7090aac..6151ef7 100644 --- a/README.md +++ b/README.md @@ -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" -``` -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,107 +39,85 @@ 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) }, -}; - // Re-useable components are just normal functions 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(); - - // 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 })) {} - } - // ... -}); -``` - -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]; +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)); - switch (renderCommand->commandType) { - case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { - DrawRectangle( - renderCommand->boundingBox, - renderCommand->config.rectangleElementConfig->color); + 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); + + // 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) }, + }; + + // 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 })) {} + } + + // 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 + } } - // ... Implement handling of other command types } } ``` - + The above example, rendered correctly will look something like the following: ![Clay Example](https://github.com/user-attachments/assets/1928c6d4-ada9-4a4c-a3d1-44fe9b23b3bd)