From 8032c7340bdce9f63827e89aeb424f93f9160fe0 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Tue, 7 Mar 2023 19:06:04 +0100 Subject: [PATCH] [ui, styling] revisiting layout/sizing --- examples/ui_style_test/main.c | 290 +++++++++++++++++--- src/ui.c | 490 ++++++++++++++++++++++++++-------- src/ui.h | 181 ++++++++++--- 3 files changed, 772 insertions(+), 189 deletions(-) diff --git a/examples/ui_style_test/main.c b/examples/ui_style_test/main.c index 90ea73b..a82bb26 100644 --- a/examples/ui_style_test/main.c +++ b/examples/ui_style_test/main.c @@ -31,6 +31,14 @@ void debug_print_rule(ui_style_rule* rule) { switch(selector->kind) { + case UI_SEL_ANY: + printf("any: "); + break; + + case UI_SEL_OWNER: + printf("owner: "); + break; + case UI_SEL_TEXT: printf("text='%.*s': ", (int)selector->text.len, selector->text.ptr); break; @@ -39,6 +47,26 @@ void debug_print_rule(ui_style_rule* rule) printf("tag=0x%llx: ", selector->tag.hash); break; + case UI_SEL_STATUS: + { + if(selector->status & UI_HOVER) + { + printf("hover: "); + } + if(selector->status & UI_ACTIVE) + { + printf("active: "); + } + if(selector->status & UI_DRAGGING) + { + printf("dragging: "); + } + } break; + + case UI_SEL_KEY: + printf("key=0x%llx: ", selector->key.hash); + break; + default: printf("unknown: "); break; @@ -46,6 +74,30 @@ void debug_print_rule(ui_style_rule* rule) } printf("=> font size = %f\n", rule->style->fontSize); } +void debug_print_size(ui_box* box, ui_axis axis, int indent) +{ + debug_print_indent(indent); + printf("size %s: ", axis == UI_AXIS_X ? "x" : "y"); + switch(box->targetStyle->size.s[axis].kind) + { + case UI_SIZE_TEXT: + printf("text\n"); + break; + + case UI_SIZE_CHILDREN: + printf("children\n"); + break; + + case UI_SIZE_PARENT: + printf("parent\n"); + break; + + case UI_SIZE_PIXELS: + printf("pixels\n"); + break; + } + +} void debug_print_styles(ui_box* box, int indent) { @@ -56,6 +108,9 @@ void debug_print_styles(ui_box* box, int indent) debug_print_indent(indent); printf("font size: %f\n", box->targetStyle->fontSize); + debug_print_size(box, UI_AXIS_X, indent); + debug_print_size(box, UI_AXIS_Y, indent); + if(!ListEmpty(&box->beforeRules)) { debug_print_indent(indent); @@ -90,6 +145,38 @@ void debug_print_styles(ui_box* box, int indent) } } +mg_font create_font() +{ + //NOTE(martin): create font + str8 fontPath = mp_app_get_resource_path(mem_scratch(), "../resources/OpenSansLatinSubset.ttf"); + char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath); + + FILE* fontFile = fopen(fontPathCString, "r"); + if(!fontFile) + { + LOG_ERROR("Could not load font file '%s': %s\n", fontPathCString, strerror(errno)); + return(mg_font_nil()); + } + unsigned char* fontData = 0; + fseek(fontFile, 0, SEEK_END); + u32 fontDataSize = ftell(fontFile); + rewind(fontFile); + fontData = (unsigned char*)malloc(fontDataSize); + fread(fontData, 1, fontDataSize, fontFile); + fclose(fontFile); + + unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN, + UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT, + UNICODE_RANGE_LATIN_EXTENDED_A, + UNICODE_RANGE_LATIN_EXTENDED_B, + UNICODE_RANGE_SPECIALS}; + + mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges); + free(fontData); + + return(font); +} + int main() { LogLevel(LOG_LEVEL_WARNING); @@ -117,59 +204,184 @@ int main() return(-1); } + mg_font font = create_font(); - //TEST UI - ui_style defaultStyle = {.bgColor = {0.9, 0.9, 0.9, 1}, - .borderSize = 2, - .borderColor = {0, 0, 1, 1}, - .color = {0, 0, 0, 1}, - .fontSize = 32}; + // start app + mp_window_bring_to_front(window); + mp_window_focus(window); - ui_box* root = 0; - ui_frame(800, 800, defaultStyle) + while(!mp_should_quit()) { - root = ui_box_top(); + bool printDebugStyle = false; - ui_pattern pattern = {0}; - ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("b")}); - ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make(str8_lit("foo"))}); - ui_style_next(pattern, - &(ui_style){.fontSize = 12}, - UI_STYLE_FONT_SIZE); + f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC); - ui_container("a", 0) + mp_pump_events(0); + mp_event event = {0}; + while(mp_next_event(&event)) { - ui_pattern pattern = {0}; - ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("b")}); - ui_style_next(pattern, - &(ui_style){.fontSize = 20}, - UI_STYLE_FONT_SIZE); - - ui_container("b", 0) + switch(event.type) { - ui_container("c", 0) + case MP_EVENT_WINDOW_CLOSE: { - ui_box_make("d", 0); - } + mp_request_quit(); + } break; - ui_container("e", 0) + + case MP_EVENT_KEYBOARD_KEY: { - ui_tag_next(str8_lit("foo")); - ui_box_make("f", 0); - } + if(event.key.action == MP_KEY_PRESS && event.key.code == MP_KEY_P) + { + printDebugStyle = true; + } + } break; + + default: + break; } - ui_tag_next(str8_lit("foo")); - ui_box_make("d", 0); } - pattern = (ui_pattern){0}; - ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("c")}); - ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("d")}); - ui_style_prev(pattern, - &(ui_style){.fontSize = 30}, - UI_STYLE_FONT_SIZE); + //TEST UI + ui_style defaultStyle = {.size.width = {UI_SIZE_CHILDREN}, + .size.height = {UI_SIZE_CHILDREN}, + .layout.axis = UI_AXIS_Y, + .layout.spacing = 10, + .layout.margin.x = 10, + .layout.margin.y = 10, + .bgColor = {0.9, 0.9, 0.9, 1}, + .borderSize = 2, + .borderColor = {0, 0, 1, 1}, + .color = {0, 0, 0, 1}, + .font = font, + .fontSize = 32}; + + ui_flags defaultFlags = UI_FLAG_DRAW_BORDER; + + ui_box* root = 0; + ui_frame(800, 610, defaultStyle) + { + root = ui_box_top(); + +// ui_label("Hello, world!"); + + + ui_pattern pattern = {0}; + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("b")}); + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make("foo")}); + ui_style_next(pattern, + &(ui_style){.fontSize = 36}, + UI_STYLE_FONT_SIZE); + + ui_style_next(ui_pattern_all(), &defaultStyle, UI_STYLE_BORDER_SIZE|UI_STYLE_BORDER_COLOR|UI_STYLE_SIZE_X|UI_STYLE_SIZE_Y|UI_STYLE_LAYOUT); + + ui_container("a", defaultFlags) + { + ui_pattern pattern = {0}; + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("b")}); + ui_style_next(pattern, + &(ui_style){.fontSize = 22}, + UI_STYLE_FONT_SIZE); + + ui_container("b", defaultFlags) + { + ui_container("c", defaultFlags) + { + if(ui_button("button d").clicked) + { + printf("clicked button d\n"); + } + } + + ui_container("e", defaultFlags) + { + ui_tag_next("foo"); + if(ui_button("button f").clicked) + { + printf("clicked button f\n"); + } + + ui_style_next(ui_pattern_owner(), + &(ui_style){.size.width = {UI_SIZE_PIXELS, 200, 0}, + .size.height = {UI_SIZE_PIXELS, 20, 0}}, + UI_STYLE_SIZE_X|UI_STYLE_SIZE_Y); + static f32 slider1 = 0; + ui_slider("slider1", 0.3, &slider1); + + + ui_style_next(ui_pattern_owner(), + &(ui_style){.size.width = {UI_SIZE_PIXELS, 200, 0}, + .size.height = {UI_SIZE_PIXELS, 20, 0}}, + UI_STYLE_SIZE_X|UI_STYLE_SIZE_Y); + static f32 slider2 = 0; + ui_slider("slider2", 0.3, &slider2); + + ui_style_next(ui_pattern_owner(), + &(ui_style){.size.width = {UI_SIZE_PIXELS, 200, 0}, + .size.height = {UI_SIZE_PIXELS, 20, 0}}, + UI_STYLE_SIZE_X|UI_STYLE_SIZE_Y); + static f32 slider3 = 0; + ui_slider("slider3", 0.3, &slider3); + + } + } + ui_tag_next("foo"); + ui_label("label d"); + } + + pattern = (ui_pattern){0}; + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("c")}); + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make("button")}); + ui_style_prev(pattern, + &(ui_style){.bgColor = {1, 0.5, 0.5, 1}}, + UI_STYLE_BG_COLOR); + + pattern = (ui_pattern){0}; + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("c")}); + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make("button")}); + ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_STATUS, .op = UI_SEL_AND, .status = UI_ACTIVE|UI_HOVER}); + ui_style_prev(pattern, + &(ui_style){.bgColor = {0.5, 1, 0.5, 1}}, + UI_STYLE_BG_COLOR); + + } + if(printDebugStyle) + { + debug_print_styles(root, 0); + } + + mg_surface_prepare(surface); + +// mg_set_color_rgba(1, 0, 0, 1); +// mg_rectangle_fill(100, 100, 400, 200); + + ui_draw(); + +/* + mg_mat2x3 transform = {1, 0, 0, + 0, -1, 800}; + + bool oldTextFlip = mg_get_text_flip(); + mg_set_text_flip(true); + + mg_matrix_push(transform); + + mg_set_font(font); + mg_set_font_size(20); + mg_set_color_rgba(0, 0, 0, 1); + + mg_move_to(0, 38); + mg_text_outlines(str8_lit("hello, world")); + mg_fill(); + + mg_matrix_pop(); + + mg_set_text_flip(oldTextFlip); +*/ + mg_flush(); + mg_surface_present(surface); + + mem_arena_clear(mem_scratch()); } - debug_print_styles(root, 0); mp_terminate(); diff --git a/src/ui.c b/src/ui.c index 22d3367..535af3b 100644 --- a/src/ui.c +++ b/src/ui.c @@ -13,6 +13,22 @@ #define LOG_SUBSYSTEM "UI" + +static ui_style UI_STYLE_DEFAULTS = +{ + .size.width = {.kind = UI_SIZE_CHILDREN, + .value = 0, + .strictness = 0}, + .size.height = {.kind = UI_SIZE_CHILDREN, + .value = 0, + .strictness = 0}, + + .layout = {.axis = UI_AXIS_Y, + .align = {UI_ALIGN_START, + UI_ALIGN_START}}, + .color = {0, 0, 0, 1}, + .fontSize = 16, +}; //----------------------------------------------------------------------------- // context //----------------------------------------------------------------------------- @@ -193,32 +209,32 @@ void ui_box_pop(void) // tagging //----------------------------------------------------------------------------- -ui_tag ui_tag_make(str8 string) +ui_tag ui_tag_make_str8(str8 string) { ui_tag tag = {.hash = mp_hash_aes_string(string)}; return(tag); } -void ui_tag_box(ui_box* box, str8 string) +void ui_tag_box_str8(ui_box* box, str8 string) { ui_context* ui = ui_get_context(); ui_tag_elt* elt = mem_arena_alloc_type(&ui->frameArena, ui_tag_elt); - elt->tag = ui_tag_make(string); + elt->tag = ui_tag_make_str8(string); ListAppend(&box->tags, &elt->listElt); } -void ui_tag_next(str8 string) +void ui_tag_next_str8(str8 string) { ui_context* ui = ui_get_context(); ui_tag_elt* elt = mem_arena_alloc_type(&ui->frameArena, ui_tag_elt); - elt->tag = ui_tag_make(string); + elt->tag = ui_tag_make_str8(string); ListAppend(&ui->nextBoxTags, &elt->listElt); } //----------------------------------------------------------------------------- // key hashing and caching //----------------------------------------------------------------------------- -ui_key ui_key_from_string(str8 string) +ui_key ui_key_make_str8(str8 string) { ui_context* ui = ui_get_context(); u64 seed = 0; @@ -233,6 +249,23 @@ ui_key ui_key_from_string(str8 string) return(key); } +ui_key ui_key_make_path(str8_list path) +{ + ui_context* ui = ui_get_context(); + u64 seed = 0; + ui_box* parent = ui_box_top(); + if(parent) + { + seed = parent->key.hash; + } + for_each_in_list(&path.list, elt, str8_elt, listElt) + { + seed = mp_hash_aes_string_seed(elt->string, seed); + } + ui_key key = {seed}; + return(key); +} + bool ui_key_equal(ui_key a, ui_key b) { return(a.hash == b.hash); @@ -244,8 +277,9 @@ void ui_box_cache(ui_context* ui, ui_box* box) ListAppend(&(ui->boxMap[index]), &box->bucketElt); } -ui_box* ui_box_lookup_with_key(ui_context* ui, ui_key key) +ui_box* ui_box_lookup_key(ui_key key) { + ui_context* ui = ui_get_context(); u64 index = key.hash & (UI_BOX_MAP_BUCKET_COUNT-1); for_each_in_list(&ui->boxMap[index], box, ui_box, bucketElt) @@ -260,14 +294,8 @@ ui_box* ui_box_lookup_with_key(ui_context* ui, ui_key key) ui_box* ui_box_lookup_str8(str8 string) { - ui_context* ui = ui_get_context(); - ui_key key = ui_key_from_string(string); - return(ui_box_lookup_with_key(ui, key)); -} - -ui_box* ui_box_lookup(const char* string) -{ - return(ui_box_lookup_str8(str8_from_cstring((char*)string))); + ui_key key = ui_key_make_str8(string); + return(ui_box_lookup_key(key)); } //----------------------------------------------------------------------------- @@ -308,9 +336,58 @@ void ui_style_prev(ui_pattern pattern, ui_style* style, ui_style_mask mask) *rule->style = *style; ListAppend(&ui->lastBox->afterRules, &rule->boxElt); + rule->owner = ui->lastBox; } } +void ui_style_box_before(ui_box* box, ui_pattern pattern, ui_style* style, ui_style_mask mask) +{ + ui_context* ui = ui_get_context(); + if(ui) + { + ui_style_rule* rule = mem_arena_alloc_type(&ui->frameArena, ui_style_rule); + rule->pattern = pattern; + rule->mask = mask; + rule->style = mem_arena_alloc_type(&ui->frameArena, ui_style); + *rule->style = *style; + + ListAppend(&box->beforeRules, &rule->boxElt); + rule->owner = box; + } +} + +ui_pattern ui_pattern_all(void) +{ + ui_context* ui = ui_get_context(); + ui_pattern pattern = {0}; + ui_pattern_push(&ui->frameArena, &pattern, (ui_selector){.kind = UI_SEL_ANY}); + return(pattern); +} + +ui_pattern ui_pattern_owner(void) +{ + ui_context* ui = ui_get_context(); + ui_pattern pattern = {0}; + ui_pattern_push(&ui->frameArena, &pattern, (ui_selector){.kind = UI_SEL_OWNER}); + return(pattern); +} + +void ui_box_set_size(ui_box* box, ui_axis axis, ui_size_kind kind, f32 value, f32 strictness) +{ + ui_style style = {0}; + style.size.s[axis] = (ui_size){kind, value, strictness}; + ui_style_mask mask = (axis == UI_AXIS_X) ? UI_STYLE_SIZE_X : UI_STYLE_SIZE_Y; + ui_style_box_before(box, ui_pattern_owner(), &style, mask); +} + +void ui_box_set_layout(ui_box* box, ui_axis axis, ui_align alignX, ui_align alignY) +{ + ui_style style = {.layout = {axis, .align.x = alignX, .align.y = alignY}}; + ui_style_box_before(box, ui_pattern_owner(), &style, UI_STYLE_LAYOUT); +} + +void ui_apply_style_with_mask(ui_style* dst, ui_style* src, ui_style_mask mask); + //----------------------------------------------------------------------------- // ui boxes //----------------------------------------------------------------------------- @@ -363,8 +440,8 @@ ui_box* ui_box_make_str8(str8 string, ui_flags flags) { ui_context* ui = ui_get_context(); - ui_key key = ui_key_from_string(string); - ui_box* box = ui_box_lookup_with_key(ui, key); + ui_key key = ui_key_make_str8(string); + ui_box* box = ui_box_lookup_key(key); if(!box) { @@ -402,27 +479,28 @@ ui_box* ui_box_make_str8(str8 string, ui_flags flags) box->string = str8_push_copy(&ui->frameArena, string); box->flags = flags; + //NOTE: create style and setup non-inherited attributes to default values box->targetStyle = mem_arena_alloc_type(&ui->frameArena, ui_style); - memset(box->targetStyle, 0, sizeof(ui_style)); + ui_apply_style_with_mask(box->targetStyle, &UI_STYLE_DEFAULTS, ~0ULL); //NOTE: set tags, before rules and last box box->tags = ui->nextBoxTags; ui->nextBoxTags = (list_info){0}; box->beforeRules = ui->nextBoxRules; + for_each_in_list(&box->beforeRules, rule, ui_style_rule, boxElt) + { + rule->owner = box; + } ui->nextBoxRules = (list_info){0}; + box->afterRules = (list_info){0}; + ui->lastBox = box; return(box); } -ui_box* ui_box_make(const char* cstring, ui_flags flags) -{ - str8 string = str8_from_cstring((char*)cstring); - return(ui_box_make_str8(string, flags)); -} - ui_box* ui_box_begin_str8(str8 string, ui_flags flags) { ui_context* ui = ui_get_context(); @@ -431,12 +509,6 @@ ui_box* ui_box_begin_str8(str8 string, ui_flags flags) return(box); } -ui_box* ui_box_begin(const char* cstring, ui_flags flags) -{ - str8 string = str8_from_cstring((char*)cstring); - return(ui_box_begin_str8(string, flags)); -} - ui_box* ui_box_end(void) { ui_context* ui = ui_get_context(); @@ -456,18 +528,7 @@ void ui_box_set_render_proc(ui_box* box, ui_box_render_proc proc, void* data) box->renderProc = proc; box->renderData = data; } -/* -void ui_box_set_size(ui_box* box, ui_axis axis, ui_size_kind kind, f32 value, f32 strictness) -{ - box->targetStyle->size[axis] = (ui_size){kind, value, strictness}; -} -void ui_box_set_floating(ui_box* box, ui_axis axis, f32 pos) -{ - box->floating[axis] = true; - box->floatTarget.c[axis] = pos; -} -*/ void ui_box_set_closed(ui_box* box, bool closed) { box->closed = closed; @@ -619,20 +680,20 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box) { if(flags & UI_STYLE_ANIMATE_SIZE_X) { - ui_animate_ui_size(ui, &box->style.size[UI_AXIS_X], targetStyle->size[UI_AXIS_X], animationTime); + ui_animate_ui_size(ui, &box->style.size.s[UI_AXIS_X], targetStyle->size.s[UI_AXIS_X], animationTime); } else { - box->style.size[UI_AXIS_X] = targetStyle->size[UI_AXIS_X]; + box->style.size.s[UI_AXIS_X] = targetStyle->size.s[UI_AXIS_X]; } if(flags & UI_STYLE_ANIMATE_SIZE_Y) { - ui_animate_ui_size(ui, &box->style.size[UI_AXIS_Y], targetStyle->size[UI_AXIS_Y], animationTime); + ui_animate_ui_size(ui, &box->style.size.s[UI_AXIS_Y], targetStyle->size.s[UI_AXIS_Y], animationTime); } else { - box->style.size[UI_AXIS_Y] = targetStyle->size[UI_AXIS_Y]; + box->style.size.s[UI_AXIS_Y] = targetStyle->size.s[UI_AXIS_Y]; } if(flags & UI_STYLE_ANIMATE_COLOR) @@ -689,6 +750,9 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box) { box->style.roundness = targetStyle->roundness; } + + //TODO: non animatable attributes use mask + box->style.layout = targetStyle->layout; box->style.font = targetStyle->font; } } @@ -697,11 +761,45 @@ void ui_apply_style_with_mask(ui_style* dst, ui_style* src, ui_style_mask mask) { if(mask & UI_STYLE_SIZE_X) { - dst->size[UI_AXIS_X] = src->size[UI_AXIS_X]; + dst->size.s[UI_AXIS_X] = src->size.s[UI_AXIS_X]; } if(mask & UI_STYLE_SIZE_Y) { - dst->size[UI_AXIS_Y] = src->size[UI_AXIS_Y]; + dst->size.s[UI_AXIS_Y] = src->size.s[UI_AXIS_Y]; + } + if(mask & UI_STYLE_LAYOUT_AXIS) + { + dst->layout.axis = src->layout.axis; + } + if(mask & UI_STYLE_LAYOUT_ALIGN_X) + { + dst->layout.align.x = src->layout.align.x; + } + if(mask & UI_STYLE_LAYOUT_ALIGN_Y) + { + dst->layout.align.y = src->layout.align.y; + } + if(mask & UI_STYLE_LAYOUT_SPACING) + { + dst->layout.spacing = src->layout.spacing; + } + if(mask & UI_STYLE_LAYOUT_MARGIN_X) + { + dst->layout.margin.x = src->layout.margin.y; + } + if(mask & UI_STYLE_LAYOUT_MARGIN_Y) + { + dst->layout.margin.y = src->layout.margin.y; + } + if(mask & UI_STYLE_FLOAT_X) + { + dst->floating[UI_AXIS_X] = src->floating[UI_AXIS_X]; + dst->floatTarget.x = src->floatTarget.x; + } + if(mask & UI_STYLE_FLOAT_Y) + { + dst->floating[UI_AXIS_Y] = src->floating[UI_AXIS_Y]; + dst->floatTarget.y = src->floatTarget.y; } if(mask & UI_STYLE_COLOR) { @@ -742,11 +840,19 @@ void ui_apply_style_with_mask(ui_style* dst, ui_style* src, ui_style_mask mask) } -bool ui_style_selector_match(ui_box* box, ui_selector* selector) +bool ui_style_selector_match(ui_box* box, ui_style_rule* rule, ui_selector* selector) { bool res = false; switch(selector->kind) { + case UI_SEL_ANY: + res = true; + break; + + case UI_SEL_OWNER: + res = (box == rule->owner); + break; + case UI_SEL_TEXT: res = !str8_cmp(box->string, selector->text); break; @@ -763,17 +869,22 @@ bool ui_style_selector_match(ui_box* box, ui_selector* selector) } } break; - case UI_SEL_HOVER: - res = box->hot; - break; - - case UI_SEL_ACTIVE: - res = box->active; - break; - - case UI_SEL_DRAGGING: - res = box->dragging; - break; + case UI_SEL_STATUS: + { + res = true; + if(selector->status & UI_HOVER) + { + res = res && box->hot; + } + if(selector->status & UI_ACTIVE) + { + res = res && box->active; + } + if(selector->status & UI_DRAGGING) + { + res = res && box->dragging; + } + } break; case UI_SEL_KEY: res = ui_key_equal(box->key, selector->key); @@ -786,9 +897,18 @@ bool ui_style_selector_match(ui_box* box, ui_selector* selector) void ui_style_rule_match(ui_context* ui, ui_box* box, ui_style_rule* rule, list_info* buildList, list_info* tmpList) { ui_selector* selector = ListFirstEntry(&rule->pattern.l, ui_selector, listElt); - if(ui_style_selector_match(box, selector)) + bool match = ui_style_selector_match(box, rule, selector); + + selector = ListNextEntry(&rule->pattern.l, selector, ui_selector, listElt); + while(match && selector && selector->op == UI_SEL_AND) { - if(!selector->listElt.next) + match = match && ui_style_selector_match(box, rule, selector); + selector = ListNextEntry(&rule->pattern.l, selector, ui_selector, listElt); + } + + if(match) + { + if(!selector) { ui_apply_style_with_mask(box->targetStyle, rule->style, rule->mask); } @@ -798,10 +918,10 @@ void ui_style_rule_match(ui_context* ui, ui_box* box, ui_style_rule* rule, list_ ui_style_rule* derived = mem_arena_alloc_type(&ui->frameArena, ui_style_rule); derived->mask = rule->mask; derived->style = rule->style; - derived->pattern.l = (list_info){selector->listElt.next, rule->pattern.l.last}; + derived->pattern.l = (list_info){&selector->listElt, rule->pattern.l.last}; - ListPush(buildList, &derived->buildElt); - ListPush(tmpList, &derived->tmpElt); + ListAppend(buildList, &derived->buildElt); + ListAppend(tmpList, &derived->tmpElt); } } } @@ -825,6 +945,7 @@ void ui_styling_prepass(ui_context* ui, ui_box* box, list_info* before, list_inf for_each_in_list(&box->beforeRules, rule, ui_style_rule, boxElt) { ListAppend(before, &rule->buildElt); + ListAppend(&tmpBefore, &rule->tmpElt); ui_style_rule_match(ui, box, rule, before, &tmpBefore); } @@ -836,6 +957,7 @@ void ui_styling_prepass(ui_context* ui, ui_box* box, list_info* before, list_inf for_each_in_list(&box->afterRules, rule, ui_style_rule, boxElt) { ListAppend(after, &rule->buildElt); + ListAppend(&tmpAfter, &rule->tmpElt); ui_style_rule_match(ui, box, rule, after, &tmpAfter); } @@ -850,8 +972,8 @@ void ui_styling_prepass(ui_context* ui, ui_box* box, list_info* before, list_inf ui_style* style = &box->style; mp_rect textBox = {}; - ui_size desiredSize[2] = {box->style.size[UI_AXIS_X], - box->style.size[UI_AXIS_Y]}; + ui_size desiredSize[2] = {box->style.size.s[UI_AXIS_X], + box->style.size.s[UI_AXIS_Y]}; if( desiredSize[UI_AXIS_X].kind == UI_SIZE_TEXT ||desiredSize[UI_AXIS_Y].kind == UI_SIZE_TEXT) @@ -897,15 +1019,16 @@ void ui_layout_upward_dependent_size(ui_context* ui, ui_box* box, int axis) return; } - ui_size* size = &box->style.size[axis]; + ui_size* size = &box->style.size.s[axis]; - if(size->kind == UI_SIZE_PARENT_RATIO) + if(size->kind == UI_SIZE_PARENT) { ui_box* parent = box->parent; if( parent - && parent->style.size[axis].kind != UI_SIZE_CHILDREN) + && parent->style.size.s[axis].kind != UI_SIZE_CHILDREN) { - box->rect.c[2+axis] = parent->rect.c[2+axis] * size->value; + f32 margin = parent->style.layout.margin.m[axis]; + box->rect.c[2+axis] = maximum(0, parent->rect.c[2+axis] - 2*margin) * size->value; } //TODO else? } @@ -919,8 +1042,10 @@ void ui_layout_upward_dependent_size(ui_context* ui, ui_box* box, int axis) void ui_layout_downward_dependent_size(ui_context* ui, ui_box* box, int axis) { f32 sum = 0; + if(box->style.layout.axis == axis) { + int count = 0; for_each_in_list(&box->children, child, ui_box, listElt) { if(!ui_box_hidden(child)) @@ -929,9 +1054,11 @@ void ui_layout_downward_dependent_size(ui_context* ui, ui_box* box, int axis) if(!child->style.floating[axis]) { sum += child->rect.c[2+axis]; + count++; } } } + sum += maximum(0, count-1)*box->style.layout.spacing; } else { @@ -950,10 +1077,11 @@ void ui_layout_downward_dependent_size(ui_context* ui, ui_box* box, int axis) box->childrenSum[axis] = sum; - ui_size* size = &box->style.size[axis]; + ui_size* size = &box->style.size.s[axis]; if(size->kind == UI_SIZE_CHILDREN) { - box->rect.c[2+axis] = sum + size->value*2; + f32 margin = box->style.layout.margin.m[axis]; + box->rect.c[2+axis] = sum + margin*2; } } @@ -976,20 +1104,17 @@ void ui_layout_compute_rect(ui_context* ui, ui_box* box, vec2 pos) ui_axis layoutAxis = box->style.layout.axis; ui_axis secondAxis = (layoutAxis == UI_AXIS_X) ? UI_AXIS_Y : UI_AXIS_X; - ui_align* align = box->style.layout.align; + f32 spacing = box->style.layout.spacing; + + ui_align* align = box->style.layout.align.a; vec2 origin = {box->rect.x - box->scroll.x, box->rect.y - box->scroll.y}; vec2 currentPos = origin; - vec2 margin = {0, 0}; - for(int i=0; istyle.size[i].kind == UI_SIZE_CHILDREN) - { - margin.c[i] = box->style.size[i].value; - } - } + vec2 margin = {box->style.layout.margin.x, + box->style.layout.margin.y}; + currentPos.x += margin.x; currentPos.y += margin.y; @@ -1038,7 +1163,7 @@ void ui_layout_compute_rect(ui_context* ui, ui_box* box, vec2 pos) if(!child->style.floating[layoutAxis]) { - currentPos.c[layoutAxis] += child->rect.c[2+layoutAxis]; + currentPos.c[layoutAxis] += child->rect.c[2+layoutAxis] + spacing; } } } @@ -1128,7 +1253,7 @@ void ui_draw_box(ui_box* box) if(box->flags & UI_FLAG_CLIP) { - mg_clip_push(box->rect.x, box->rect.y, box->rect.w, box->rect.h); +// mg_clip_push(box->rect.x, box->rect.y, box->rect.w, box->rect.h); } if(box->flags & UI_FLAG_DRAW_BACKGROUND) @@ -1165,7 +1290,7 @@ void ui_draw_box(ui_box* box) if(box->flags & UI_FLAG_CLIP) { - mg_clip_pop(); +// mg_clip_pop(); } if(box->flags & UI_FLAG_DRAW_BORDER) @@ -1216,24 +1341,27 @@ void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle) ui->clipStack = 0; ui->z = 0; -// ui_push_size(UI_AXIS_X, UI_SIZE_PIXELS, width, 0); -// ui_push_size(UI_AXIS_Y, UI_SIZE_PIXELS, height, 0); + defaultStyle.size.s[UI_AXIS_X] = (ui_size){UI_SIZE_PIXELS, width, 0}; + defaultStyle.size.s[UI_AXIS_Y] = (ui_size){UI_SIZE_PIXELS, height, 0}; ui->root = ui_box_begin("_root_", 0); *ui->root->targetStyle = defaultStyle; - ui_box* contents = ui_box_make("_contents_", 0); -// ui_box_set_floating(contents, UI_AXIS_X, 0); -// ui_box_set_floating(contents, UI_AXIS_Y, 0); + ui_style_next(ui_pattern_owner(), + &(ui_style){.layout = {UI_AXIS_Y, UI_ALIGN_START, UI_ALIGN_START}, + .floating = {true, true}, + .floatTarget = {0, 0}}, + UI_STYLE_LAYOUT | UI_STYLE_FLOAT_X | UI_STYLE_FLOAT_Y); -// ui_box_set_layout(contents, UI_AXIS_Y, UI_ALIGN_START, UI_ALIGN_START); + ui_box* contents = ui_box_make("_contents_", 0); + + ui_style_next(ui_pattern_owner(), + &(ui_style){.layout = {UI_AXIS_Y, UI_ALIGN_START, UI_ALIGN_START}, + .floating = {true, true}, + .floatTarget = {0, 0}}, + UI_STYLE_LAYOUT | UI_STYLE_FLOAT_X | UI_STYLE_FLOAT_Y); ui->overlay = ui_box_make("_overlay_", 0); -// ui_box_set_floating(ui->overlay, UI_AXIS_X, 0); -// ui_box_set_floating(ui->overlay, UI_AXIS_Y, 0); - -// ui_pop_size(UI_AXIS_X); -// ui_pop_size(UI_AXIS_Y); ui->nextBoxRules = (list_info){0}; ui->nextBoxTags = (list_info){0}; @@ -1300,11 +1428,14 @@ void ui_cleanup(void) ui_sig ui_label_str8(str8 label) { + ui_style_next(ui_pattern_all(), + &(ui_style){.size.width = {UI_SIZE_TEXT, 0, 0}, + .size.height = {UI_SIZE_TEXT, 0, 0}}, + UI_STYLE_SIZE_X | UI_STYLE_SIZE_Y); + ui_flags flags = UI_FLAG_CLIP | UI_FLAG_DRAW_TEXT; ui_box* box = ui_box_make_str8(label, flags); -// ui_box_set_size(box, UI_AXIS_X, UI_SIZE_TEXT, 0, 0); -// ui_box_set_size(box, UI_AXIS_Y, UI_SIZE_TEXT, 0, 0); ui_sig sig = ui_box_sig(box); return(sig); @@ -1317,18 +1448,48 @@ ui_sig ui_label(const char* label) ui_sig ui_button_str8(str8 label) { + ui_context* ui = ui_get_context(); + + ui_style defaultStyle = {.size.width = {UI_SIZE_TEXT, 5, 0}, + .size.height = {UI_SIZE_TEXT, 5, 0}, + .bgColor = {0.5, 0.5, 0.5, 1}, + .borderColor = {0.2, 0.2, 0.2, 1}, + .borderSize = 2, + .roundness = 10}; + + ui_style_mask defaultMask = UI_STYLE_SIZE_X + | UI_STYLE_SIZE_Y + | UI_STYLE_BG_COLOR + | UI_STYLE_BORDER_COLOR + | UI_STYLE_BORDER_SIZE + | UI_STYLE_ROUNDNESS; + + ui_style_next(ui_pattern_all(), &defaultStyle, defaultMask); + + ui_style activeStyle = {.bgColor = {0.3, 0.3, 0.3, 1}, + .borderColor = {0.2, 0.2, 0.2, 1}, + .borderSize = 4}; + ui_style_mask activeMask = UI_STYLE_BG_COLOR + | UI_STYLE_BORDER_COLOR + | UI_STYLE_BORDER_SIZE; + ui_pattern activePattern = {0}; + ui_pattern_push(&ui->frameArena, + &activePattern, + (ui_selector){.kind = UI_SEL_STATUS, + .status = UI_ACTIVE|UI_HOVER}); + ui_style_next(activePattern, &activeStyle, activeMask); + ui_flags flags = UI_FLAG_CLICKABLE | UI_FLAG_CLIP - | UI_FLAG_DRAW_FOREGROUND + | UI_FLAG_DRAW_BACKGROUND | UI_FLAG_DRAW_BORDER | UI_FLAG_DRAW_TEXT | UI_FLAG_HOT_ANIMATION | UI_FLAG_ACTIVE_ANIMATION; ui_box* box = ui_box_make_str8(label, flags); + ui_tag_box(box, "button"); - //TODO -// ui_box_set_tag(box, UI_STYLE_TAG_BUTTON); ui_sig sig = ui_box_sig(box); if(sig.hovering) @@ -1356,6 +1517,112 @@ ui_sig ui_button(const char* label) return(ui_button_str8(str8_from_cstring((char*)label))); } + +ui_box* ui_slider(const char* label, f32 thumbRatio, f32* scrollValue) +{ + ui_box* frame = ui_box_begin(label, 0); + { + ui_style_box_before(frame, ui_pattern_all(), &(ui_style){0}, UI_STYLE_LAYOUT); + + ui_axis trackAxis = (frame->rect.w > frame->rect.h) ? UI_AXIS_X : UI_AXIS_Y; + ui_axis secondAxis = (trackAxis == UI_AXIS_Y) ? UI_AXIS_X : UI_AXIS_Y; + + f32 roundness = 0.5*frame->rect.c[2+secondAxis]; + f32 animationTime = 0.5; + + ui_style trackStyle = {.bgColor = {0.5, 0.5, 0.5, 1}, + .roundness = roundness}; + ui_style thumbStyle = {.bgColor = {0.3, 0.3, 0.3, 1}, + .roundness = roundness}; + + ui_style_mask styleMask = UI_STYLE_BG_COLOR | UI_STYLE_ROUNDNESS; + + ui_flags trackFlags = UI_FLAG_CLIP + | UI_FLAG_DRAW_BACKGROUND + | UI_FLAG_HOT_ANIMATION + | UI_FLAG_ACTIVE_ANIMATION; + + ui_box* track = ui_box_begin("track", trackFlags); + ui_style_box_before(track, ui_pattern_owner(), &trackStyle, styleMask); + + ui_box_set_size(track, trackAxis, UI_SIZE_PARENT, 1., 0); + ui_box_set_size(track, secondAxis, UI_SIZE_PARENT, 1., 0); + ui_box_set_layout(track, trackAxis, UI_ALIGN_START, UI_ALIGN_START); + + f32 beforeRatio = (*scrollValue) * (1. - thumbRatio); + f32 afterRatio = (1. - *scrollValue) * (1. - thumbRatio); + + ui_box* beforeSpacer = ui_box_make("before", 0); + ui_box_set_size(beforeSpacer, trackAxis, UI_SIZE_PARENT, beforeRatio, 0); + ui_box_set_size(beforeSpacer, secondAxis, UI_SIZE_PARENT, 1., 0); + + ui_flags thumbFlags = UI_FLAG_CLICKABLE + | UI_FLAG_DRAW_BACKGROUND + | UI_FLAG_HOT_ANIMATION + | UI_FLAG_ACTIVE_ANIMATION; + + ui_box* thumb = ui_box_make("thumb", thumbFlags); + ui_style_box_before(thumb, ui_pattern_owner(), &thumbStyle, styleMask); + ui_box_set_size(thumb, trackAxis, UI_SIZE_PARENT, thumbRatio, 0); + ui_box_set_size(thumb, secondAxis, UI_SIZE_PARENT, 1., 0); + + ui_box* afterSpacer = ui_box_make("after", 0); + ui_box_set_size(afterSpacer, trackAxis, UI_SIZE_PARENT, afterRatio, 0); + ui_box_set_size(afterSpacer, secondAxis, UI_SIZE_PARENT, 1., 0); + ui_box_end(); + + //NOTE: interaction + ui_sig thumbSig = ui_box_sig(thumb); + if(thumbSig.dragging) + { + f32 trackExtents = track->rect.c[2+trackAxis] - thumb->rect.c[2+trackAxis]; + f32 delta = thumbSig.delta.c[trackAxis]/trackExtents; + f32 oldValue = *scrollValue; + + *scrollValue += delta; + *scrollValue = Clamp(*scrollValue, 0, 1); + } + + ui_sig trackSig = ui_box_sig(track); + + if(ui_box_active(frame)) + { + //NOTE: activated from outside + ui_box_set_hot(track, true); + ui_box_set_hot(thumb, true); + ui_box_activate(track); + ui_box_activate(thumb); + } + + if(trackSig.hovering) + { + ui_box_set_hot(track, true); + ui_box_set_hot(thumb, true); + } + else if(thumbSig.wheel.c[trackAxis] == 0) + { + ui_box_set_hot(track, false); + ui_box_set_hot(thumb, false); + } + + if(thumbSig.dragging) + { + ui_box_activate(track); + ui_box_activate(thumb); + } + else if(thumbSig.wheel.c[trackAxis] == 0) + { + ui_box_deactivate(track); + ui_box_deactivate(thumb); + ui_box_deactivate(frame); + } + + } ui_box_end(); + + return(frame); +} + + ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) { ui_box* frame = ui_box_begin(label, 0); @@ -1366,6 +1633,7 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) f32 roundness = 0.5*frame->rect.c[2+secondAxis]; f32 animationTime = 0.5; + /* ui_push_bg_color((mg_color){0, 0, 0, 0}); ui_push_fg_color((mg_color){0, 0, 0, 0}); ui_push_roundness(roundness); @@ -1383,16 +1651,16 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) ui_box* track = ui_box_begin("track", trackFlags); -// ui_box_set_size(track, trackAxis, UI_SIZE_PARENT_RATIO, 1., 0); -// ui_box_set_size(track, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(track, trackAxis, UI_SIZE_PARENT, 1., 0); +// ui_box_set_size(track, secondAxis, UI_SIZE_PARENT, 1., 0); //ui_box_set_layout(track, trackAxis, UI_ALIGN_START, UI_ALIGN_START); f32 beforeRatio = (*scrollValue) * (1. - thumbRatio); f32 afterRatio = (1. - *scrollValue) * (1. - thumbRatio); ui_box* beforeSpacer = ui_box_make("before", 0); -// ui_box_set_size(beforeSpacer, trackAxis, UI_SIZE_PARENT_RATIO, beforeRatio, 0); -// ui_box_set_size(beforeSpacer, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(beforeSpacer, trackAxis, UI_SIZE_PARENT, beforeRatio, 0); +// ui_box_set_size(beforeSpacer, secondAxis, UI_SIZE_PARENT, 1., 0); ui_flags thumbFlags = UI_FLAG_CLICKABLE | UI_FLAG_DRAW_FOREGROUND @@ -1400,12 +1668,12 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) | UI_FLAG_ACTIVE_ANIMATION; ui_box* thumb = ui_box_make("thumb", thumbFlags); -// ui_box_set_size(thumb, trackAxis, UI_SIZE_PARENT_RATIO, thumbRatio, 0); -// ui_box_set_size(thumb, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(thumb, trackAxis, UI_SIZE_PARENT, thumbRatio, 0); +// ui_box_set_size(thumb, secondAxis, UI_SIZE_PARENT, 1., 0); ui_box* afterSpacer = ui_box_make("after", 0); -// ui_box_set_size(afterSpacer, trackAxis, UI_SIZE_PARENT_RATIO, afterRatio, 0); -// ui_box_set_size(afterSpacer, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(afterSpacer, trackAxis, UI_SIZE_PARENT, afterRatio, 0); +// ui_box_set_size(afterSpacer, secondAxis, UI_SIZE_PARENT, 1., 0); ui_box_end(); /* @@ -1500,7 +1768,7 @@ void ui_panel_end() f32 sliderX = panel->scroll.x /(contentsW - panel->rect.w); scrollBarX = ui_scrollbar("scrollerX", thumbRatioX, &sliderX); -// ui_box_set_size(scrollBarX, UI_AXIS_X, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(scrollBarX, UI_AXIS_X, UI_SIZE_PARENT, 1., 0); // ui_box_set_size(scrollBarX, UI_AXIS_Y, UI_SIZE_PIXELS, 10, 0); panel->scroll.x = sliderX * (contentsW - panel->rect.w); @@ -1518,7 +1786,7 @@ void ui_panel_end() scrollBarY = ui_scrollbar("scrollerY", thumbRatioY, &sliderY); // ui_box_set_size(scrollBarY, UI_AXIS_X, UI_SIZE_PIXELS, 10, 0); -// ui_box_set_size(scrollBarY, UI_AXIS_Y, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(scrollBarY, UI_AXIS_Y, UI_SIZE_PARENT, 1., 0); panel->scroll.y = sliderY * (contentsH - panel->rect.h); if(sig.hovering) @@ -1575,7 +1843,7 @@ void ui_tooltip_end(void) void ui_menu_bar_begin(const char* name) { ui_box* bar = ui_box_begin(name, UI_FLAG_DRAW_BACKGROUND); -// ui_box_set_size(bar, UI_AXIS_X, UI_SIZE_PARENT_RATIO, 1., 0); +// ui_box_set_size(bar, UI_AXIS_X, UI_SIZE_PARENT, 1., 0); // ui_box_set_size(bar, UI_AXIS_Y, UI_SIZE_CHILDREN, 0, 0); //ui_box_set_layout(bar, UI_AXIS_X, UI_ALIGN_START, UI_ALIGN_START); /* diff --git a/src/ui.h b/src/ui.h index 20d765c..083e412 100644 --- a/src/ui.h +++ b/src/ui.h @@ -52,19 +52,39 @@ typedef enum UI_ALIGN_CENTER, } ui_align; +typedef union ui_layout_align +{ + struct + { + ui_align x; + ui_align y; + }; + ui_align a[UI_AXIS_COUNT]; +} ui_layout_align; + typedef struct ui_layout { ui_axis axis; - ui_align align[UI_AXIS_COUNT]; + f32 spacing; + union + { + struct + { + f32 x; + f32 y; + }; + f32 m[UI_AXIS_COUNT]; + } margin; + ui_layout_align align; } ui_layout; -typedef enum +typedef enum ui_size_kind { UI_SIZE_TEXT, UI_SIZE_PIXELS, UI_SIZE_CHILDREN, - UI_SIZE_PARENT_RATIO, + UI_SIZE_PARENT, } ui_size_kind; @@ -75,6 +95,16 @@ typedef struct ui_size f32 strictness; } ui_size; +typedef union ui_box_size +{ + struct + { + ui_size width; + ui_size height; + }; + ui_size s[UI_AXIS_COUNT]; +} ui_box_size; + typedef enum { UI_STYLE_ANIMATE_SIZE_X = 1<<1, UI_STYLE_ANIMATE_SIZE_Y = 1<<2, UI_STYLE_ANIMATE_COLOR = 1<<3, @@ -88,7 +118,7 @@ typedef enum { UI_STYLE_ANIMATE_SIZE_X = 1<<1, typedef struct ui_style { - ui_size size[UI_AXIS_COUNT]; + ui_box_size size; ui_layout layout; bool floating[UI_AXIS_COUNT]; vec2 floatTarget; @@ -106,19 +136,34 @@ typedef struct ui_style typedef u64 ui_style_mask; enum { - UI_STYLE_NONE = 0, - UI_STYLE_SIZE_X = 1<<1, - UI_STYLE_SIZE_Y = 1<<2, - UI_STYLE_COLOR = 1<<3, - UI_STYLE_BG_COLOR = 1<<4, - UI_STYLE_BORDER_COLOR = 1<<6, - UI_STYLE_BORDER_SIZE = 1<<7, - UI_STYLE_ROUNDNESS = 1<<8, - UI_STYLE_FONT = 1<<9, - UI_STYLE_FONT_SIZE = 1<<10, - UI_STYLE_ANIMATION_TIME = 1<<11, - UI_STYLE_ANIMATION_FLAGS = 1<<12, - //... + UI_STYLE_NONE = 0, + UI_STYLE_SIZE_X = 1<<1, + UI_STYLE_SIZE_Y = 1<<2, + UI_STYLE_LAYOUT_AXIS = 1<<3, + UI_STYLE_LAYOUT_ALIGN_X = 1<<4, + UI_STYLE_LAYOUT_ALIGN_Y = 1<<5, + UI_STYLE_LAYOUT_SPACING = 1<<6, + UI_STYLE_LAYOUT_MARGIN_X = 1<<7, + UI_STYLE_LAYOUT_MARGIN_Y = 1<<8, + UI_STYLE_FLOAT_X = 1<<9, + UI_STYLE_FLOAT_Y = 1<<10, + UI_STYLE_COLOR = 1<<11, + UI_STYLE_BG_COLOR = 1<<12, + UI_STYLE_BORDER_COLOR = 1<<13, + UI_STYLE_BORDER_SIZE = 1<<14, + UI_STYLE_ROUNDNESS = 1<<15, + UI_STYLE_FONT = 1<<16, + UI_STYLE_FONT_SIZE = 1<<17, + UI_STYLE_ANIMATION_TIME = 1<<18, + UI_STYLE_ANIMATION_FLAGS = 1<<19, + + //masks + UI_STYLE_LAYOUT = UI_STYLE_LAYOUT_AXIS + | UI_STYLE_LAYOUT_ALIGN_X + | UI_STYLE_LAYOUT_ALIGN_Y + | UI_STYLE_LAYOUT_SPACING + | UI_STYLE_LAYOUT_MARGIN_X + | UI_STYLE_LAYOUT_MARGIN_Y, UI_STYLE_MASK_INHERITED = UI_STYLE_COLOR | UI_STYLE_FONT @@ -132,43 +177,62 @@ typedef struct ui_tag { u64 hash; } ui_tag; typedef enum { + UI_SEL_ANY, + UI_SEL_OWNER, UI_SEL_TEXT, UI_SEL_TAG, - UI_SEL_HOVER, - UI_SEL_ACTIVE, - UI_SEL_DRAGGING, + UI_SEL_STATUS, UI_SEL_KEY, //... } ui_selector_kind; +typedef u8 ui_status; +enum +{ + UI_NONE = 0, + UI_HOVER = 1<<1, + UI_ACTIVE = 1<<2, + UI_DRAGGING = 1<<3, +}; + +typedef enum +{ + UI_SEL_DESCENDANT = 0, + UI_SEL_AND = 1, + //... +} ui_selector_op; + typedef struct ui_selector { list_elt listElt; ui_selector_kind kind; + ui_selector_op op; union { str8 text; ui_key key; ui_tag tag; + ui_status status; //... }; } ui_selector; typedef struct ui_pattern { list_info l; } ui_pattern; +typedef struct ui_box ui_box; + typedef struct ui_style_rule { list_elt boxElt; list_elt buildElt; list_elt tmpElt; + ui_box* owner; ui_pattern pattern; ui_style_mask mask; ui_style* style; } ui_style_rule; -typedef struct ui_box ui_box; - typedef struct ui_sig { ui_box* box; @@ -242,22 +306,38 @@ struct ui_box typedef struct ui_context ui_context; +//------------------------------------------------------------------------------------- +// UI context initialization and frame cycle +//------------------------------------------------------------------------------------- void ui_init(void); ui_context* ui_get_context(void); void ui_set_context(ui_context* context); void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle); void ui_end_frame(void); -#define ui_frame(width, height, defaultStyle) defer_loop(ui_begin_frame(width, height, defaultStyle), ui_end_frame()) - void ui_draw(void); -ui_box* ui_box_lookup(const char* string); +#define ui_frame(width, height, defaultStyle) defer_loop(ui_begin_frame(width, height, defaultStyle), ui_end_frame()) + +//------------------------------------------------------------------------------------- +// Box keys +//------------------------------------------------------------------------------------- +ui_key ui_key_make_str8(str8 string); +ui_key ui_key_make_path(str8_list path); + +ui_box* ui_box_lookup_key(ui_key key); ui_box* ui_box_lookup_str8(str8 string); -ui_box* ui_box_make(const char* string, ui_flags flags); -ui_box* ui_box_begin(const char* string, ui_flags flags); + +// C-string helper +#define ui_key_make(s) ui_key_make_str8(str8_lit(s)) +#define ui_box_lookup(s) ui_box_lookup_str8(str8_lit(s)) + +//------------------------------------------------------------------------------------- +// Box hierarchy building +//------------------------------------------------------------------------------------- ui_box* ui_box_make_str8(str8 string, ui_flags flags); ui_box* ui_box_begin_str8(str8 string, ui_flags flags); + ui_box* ui_box_end(void); #define ui_container(name, flags) defer_loop(ui_box_begin(name, flags), ui_box_end()) @@ -265,10 +345,16 @@ void ui_box_push(ui_box* box); void ui_box_pop(void); ui_box* ui_box_top(void); -ui_tag ui_tag_make(str8 string); -void ui_tag_box(ui_box* box, str8 string); -void ui_tag_next(str8 string); +void ui_box_set_render_proc(ui_box* box, ui_box_render_proc proc, void* data); +// C-string helpers +#define ui_box_lookup(s) ui_box_lookup_str8(str8_lit(s)) +#define ui_box_make(s, flags) ui_box_make_str8(str8_lit(s), flags) +#define ui_box_begin(s, flags) ui_box_begin_str8(str8_lit(s), flags) + +//------------------------------------------------------------------------------------- +// Box status and signals +//------------------------------------------------------------------------------------- bool ui_box_closed(ui_box* box); void ui_box_set_closed(ui_box* box, bool closed); @@ -279,15 +365,23 @@ void ui_box_deactivate(ui_box* box); bool ui_box_hot(ui_box* box); void ui_box_set_hot(ui_box* box, bool hot); -void ui_box_set_render_proc(ui_box* box, ui_box_render_proc proc, void* data); -/* -void ui_box_set_layout(ui_box* box, ui_axis axis, ui_align alignX, ui_align alignY); -void ui_box_set_size(ui_box* box, ui_axis axis, ui_size_kind kind, f32 value, f32 strictness); -void ui_box_set_floating(ui_box* box, ui_axis axis, f32 pos); -void ui_box_set_style_selector(ui_box* box, ui_style_selector selector); -*/ ui_sig ui_box_sig(ui_box* box); +//------------------------------------------------------------------------------------- +// Tagging +//------------------------------------------------------------------------------------- +ui_tag ui_tag_make_str8(str8 string); +void ui_tag_box_str8(ui_box* box, str8 string); +void ui_tag_next_str8(str8 string); + +// C-string helpers +#define ui_tag_make(s) ui_tag_make_str8(str8_lit(s)) +#define ui_tag_box(b, s) ui_tag_box_str8(b, str8_lit(s)) +#define ui_tag_next(s) ui_tag_next_str8(str8_lit(s)) + +//------------------------------------------------------------------------------------- +// Styling +//------------------------------------------------------------------------------------- //NOTE: styling API //WARN: You can use a pattern in multiple rules, but be aware that a pattern is references an underlying list of selectors, // hence pushing to a pattern also modifies rules in which the pattern was previously used! @@ -295,6 +389,15 @@ void ui_pattern_push(mem_arena* arena, ui_pattern* pattern, ui_selector selector void ui_style_next(ui_pattern pattern, ui_style* style, ui_style_mask mask); void ui_style_prev(ui_pattern pattern, ui_style* style, ui_style_mask mask); +// common patterns helpers +ui_pattern ui_pattern_all(void); +ui_pattern ui_pattern_owner(void); + +// single box styling helpers +void ui_box_set_size(ui_box* box, ui_axis axis, ui_size_kind kind, f32 value, f32 stricness); +void ui_box_set_layout(ui_box* box, ui_axis axis, ui_align alignX, ui_align alignY); +//... + //------------------------------------------------------------------------- // Basic widget helpers //------------------------------------------------------------------------- @@ -309,10 +412,10 @@ enum { }; ui_sig ui_label(const char* label); -ui_sig ui_label_str8(str8 label); ui_sig ui_button(const char* label); -ui_sig ui_button_str8(str8 label); + +ui_box* ui_slider(const char* label, f32 thumbRatio, f32* scrollValue); ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue); ui_box* ui_panel_begin(const char* name);