#define _DEFAULT_SOURCE #include #include #include #include #include "raylib.h" #include "ugui.h" typedef struct _UgCmd { enum { CMD_RECT = 0, } type; union { struct { int32_t x, y, w, h; uint8_t color[4]; // rgba, [0] = r } rect; }; } UgCmd; typedef struct _UgStack { int size, count; UgCmd *stack; } UgStack; int ug_stack_init(UgStack *stack, uint32_t size); int ug_stack_free(UgStack *stack); int ug_stack_push(UgStack *stack, UgCmd *cmd); int ug_stack_pop(UgStack *stack, UgCmd *cmd); int ug_stack_init(UgStack *stack, uint32_t size) { if (stack == NULL) { return -1; } stack->size = size; stack->count = 0; stack->stack = calloc(size, sizeof(UgCmd)); if (stack->stack == NULL) { return -1; } return 0; } int ug_stack_free(UgStack *stack) { if (stack != NULL && stack->stack != NULL) { free(stack->stack); stack->size = 0; stack->count = 0; } return 0; } #define STACK_STEP 10 int ug_stack_push(UgStack *stack, UgCmd *cmd) { if (stack == NULL || cmd == NULL) { return -1; } if (stack->count >= stack->size) { // UgCmd *tmp = reallocarray(stack->stack, stack->size + // STACK_STEP, sizeof(UgCmd)); if (tmp == NULL) { return -1; // } // stack->stack = tmp; // stack->size += STACK_STEP; return -1; } stack->stack[stack->count++] = *cmd; return 0; } int ug_stack_pop(UgStack *stack, UgCmd *cmd) { if (stack == NULL || cmd == NULL) { return -1; } if (stack->count <= 0) { // UgCmd *tmp = reallocarray(stack->stack, stack->size + // STACK_STEP, sizeof(UgCmd)); if (tmp == NULL) { return -1; // } // stack->stack = tmp; // stack->size += STACK_STEP; return -1; } *cmd = stack->stack[--stack->count]; return 0; } struct _UgCtx { enum { row = 0, column, floating, } layout; UgTree tree; UgElemCache cache; UgStack stack; struct { int width, height; } size; int div_using; // tree node indicating the current active div }; int ug_div_begin(UgCtx *ctx, const char *name, UgRect div); int ug_div_end(UgCtx *ctx); int ug_input_window_size(UgCtx *ctx, int width, int height); UgId djb2(const char *str); int main(void) { UgCtx ctx; ug_init(&ctx); int width = 800, height = 450; SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(width, height, "Ugui Test"); ug_input_window_size(&ctx, width, height); // Main loop while (!WindowShouldClose()) { // UI handling ug_frame_begin(&ctx); if (IsWindowResized()) { width = GetScreenWidth(); height = GetScreenHeight(); ug_input_window_size(&ctx, width, height); } // main div, fill the whole window #define DIV_FILL (UgRect) {.x = 0, .y = 0, .w = -1, .h = -1} ug_div_begin(&ctx, "main", DIV_FILL); ug_div_end(&ctx); ug_frame_end(&ctx); // drawing BeginDrawing(); ClearBackground(BLACK); Rectangle r; Color c; for (UgCmd cmd; ug_stack_pop(&ctx.stack, &cmd) >= 0;) { switch (cmd.type) { case CMD_RECT: printf( "rect x=%d y=%d w=%d h=%d\n", cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h ); c = (Color) { .r = cmd.rect.color[0], .g = cmd.rect.color[1], .b = cmd.rect.color[2], .a = cmd.rect.color[3], }; DrawRectangle( cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h, c ); break; default: printf("Unknown cmd type: %d\n", cmd.type); break; } } EndDrawing(); WaitTime(0.2); } CloseWindow(); ug_destroy(&ctx); return 0; } #define MAX_ELEMS 128 #define MAX_CMDS 256 int ug_init(UgCtx *ctx) { if (ctx == NULL) { return -1; } ug_tree_init(&ctx->tree, MAX_ELEMS); ug_stack_init(&ctx->stack, MAX_CMDS); ctx->cache = ug_cache_init(); ctx->layout = row; ctx->div_using = 0; return 0; } int ug_destroy(UgCtx *ctx) { if (ctx == NULL) { return -1; } ug_tree_destroy(&ctx->tree); ug_cache_free(&ctx->cache); return 0; } int ug_frame_begin(UgCtx *ctx) { return 0; } int ug_frame_end(UgCtx *ctx) { return 0; } int ug_input_window_size(UgCtx *ctx, int width, int height) { if (ctx == NULL) { return -1; } if (width <= 0 || height <= 0) { return -1; } if (width >= 0) { ctx->size.width = width; } if (height >= 0) { ctx->size.height = height; } return 0; } UgId djb2(const char *str) { uint64_t hash = 5381; uint32_t c; while ((c = (str++)[0]) != 0) { hash = ((hash << 5) + hash) + c; } /* hash * 33 + c */ return hash; } int ug_div_begin(UgCtx *ctx, const char *name, UgRect div) { if (ctx == NULL || name == NULL) { return -1; } UgId id = djb2(name); // add the element if it does not exist UgElem *c_elem = ug_cache_search(&ctx->cache, id); if (c_elem == NULL) { UgElem elem = { .id = id, .type = ETYPE_DIV, .rec = (UgRect) { .x = div.x, .y = div.y, .w = div.w >= 0 ? div.w : ctx->size.width, .h = div.h >= 0 ? div.h : ctx->size.height, }}; uint32_t c_idx; c_elem = ug_cache_insert(&ctx->cache, &elem, &c_idx); } // FIXME: why save the id in the tree and not something more direct like // the element pointer or the index into the cache vector? int div_node = ug_tree_add(&ctx->tree, c_elem->id, ctx->div_using); if (div_node < 0) { // do something printf("Error adding to tree\n"); } ctx->div_using = div_node; // printf( // "elem.rect: x=%d x=%d w=%d h=%d\n", // c_elem->rec.x, // c_elem->rec.y, // c_elem->rec.w, // c_elem->rec.h // ); UgCmd cmd = { .type = CMD_RECT, .rect = { .x = c_elem->rec.x, .y = c_elem->rec.y, .w = c_elem->rec.w, .h = c_elem->rec.h, .color = {0xff, 0x00, 0x00, 0xff}, // red }, }; ug_stack_push(&ctx->stack, &cmd); return 0; } int ug_div_end(UgCtx *ctx) { // the div_using returns to the parent of the current one ctx->div_using = ug_tree_parentof(&ctx->tree, ctx->div_using); return 0; }