diff --git a/Makefile b/Makefile index 8f92d90..7eb7617 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,10 @@ all: ugui raylib/src/libraylib.a: raylib/src/Makefile cd raylib/src; $(MAKE) PLATFORM=PLATFORM_DESKTOP -ugui: ugui.o vectree.o raylib/src/libraylib.a +ugui: ugui.o vectree.o cache.o raylib/src/libraylib.a ugui.o: ugui.c ugui.h vectree.o: vectree.c ugui.h + +cache.o: cache.c ugui.h diff --git a/cache.c b/cache.c index 593bebc..1ed0d35 100644 --- a/cache.c +++ b/cache.c @@ -24,13 +24,13 @@ #define HASH_MAXSIZE 4096 -// hash table (id -> index) +// hash table (id -> cache index) typedef struct { - uint32_t id; + UgId id; uint32_t index; } IdElem; -typedef struct { +typedef struct _IdTable { uint32_t items, size, exp; IdElem bucket[]; } IdTable; @@ -62,7 +62,7 @@ void table_destroy(IdTable *ht) } // Find and return the element by pointer -IdElem *table_search(IdTable *ht, uint32_t id) +IdElem *table_search(IdTable *ht, UgId id) { if (!ht) { return NULL; @@ -93,7 +93,7 @@ IdElem *table_insert(IdTable *ht, IdElem entry) return r; } -IdElem *table_remove(IdTable *ht, uint32_t id) +IdElem *table_remove(IdTable *ht, UgId id) { if (!ht) { return NULL; @@ -119,13 +119,6 @@ IdElem *table_remove(IdTable *ht, uint32_t id) } \ } while (0) -typedef struct { - IdTable *table; - UgElem *array; - uint64_t *present, *used; - int cycles; -} UgElemCache; - /* FIXME: check for allocation errors */ UgElemCache ug_cache_init(void) { @@ -148,7 +141,7 @@ void ug_cache_free(UgElemCache *cache) } } -const UgElem *ug_cache_search(UgElemCache *cache, uint32_t id) +UgElem *ug_cache_search(UgElemCache *cache, UgId id) { if (!cache) { return NULL; @@ -190,7 +183,7 @@ int ug_cache_get_free_spot(UgElemCache *cache) return 0; } -const UgElem *ug_cache_insert_at(UgElemCache *cache, const UgElem *g, uint32_t index) +UgElem *ug_cache_insert_at(UgElemCache *cache, const UgElem *g, uint32_t index) { if (!cache) { return NULL; @@ -211,7 +204,7 @@ const UgElem *ug_cache_insert_at(UgElemCache *cache, const UgElem *g, uint32_t i } // Insert an element in the cache -const UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, int32_t *index) +UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, uint32_t *index) { *index = ug_cache_get_free_spot(cache); return ug_cache_insert_at(cache, g, *index); diff --git a/ugui.c b/ugui.c index a22e092..8ee7c41 100644 --- a/ugui.c +++ b/ugui.c @@ -1,3 +1,5 @@ +#define _DEFAULT_SOURCE + #include #include #include @@ -6,30 +8,185 @@ #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); - InitWindow(800, 450, "Ugui Test"); + 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()) { - ug_begin_frame(&ctx); + + // 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); - DrawText( - "Congrats! You created your first window!", - 190, - 200, - 20, - LIGHTGRAY - ); + + 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(); - ug_end_frame(&ctx); + WaitTime(0.2); } CloseWindow(); @@ -38,13 +195,21 @@ int main(void) 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, 10); + 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; } @@ -56,6 +221,106 @@ int ug_destroy(UgCtx *ctx) } 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; } diff --git a/ugui.h b/ugui.h index 08dabb6..ecb2fe8 100644 --- a/ugui.h +++ b/ugui.h @@ -15,16 +15,15 @@ typedef struct { uint8_t r, g, b, a; } UgColor; +typedef uint64_t UgId; + typedef enum { ETYPE_NONE = 0, - ETYPE_BUTTON, - ETYPE_TEXT, - ETYPE_SCROLL, - ETYPE_SLIDER, + ETYPE_DIV, } UgElemType; typedef struct { - uint64_t id; + UgId id; UgRect rec; union { uint32_t type_int; @@ -37,27 +36,42 @@ typedef struct { typedef struct { int size, elements; - uint32_t *vector; // vector of element ids + UgId *vector; // vector of element ids int *refs, *ordered_refs; } UgTree; typedef struct { - UgTree tree; -} UgCtx; + struct _IdTable *table; + UgElem *array; + uint64_t *present, *used; + int cycles; +} UgElemCache; + +typedef struct _UgCtx UgCtx; // tree implementation -int ug_tree_init(UgTree *tree, unsigned int size); +int ug_tree_init(UgTree *tree, unsigned int size); int ug_tree_pack(UgTree *tree); int ug_tree_resize(UgTree *tree, unsigned int newsize); -int ug_tree_add(UgTree *tree, uint32_t elem, int parent); +int ug_tree_add(UgTree *tree, UgId elem, int parent); int ug_tree_prune(UgTree *tree, int ref); int ug_tree_subtree_size(UgTree *tree, int ref); int ug_tree_children_it(UgTree *tree, int parent, int *cursor); int ug_tree_level_order_it(UgTree *tree, int ref, int *cursor); +int ug_tree_parentof(UgTree *tree, int node); int ug_tree_destroy(UgTree *tree); +// cache implementation +UgElemCache ug_cache_init(void); +void ug_cache_free(UgElemCache *cache); +UgElem *ug_cache_search(UgElemCache *cache, UgId id); +UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, uint32_t *index); + + int ug_init(UgCtx *ctx); int ug_destroy(UgCtx *ctx); +int ug_frame_begin(UgCtx *ctx); +int ug_frame_end(UgCtx *ctx); #endif // _UGUI_H diff --git a/vectree.c b/vectree.c index e9e80f8..8c3af8d 100644 --- a/vectree.c +++ b/vectree.c @@ -12,7 +12,7 @@ int ug_tree_init(UgTree *tree, unsigned int size) return -1; } - tree->vector = malloc(sizeof(UgElem) * size); + tree->vector = malloc(sizeof(UgId) * size); if (tree->vector == NULL) { return -1; } @@ -37,7 +37,7 @@ int ug_tree_init(UgTree *tree, unsigned int size) } // fill vector with zeroes - memset(tree->vector, 0, size * sizeof(UgElem)); + memset(tree->vector, 0, size * sizeof(UgId)); tree->size = size; tree->elements = 0; @@ -118,7 +118,7 @@ int ug_tree_resize(UgTree *tree, unsigned int newsize) return -1; } - uint32_t *newvec = realloc(tree->vector, newsize * sizeof(uint32_t)); + UgId *newvec = realloc(tree->vector, newsize * sizeof(UgId)); if (newvec == NULL) { return -1; } @@ -149,7 +149,7 @@ int ug_tree_resize(UgTree *tree, unsigned int newsize) } // add an element to the tree, return it's ref -int ug_tree_add(UgTree *tree, uint32_t elem, int parent) +int ug_tree_add(UgTree *tree, UgId elem, int parent) { if (tree == NULL) { return -1; @@ -333,3 +333,12 @@ int ug_tree_level_order_it(UgTree *tree, int ref, int *cursor) return -1; } + +int ug_tree_parentof(UgTree *tree, int node) +{ + if (tree == NULL || !IS_VALID_REF(tree, node) || + !REF_IS_PRESENT(tree, node)) { + return -1; + } + return tree->refs[node]; +}