some layout work

rewrite2
Alessandro Mauri 4 months ago
parent 59acce1150
commit d6358944ac
  1. 5
      cache.c
  2. 318
      ugui.c
  3. 38
      ugui.h
  4. 12
      vectree.c

@ -26,7 +26,7 @@
// hash table (id -> cache index) // hash table (id -> cache index)
typedef struct { typedef struct {
UgId id; UgId id;
uint32_t index; uint32_t index;
} IdElem; } IdElem;
@ -204,9 +204,8 @@ UgElem *ug_cache_insert_at(UgElemCache *cache, const UgElem *g, uint32_t index)
} }
// Insert an element in the cache // Insert an element in the cache
UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, uint32_t *index) UgElem *ug_cache_insert_new(UgElemCache *cache, const UgElem *g, uint32_t *index)
{ {
*index = ug_cache_get_free_spot(cache); *index = ug_cache_get_free_spot(cache);
return ug_cache_insert_at(cache, g, *index); return ug_cache_insert_at(cache, g, *index);
} }

318
ugui.c

@ -17,6 +17,13 @@
#define DIV_FILL \ #define DIV_FILL \
(UgRect) { .x = 0, .y = 0, .w = 0, .h = 0 } (UgRect) { .x = 0, .y = 0, .w = 0, .h = 0 }
#define MARK() \
do { \
printf("lmao\n"); \
} while (0)
#define FTEST(e, f) ((e)->flags & (f))
#define STACK_STEP 10 #define STACK_STEP 10
#define MAX_ELEMS 128 #define MAX_ELEMS 128
#define MAX_CMDS 256 #define MAX_CMDS 256
@ -162,9 +169,12 @@ int ug_div_begin(UgCtx *ctx, const char *label, UgRect div);
int ug_div_end(UgCtx *ctx); int ug_div_end(UgCtx *ctx);
int ug_input_window_size(UgCtx *ctx, int width, int height); int ug_input_window_size(UgCtx *ctx, int width, int height);
UgId djb2(const char *str); UgId djb2(const char *str);
UgId FNV_1a(const char *str);
int ug_button(UgCtx *ctx, const char *label, UgRect size); int ug_button(UgCtx *ctx, const char *label, UgRect size);
UgRect position_element(UgCtx *ctx, UgElem *parent, UgRect rect);
int main(void) int main(void)
{ {
UgCtx ctx; UgCtx ctx;
@ -190,28 +200,43 @@ int main(void)
// main div, fill the whole window // main div, fill the whole window
ug_div_begin(&ctx, "main", DIV_FILL); ug_div_begin(&ctx, "main", DIV_FILL);
{
ug_button(&ctx, "button0", (UgRect) {.w = 100, .h = 16}); ug_button(
&ctx,
"batt342353453452wrwea",
(UgRect) {.y = 100, .x = 130, .w = 100, .h = 16}
);
ug_button(
&ctx,
"arieasd3ree2234tast",
(UgRect) {.x = 10, .w = 30, .h = 30}
);
ug_button(
&ctx,
"bughsdfsfdstton2",
(UgRect) {.x = 10, .w = 30, .h = 30}
);
}
ug_div_end(&ctx); ug_div_end(&ctx);
ug_frame_end(&ctx); ug_frame_end(&ctx);
// drawing // drawing
BeginDrawing(); BeginDrawing();
ClearBackground(BLACK); // ClearBackground(BLACK);
printf("----- Draw Begin -----\n");
Color c; Color c;
for (UgCmd cmd; ug_fifo_dequeue(&ctx.fifo, &cmd) >= 0;) { for (UgCmd cmd; ug_fifo_dequeue(&ctx.fifo, &cmd) >= 0;) {
switch (cmd.type) { switch (cmd.type) {
case CMD_RECT: case CMD_RECT:
// printf( printf(
// "rect x=%d y=%d w=%d h=%d\n", "draw rect x=%d y=%d w=%d h=%d\n",
// cmd.rect.rect.x, cmd.rect.rect.x,
// cmd.rect.rect.y, cmd.rect.rect.y,
// cmd.rect.rect.w, cmd.rect.rect.w,
// cmd.rect.rect.h cmd.rect.rect.h
//); );
c = (Color) { c = (Color) {
.r = cmd.rect.color.r, .r = cmd.rect.color.r,
.g = cmd.rect.color.g, .g = cmd.rect.color.g,
@ -231,6 +256,7 @@ int main(void)
break; break;
} }
} }
printf("----- Draw End -----\n\n");
EndDrawing(); EndDrawing();
@ -243,6 +269,24 @@ int main(void)
return 0; return 0;
} }
// search the element of the corresponding id in the cache, if no element is found
// insert a new one of that id. Return the pointer to the element
int search_or_insert(UgCtx *ctx, UgElem **elem, UgId id)
{
int is_new = 0;
uint32_t cache_idx;
UgElem *c_elem = ug_cache_search(&ctx->cache, id);
if (c_elem == NULL) {
UgElem tmp = {.id = id};
c_elem = ug_cache_insert_new(&ctx->cache, &tmp, &cache_idx);
is_new = 1;
}
*elem = c_elem;
return is_new;
}
int ug_init(UgCtx *ctx) int ug_init(UgCtx *ctx)
{ {
if (ctx == NULL) { if (ctx == NULL) {
@ -276,9 +320,14 @@ void print_tree(UgCtx *ctx)
{ {
printf("ctx->tree: ["); printf("ctx->tree: [");
for (int c = -1, x; (x = ug_tree_level_order_it(&ctx->tree, 0, &c)) != -1;) { for (int c = -1, x; (x = ug_tree_level_order_it(&ctx->tree, 0, &c)) != -1;) {
printf("%d, ", x); printf(
"[%d:%d,%.4lx], ",
x,
ug_tree_parentof(&ctx->tree, x),
ug_tree_get(&ctx->tree, x) & 0xffff
);
} }
printf("-1]\n"); printf("[-1]]\n");
} }
int ug_frame_begin(UgCtx *ctx) int ug_frame_begin(UgCtx *ctx)
@ -315,8 +364,12 @@ int ug_frame_begin(UgCtx *ctx)
} }
// FIXME: check errors // FIXME: check errors
uint32_t cache_idx; UgElem *c_elem;
ug_cache_insert(&ctx->cache, &root, &cache_idx); int is_new = search_or_insert(ctx, &c_elem, root.id);
if (is_new || FTEST(&root, ELEM_UPDATED)) {
*c_elem = root;
}
ctx->div_using = ug_tree_add(&ctx->tree, root.id, 0); ctx->div_using = ug_tree_add(&ctx->tree, root.id, 0);
if (ctx->div_using < 0) { if (ctx->div_using < 0) {
@ -329,6 +382,8 @@ int ug_frame_begin(UgCtx *ctx)
// The root element does not push anything to the stack // The root element does not push anything to the stack
// TODO: add a background color taken from a theme or config // TODO: add a background color taken from a theme or config
printf("##### Frame Begin #####\n");
return 0; return 0;
} }
@ -344,6 +399,7 @@ int ug_frame_end(UgCtx *ctx)
// 2. clear input fields // 2. clear input fields
ctx->input.flags = 0; ctx->input.flags = 0;
printf("##### Frame End #####\n\n");
return 0; return 0;
} }
@ -367,6 +423,20 @@ int ug_input_window_size(UgCtx *ctx, int width, int height)
return 0; return 0;
} }
UgId FNV_1a(const char *str)
{
const uint64_t fnv_off = 0xcbf29ce484222325;
const uint64_t fnv_prime = 0x100000001b3;
uint64_t hash = fnv_off;
for (uint32_t c; (c = str[0]) != 0; str++) {
hash ^= c;
hash *= fnv_prime;
}
return hash;
}
UgId djb2(const char *str) UgId djb2(const char *str)
{ {
uint64_t hash = 5381; uint64_t hash = 5381;
@ -385,98 +455,55 @@ int ug_div_begin(UgCtx *ctx, const char *label, UgRect div)
return -1; return -1;
} }
UgId id = djb2(label); UgId id = FNV_1a(label);
// TODO: do layouting if the element is new or the parent has updated UgElem *c_elem;
int is_new_elem = 0; int is_new = search_or_insert(ctx, &c_elem, id);
// add the element if it does not exist // FIXME: why save the id in the tree and not something more direct like
UgElem *c_elem = ug_cache_search(&ctx->cache, id); // the element pointer or the index into the cache vector?
if (c_elem == NULL) { int div_node = ug_tree_add(&ctx->tree, id, ctx->div_using);
UgElem elem = {0}; if (div_node < 0) {
uint32_t c_idx; // do something
c_elem = ug_cache_insert(&ctx->cache, &elem, &c_idx); printf("Error adding to tree\n");
is_new_elem = 1;
} }
// take a reference to the parent // take a reference to the parent
// FIXME: if the tree held pointers to the elements then no more // FIXME: if the tree held pointers to the elements then no more
// redundant cache search // redundant cache search
UgId parent_id = ctx->tree.vector[ctx->div_using]; UgId parent_id = ug_tree_get(&ctx->tree, ctx->div_using);
UgElem *parent = ug_cache_search(&ctx->cache, parent_id); UgElem *parent = ug_cache_search(&ctx->cache, parent_id);
if (parent == NULL) { if (parent == NULL) {
// Error, did you forget to do frame_begin()? // Error, did you forget to do frame_begin()?
return -1; return -1;
} }
// FIXME: why save the id in the tree and not something more direct like print_tree(ctx);
// 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;
// print_tree(ctx); // Use the current div
ctx->div_using = div_node;
// layouting // 1. Fill the element fields
// TODO: do layout // this resets the flags
if (is_new_elem || parent->flags & ELEM_UPDATED) { c_elem->type = ETYPE_DIV;
c_elem->id = id; c_elem->flags = 0;
c_elem->type = ETYPE_DIV;
// 1. Select the right origin offset
switch (parent->div.layout) {
case DIV_LAYOUT_ROW:
c_elem->rect = (UgRect) {
.x = parent->div.origin_r.x + div.x,
.y = parent->div.origin_r.y + div.y,
};
break;
case DIV_LAYOUT_COLUMN:
c_elem->rect = (UgRect) {
.x = parent->div.origin_c.x + div.x,
.y = parent->div.origin_c.y + div.y,
};
break;
case DIV_LAYOUT_FLOATING:
c_elem->rect = (UgRect) {
.x = div.x,
.y = div.y,
};
break;
default:
// Error
break;
}
// 2. Calculate width & height // do layout and update flags only if the element was updated
// TODO: what about negative values? if (is_new || FTEST(parent, ELEM_UPDATED)) {
c_elem->rect.w = div.w != 0 ? div.w : parent->rect.w; // 2. layout the element
c_elem->rect.h = div.h != 0 ? div.h : parent->rect.h; c_elem->rect = position_element(ctx, parent, div);
// 3. Mark the element as updated // 3. Mark the element as updated
c_elem->flags |= ELEM_UPDATED; c_elem->flags |= ELEM_UPDATED;
// 4. Set div information // 4. Fill the div fields
c_elem->div.layout = parent->div.layout; c_elem->div.layout = parent->div.layout;
c_elem->div.origin_c = (UgPoint) { c_elem->div.origin_c = (UgPoint) {
.x = c_elem->rect.x, .x = c_elem->rect.x,
.y = c_elem->rect.y, .y = c_elem->rect.y,
}; };
c_elem->div.origin_r = c_elem->div.origin_c;
c_elem->div.color_bg = RGBA(0xff0000ff); c_elem->div.color_bg = RGBA(0xff0000ff);
c_elem->div.origin_r = c_elem->div.origin_c;
// 4. Update the origins of the parent
parent->div.origin_r = (UgPoint) {
.x = c_elem->rect.x + c_elem->rect.w,
.y = c_elem->rect.y,
};
parent->div.origin_c = (UgPoint) {
.x = c_elem->rect.x,
.y = c_elem->rect.y + c_elem->rect.h,
};
} else { } else {
// TODO: check active // TODO: check active
// TODO: check resizeable // TODO: check resizeable
@ -504,84 +531,99 @@ int ug_div_end(UgCtx *ctx)
return 0; return 0;
} }
// position the rectangle inside the parent according to the layout
UgRect position_element(UgCtx *ctx, UgElem *parent, UgRect rect)
{
UgRect elem_rect = {0};
UgPoint origin = {0};
// 1. Select the right origin
switch (parent->div.layout) {
case DIV_LAYOUT_ROW:
origin = parent->div.origin_r;
break;
case DIV_LAYOUT_COLUMN:
origin = parent->div.origin_c;
break;
case DIV_LAYOUT_FLOATING: // none
default:
// Error
break;
}
// 2. Position the rect
elem_rect.x = origin.x + rect.x;
elem_rect.y = origin.y + rect.y;
// 3. Calculate width & height
// TODO: what about negative values?
// FIXME: account for origin offset!!
elem_rect.w = rect.w > 0 ? rect.w : parent->rect.w;
elem_rect.h = rect.h > 0 ? rect.h : parent->rect.h;
// 4. Update the origins of the parent
parent->div.origin_r = (UgPoint) {
.x = elem_rect.x + elem_rect.w,
.y = elem_rect.y,
};
parent->div.origin_c = (UgPoint) {
.x = elem_rect.x,
.y = elem_rect.y + elem_rect.h,
};
/*
printf(
"positioning rect: %lx {%d %d %d %d}(%d %d %d %d) -> {%d %d %d
%d}\n", parent->id, rect.x, rect.y, rect.w, rect.h, parent->rect.x,
parent->rect.y,
parent->rect.w,
parent->rect.h,
elem_rect.x,
elem_rect.y,
elem_rect.w,
elem_rect.h
);
*/
return elem_rect;
}
int ug_button(UgCtx *ctx, const char *label, UgRect size) int ug_button(UgCtx *ctx, const char *label, UgRect size)
{ {
if (ctx == NULL || label == NULL) { if (ctx == NULL || label == NULL) {
return -1; return -1;
} }
UgId id = djb2(label); UgId id = FNV_1a(label);
// TODO: do layouting if the element is new or the parent has updated // TODO: do layouting if the element is new or the parent has updated
int is_new_elem = 0; UgElem *c_elem;
int is_new = search_or_insert(ctx, &c_elem, id);
// add the element if it does not exist // add it to the tree
UgElem *c_elem = ug_cache_search(&ctx->cache, id); ug_tree_add(&ctx->tree, id, ctx->div_using);
if (c_elem == NULL) { print_tree(ctx);
UgElem elem = {0};
uint32_t c_idx;
c_elem = ug_cache_insert(&ctx->cache, &elem, &c_idx);
is_new_elem = 1;
}
// take a reference to the parent // take a reference to the parent
// FIXME: if the tree held pointers to the elements then no more // FIXME: if the tree held pointers to the elements then no more
// redundant cache search // redundant cache search
UgId parent_id = ctx->tree.vector[ctx->div_using]; UgId parent_id = ug_tree_get(&ctx->tree, ctx->div_using);
UgElem *parent = ug_cache_search(&ctx->cache, parent_id); UgElem *parent = ug_cache_search(&ctx->cache, parent_id);
if (parent == NULL) { if (parent == NULL) {
// Error, did you forget to do frame_begin()? // Error, did you forget to do frame_begin()?
return -1; return -1;
} }
// if the element is new or the parent was updated then redo layout // 1. Fill the element fields
if (is_new_elem || parent->flags & ELEM_UPDATED) { // this resets the flags
c_elem->id = id; c_elem->type = ETYPE_BUTTON;
c_elem->type = ETYPE_BUTTON; c_elem->flags = 0;
// 1. Select the right origin offset
switch (parent->div.layout) {
case DIV_LAYOUT_ROW:
c_elem->rect = (UgRect) {
.x = parent->div.origin_r.x + size.x,
.y = parent->div.origin_r.y + size.y,
};
break;
case DIV_LAYOUT_COLUMN:
c_elem->rect = (UgRect) {
.x = parent->div.origin_c.x + size.x,
.y = parent->div.origin_c.y + size.y,
};
break;
case DIV_LAYOUT_FLOATING:
c_elem->rect = (UgRect) {
.x = size.x,
.y = size.y,
};
break;
default:
// Error
break;
}
// 2. Calculate width & height
// TODO: what about negative values?
c_elem->rect.w = size.w != 0 ? size.w : parent->rect.w;
c_elem->rect.h = size.h != 0 ? size.h : parent->rect.h;
/// Not a div, so not needed // if the element is new or the parent was updated then redo layout
// 3. Mark the element as updated if (is_new || parent->flags & ELEM_UPDATED) {
// 4. Set div information // 2. Layout
c_elem->rect = position_element(ctx, parent, size);
// 4. Update the origins of the parent // 3. TODO: Fill the button specific fields
parent->div.origin_r = (UgPoint) {
.x = c_elem->rect.x + c_elem->rect.w,
.y = c_elem->rect.y,
};
parent->div.origin_c = (UgPoint) {
.x = c_elem->rect.x,
.y = c_elem->rect.y + c_elem->rect.h,
};
} else { } else {
// TODO: Check for interactions // TODO: Check for interactions
} }

@ -28,23 +28,20 @@ enum UgElemFlags {
}; };
typedef struct { typedef struct {
UgId id; UgId id;
uint32_t flags; uint32_t flags;
UgRect rect; UgRect rect;
UgElemType type;
union {
uint32_t type_int;
UgElemType type;
};
// type-specific fields // type-specific fields
union { union {
struct { struct UgDiv {
enum { enum {
DIV_LAYOUT_ROW = 0, DIV_LAYOUT_ROW = 0,
DIV_LAYOUT_COLUMN, DIV_LAYOUT_COLUMN,
DIV_LAYOUT_FLOATING, DIV_LAYOUT_FLOATING,
} layout; } layout;
UgPoint origin_r, origin_c; UgPoint origin_r, origin_c;
UgColor color_bg; UgColor color_bg;
} div; // Div } div; // Div
@ -70,22 +67,23 @@ typedef struct {
typedef struct _UgCtx UgCtx; typedef struct _UgCtx UgCtx;
// tree implementation // 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_pack(UgTree *tree);
int ug_tree_resize(UgTree *tree, unsigned int newsize); int ug_tree_resize(UgTree *tree, unsigned int newsize);
int ug_tree_add(UgTree *tree, UgId elem, int parent); int ug_tree_add(UgTree *tree, UgId elem, int parent);
int ug_tree_prune(UgTree *tree, int ref); int ug_tree_prune(UgTree *tree, int ref);
int ug_tree_subtree_size(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_children_it(UgTree *tree, int parent, int *cursor);
int ug_tree_level_order_it(UgTree *tree, int ref, 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_parentof(UgTree *tree, int node);
int ug_tree_destroy(UgTree *tree); int ug_tree_destroy(UgTree *tree);
UgId ug_tree_get(UgTree *tree, int node);
// cache implementation // cache implementation
UgElemCache ug_cache_init(void); UgElemCache ug_cache_init(void);
void ug_cache_free(UgElemCache *cache); void ug_cache_free(UgElemCache *cache);
UgElem *ug_cache_search(UgElemCache *cache, UgId id); UgElem *ug_cache_search(UgElemCache *cache, UgId id);
UgElem *ug_cache_insert(UgElemCache *cache, const UgElem *g, uint32_t *index); UgElem *ug_cache_insert_new(UgElemCache *cache, const UgElem *g, uint32_t *index);
int ug_init(UgCtx *ctx); int ug_init(UgCtx *ctx);
int ug_destroy(UgCtx *ctx); int ug_destroy(UgCtx *ctx);

@ -128,7 +128,7 @@ int ug_tree_resize(UgTree *tree, unsigned int newsize)
return -1; return -1;
} }
int *neworrefs = realloc(tree->ordered_refs, (newsize + 1)* sizeof(int)); int *neworrefs = realloc(tree->ordered_refs, (newsize + 1) * sizeof(int));
if (neworrefs == NULL) { if (neworrefs == NULL) {
return -1; return -1;
} }
@ -319,7 +319,7 @@ int ug_tree_level_order_it(UgTree *tree, int ref, int *cursor)
} while (IS_VALID_REF(tree, ref)); } while (IS_VALID_REF(tree, ref));
// This line is why tree->ordered_refs has to be size+1 // This line is why tree->ordered_refs has to be size+1
queue[off+1] = -1; queue[off + 1] = -1;
} }
// PRINT_ARR(queue, tree->size); // PRINT_ARR(queue, tree->size);
@ -345,3 +345,11 @@ int ug_tree_parentof(UgTree *tree, int node)
} }
return tree->refs[node]; return tree->refs[node];
} }
UgId ug_tree_get(UgTree *tree, int node)
{
if (tree == NULL || !IS_VALID_REF(tree, node)) {
return 0;
}
return tree->vector[node];
}

Loading…
Cancel
Save