lodestone.life

Clay UI Hello World

Draft post. I was looking for something that is a simple intro to Clay UI, but I gave up on it. TBD

Basic SDL Hello world:

// Initial SDL set up
#define SDL_MAIN_HANDLED

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

int main(int argc, char* argv[]) {
	int screenWidth = 600;
	int screenHeight = 600;
	// SDL Setup
	SDL_Window* window = NULL;
	SDL_Renderer* renderer = NULL;
    bool success = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
    if(!success) {
        SDL_Log("SDL_Init error: %s", SDL_GetError());
        return 1;
    }
    window = SDL_CreateWindow("SDL Hello World",screenWidth,screenHeight,SDL_WINDOW_RESIZABLE);
    if(window == NULL) {
        SDL_Log("SDL_CreateWindow error: %s", SDL_GetError());
        return 1;
    }
    renderer = SDL_CreateRenderer(window, NULL);
    if(renderer == NULL) {
        SDL_Log("SDL_CreateRenderer error: %s", SDL_GetError());
        return 1;
    }

	// SDL Render loop
	bool running = true;
	SDL_Event event;
	while(running){
		/* Event handling */
        while(SDL_PollEvent(&event)){
            switch(event.type) {
                case SDL_EVENT_QUIT:
                    running = false;
                    break;
                default:
                    break;
            }
        }
		/* Rendering */
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 100);
        SDL_RenderClear(renderer);	
		SDL_RenderPresent(renderer);
	}
    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderer);
    SDL_Quit();
    return 0;
}

Adding Clay to the SDL setup.

// Initial SDL set up
#define SDL_MAIN_HANDLED
// alternatively use SDL_MAIN_HANDLED
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

#define CLAY_IMPLEMENTATION
#include <clay.H>

// Example measure text function
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
    };
}

// Example clay error handler
void  HandleClayErrors(Clay_ErrorData errorData) {
    SDL_Log("%s", errorData.errorText.chars);
}

// Example clay SDL Renderer
void SDL_Clay_Render(Clay_RenderCommandArray* renderCommands, SDL_Renderer* renderer) {
    for(int i = 0; i < renderCommands->length; i++) {
        Clay_RenderCommand* rcmd = Clay_RenderCommandArray_Get(renderCommands, i);
        const Clay_BoundingBox bounding_box = rcmd->boundingBox;
        SDL_FRect rect = { (int)bounding_box.x, (int)bounding_box.y, (int)bounding_box.width, (int)bounding_box.height};
        switch(rcmd->commandType) {
            case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:
            Clay_RectangleRenderData *config = &(rcmd->renderData.rectangle);
            SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
            SDL_SetRenderDrawColor(renderer, config->backgroundColor.r, config->backgroundColor.b, config->backgroundColor.g, config->backgroundColor.a);
            if(config->cornerRadius.topLeft > 0)
            {
                SDL_RenderFillRect(renderer, &rect);
            } else {
                SDL_RenderFillRect(renderer, &rect);
            }
            break;
            case CLAY_RENDER_COMMAND_TYPE_TEXT:
                break;
            case CLAY_RENDER_COMMAND_TYPE_BORDER:
                break;
            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END:
                break;
            case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START:
                break;
            case CLAY_RENDER_COMMAND_TYPE_IMAGE:
                break;
            default:
            SDL_Log("Unknkown render command: %d", rcmd->commandType );
        }
    }
}

int main(int argc, char* argv[]) {
	int screenWidth = 600;
	int screenHeight = 600;
	// SDL Setup
	SDL_Window* window = NULL;
	SDL_Renderer* renderer = NULL;
    bool success = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
    if(!success) {
        SDL_Log("SDL_Init error: %s", SDL_GetError());
        return 1;
    }
    window = SDL_CreateWindow("SDL Hello World",screenWidth,screenHeight,SDL_WINDOW_RESIZABLE);
    if(window == NULL) {
        SDL_Log("SDL_CreateWindow error: %s", SDL_GetError());
        return 1;
    }
    renderer = SDL_CreateRenderer(window, NULL);
    if(renderer == NULL) {
        SDL_Log("SDL_CreateRenderer error: %s", SDL_GetError());
        return 1;
    }

    // Clay Setup
    uint64_t totalMemorySize = Clay_MinMemorySize();
    Clay_Arena arena = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
    Clay_Initialize(arena, (Clay_Dimensions) {screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors });
    // Set layout dimension in render loop if resizing 
    Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight });

    // Clay element declaration
    /*
        <OutterContainer>
            <InnerContainer>
                <MainContent />
            </InnerContainer> 
        </OutterContainer>
    */
    Clay_BeginLayout();
    CLAY({ .id = CLAY_ID("OuterContainer"), .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
        CLAY({
            .id = CLAY_ID("InnerContainer"),
            .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(300), .height = CLAY_SIZING_GROW(1) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 },
            .backgroundColor = (Clay_Color) {224,215,210,255}
        }) {
            CLAY({ .id = CLAY_ID("MainContent"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = (Clay_Color) {225,138,50,255} }) {}
        }
    }
    Clay_RenderCommandArray renderCommands = Clay_EndLayout();
	// SDL Render loop
	bool running = true;
	SDL_Event event;
	while(running){
        while(SDL_PollEvent(&event)){
            switch(event.type) {
                case SDL_EVENT_QUIT:
                    running = false;
                    break;
                default:
                    break;
            }
        }
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 100);
        SDL_RenderClear(renderer);	
        // Custom render function 
        SDL_Clay_Render(&renderCommands, renderer);
		SDL_RenderPresent(renderer);
	}
    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderer);
    SDL_Quit();
    return 0;
}