[ui, styling] revisiting layout/sizing

This commit is contained in:
Martin Fouilleul 2023-03-07 19:06:04 +01:00
parent d3a8a651e5
commit 8032c7340b
3 changed files with 772 additions and 189 deletions

View File

@ -31,6 +31,14 @@ void debug_print_rule(ui_style_rule* rule)
{ {
switch(selector->kind) switch(selector->kind)
{ {
case UI_SEL_ANY:
printf("any: ");
break;
case UI_SEL_OWNER:
printf("owner: ");
break;
case UI_SEL_TEXT: case UI_SEL_TEXT:
printf("text='%.*s': ", (int)selector->text.len, selector->text.ptr); printf("text='%.*s': ", (int)selector->text.len, selector->text.ptr);
break; break;
@ -39,6 +47,26 @@ void debug_print_rule(ui_style_rule* rule)
printf("tag=0x%llx: ", selector->tag.hash); printf("tag=0x%llx: ", selector->tag.hash);
break; 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: default:
printf("unknown: "); printf("unknown: ");
break; break;
@ -46,6 +74,30 @@ void debug_print_rule(ui_style_rule* rule)
} }
printf("=> font size = %f\n", rule->style->fontSize); 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) 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); debug_print_indent(indent);
printf("font size: %f\n", box->targetStyle->fontSize); 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)) if(!ListEmpty(&box->beforeRules))
{ {
debug_print_indent(indent); 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() int main()
{ {
LogLevel(LOG_LEVEL_WARNING); LogLevel(LOG_LEVEL_WARNING);
@ -117,59 +204,184 @@ int main()
return(-1); return(-1);
} }
mg_font font = create_font();
// start app
mp_window_bring_to_front(window);
mp_window_focus(window);
while(!mp_should_quit())
{
bool printDebugStyle = false;
f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC);
mp_pump_events(0);
mp_event event = {0};
while(mp_next_event(&event))
{
switch(event.type)
{
case MP_EVENT_WINDOW_CLOSE:
{
mp_request_quit();
} break;
case MP_EVENT_KEYBOARD_KEY:
{
if(event.key.action == MP_KEY_PRESS && event.key.code == MP_KEY_P)
{
printDebugStyle = true;
}
} break;
default:
break;
}
}
//TEST UI //TEST UI
ui_style defaultStyle = {.bgColor = {0.9, 0.9, 0.9, 1}, 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, .borderSize = 2,
.borderColor = {0, 0, 1, 1}, .borderColor = {0, 0, 1, 1},
.color = {0, 0, 0, 1}, .color = {0, 0, 0, 1},
.font = font,
.fontSize = 32}; .fontSize = 32};
ui_flags defaultFlags = UI_FLAG_DRAW_BORDER;
ui_box* root = 0; ui_box* root = 0;
ui_frame(800, 800, defaultStyle) ui_frame(800, 610, defaultStyle)
{ {
root = ui_box_top(); root = ui_box_top();
// ui_label("Hello, world!");
ui_pattern pattern = {0}; 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_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_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make("foo")});
ui_style_next(pattern, ui_style_next(pattern,
&(ui_style){.fontSize = 12}, &(ui_style){.fontSize = 36},
UI_STYLE_FONT_SIZE); UI_STYLE_FONT_SIZE);
ui_container("a", 0) 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 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_TEXT, .text = str8_lit("b")});
ui_style_next(pattern, ui_style_next(pattern,
&(ui_style){.fontSize = 20}, &(ui_style){.fontSize = 22},
UI_STYLE_FONT_SIZE); UI_STYLE_FONT_SIZE);
ui_container("b", 0) ui_container("b", defaultFlags)
{ {
ui_container("c", 0) ui_container("c", defaultFlags)
{ {
ui_box_make("d", 0); if(ui_button("button d").clicked)
{
printf("clicked button d\n");
}
} }
ui_container("e", 0) ui_container("e", defaultFlags)
{ {
ui_tag_next(str8_lit("foo")); ui_tag_next("foo");
ui_box_make("f", 0); 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(str8_lit("foo")); ui_tag_next("foo");
ui_box_make("d", 0); ui_label("label d");
} }
pattern = (ui_pattern){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("c")});
ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = str8_lit("d")}); ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TAG, .tag = ui_tag_make("button")});
ui_style_prev(pattern, ui_style_prev(pattern,
&(ui_style){.fontSize = 30}, &(ui_style){.bgColor = {1, 0.5, 0.5, 1}},
UI_STYLE_FONT_SIZE); 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); 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());
}
mp_terminate(); mp_terminate();

490
src/ui.c
View File

@ -13,6 +13,22 @@
#define LOG_SUBSYSTEM "UI" #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 // context
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -193,32 +209,32 @@ void ui_box_pop(void)
// tagging // 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)}; ui_tag tag = {.hash = mp_hash_aes_string(string)};
return(tag); 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_context* ui = ui_get_context();
ui_tag_elt* elt = mem_arena_alloc_type(&ui->frameArena, ui_tag_elt); 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); 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_context* ui = ui_get_context();
ui_tag_elt* elt = mem_arena_alloc_type(&ui->frameArena, ui_tag_elt); 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); ListAppend(&ui->nextBoxTags, &elt->listElt);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// key hashing and caching // 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(); ui_context* ui = ui_get_context();
u64 seed = 0; u64 seed = 0;
@ -233,6 +249,23 @@ ui_key ui_key_from_string(str8 string)
return(key); 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) bool ui_key_equal(ui_key a, ui_key b)
{ {
return(a.hash == b.hash); 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); 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); u64 index = key.hash & (UI_BOX_MAP_BUCKET_COUNT-1);
for_each_in_list(&ui->boxMap[index], box, ui_box, bucketElt) 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_box* ui_box_lookup_str8(str8 string)
{ {
ui_context* ui = ui_get_context(); ui_key key = ui_key_make_str8(string);
ui_key key = ui_key_from_string(string); return(ui_box_lookup_key(key));
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)));
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -308,9 +336,58 @@ void ui_style_prev(ui_pattern pattern, ui_style* style, ui_style_mask mask)
*rule->style = *style; *rule->style = *style;
ListAppend(&ui->lastBox->afterRules, &rule->boxElt); 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 // ui boxes
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -363,8 +440,8 @@ ui_box* ui_box_make_str8(str8 string, ui_flags flags)
{ {
ui_context* ui = ui_get_context(); ui_context* ui = ui_get_context();
ui_key key = ui_key_from_string(string); ui_key key = ui_key_make_str8(string);
ui_box* box = ui_box_lookup_with_key(ui, key); ui_box* box = ui_box_lookup_key(key);
if(!box) 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->string = str8_push_copy(&ui->frameArena, string);
box->flags = flags; box->flags = flags;
//NOTE: create style and setup non-inherited attributes to default values
box->targetStyle = mem_arena_alloc_type(&ui->frameArena, ui_style); 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 //NOTE: set tags, before rules and last box
box->tags = ui->nextBoxTags; box->tags = ui->nextBoxTags;
ui->nextBoxTags = (list_info){0}; ui->nextBoxTags = (list_info){0};
box->beforeRules = ui->nextBoxRules; box->beforeRules = ui->nextBoxRules;
for_each_in_list(&box->beforeRules, rule, ui_style_rule, boxElt)
{
rule->owner = box;
}
ui->nextBoxRules = (list_info){0}; ui->nextBoxRules = (list_info){0};
box->afterRules = (list_info){0};
ui->lastBox = box; ui->lastBox = box;
return(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_box* ui_box_begin_str8(str8 string, ui_flags flags)
{ {
ui_context* ui = ui_get_context(); ui_context* ui = ui_get_context();
@ -431,12 +509,6 @@ ui_box* ui_box_begin_str8(str8 string, ui_flags flags)
return(box); 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_box* ui_box_end(void)
{ {
ui_context* ui = ui_get_context(); 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->renderProc = proc;
box->renderData = data; 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) void ui_box_set_closed(ui_box* box, bool closed)
{ {
box->closed = 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) 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 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) 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 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) 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; box->style.roundness = targetStyle->roundness;
} }
//TODO: non animatable attributes use mask
box->style.layout = targetStyle->layout;
box->style.font = targetStyle->font; 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) 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) 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) 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; bool res = false;
switch(selector->kind) switch(selector->kind)
{ {
case UI_SEL_ANY:
res = true;
break;
case UI_SEL_OWNER:
res = (box == rule->owner);
break;
case UI_SEL_TEXT: case UI_SEL_TEXT:
res = !str8_cmp(box->string, selector->text); res = !str8_cmp(box->string, selector->text);
break; break;
@ -763,17 +869,22 @@ bool ui_style_selector_match(ui_box* box, ui_selector* selector)
} }
} break; } break;
case UI_SEL_HOVER: case UI_SEL_STATUS:
res = box->hot; {
break; res = true;
if(selector->status & UI_HOVER)
case UI_SEL_ACTIVE: {
res = box->active; res = res && box->hot;
break; }
if(selector->status & UI_ACTIVE)
case UI_SEL_DRAGGING: {
res = box->dragging; res = res && box->active;
break; }
if(selector->status & UI_DRAGGING)
{
res = res && box->dragging;
}
} break;
case UI_SEL_KEY: case UI_SEL_KEY:
res = ui_key_equal(box->key, selector->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) 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); 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); 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); ui_style_rule* derived = mem_arena_alloc_type(&ui->frameArena, ui_style_rule);
derived->mask = rule->mask; derived->mask = rule->mask;
derived->style = rule->style; 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); ListAppend(buildList, &derived->buildElt);
ListPush(tmpList, &derived->tmpElt); 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) for_each_in_list(&box->beforeRules, rule, ui_style_rule, boxElt)
{ {
ListAppend(before, &rule->buildElt); ListAppend(before, &rule->buildElt);
ListAppend(&tmpBefore, &rule->tmpElt);
ui_style_rule_match(ui, box, rule, before, &tmpBefore); 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) for_each_in_list(&box->afterRules, rule, ui_style_rule, boxElt)
{ {
ListAppend(after, &rule->buildElt); ListAppend(after, &rule->buildElt);
ListAppend(&tmpAfter, &rule->tmpElt);
ui_style_rule_match(ui, box, rule, after, &tmpAfter); 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; ui_style* style = &box->style;
mp_rect textBox = {}; mp_rect textBox = {};
ui_size desiredSize[2] = {box->style.size[UI_AXIS_X], ui_size desiredSize[2] = {box->style.size.s[UI_AXIS_X],
box->style.size[UI_AXIS_Y]}; box->style.size.s[UI_AXIS_Y]};
if( desiredSize[UI_AXIS_X].kind == UI_SIZE_TEXT if( desiredSize[UI_AXIS_X].kind == UI_SIZE_TEXT
||desiredSize[UI_AXIS_Y].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; 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; ui_box* parent = box->parent;
if( 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? //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) void ui_layout_downward_dependent_size(ui_context* ui, ui_box* box, int axis)
{ {
f32 sum = 0; f32 sum = 0;
if(box->style.layout.axis == axis) if(box->style.layout.axis == axis)
{ {
int count = 0;
for_each_in_list(&box->children, child, ui_box, listElt) for_each_in_list(&box->children, child, ui_box, listElt)
{ {
if(!ui_box_hidden(child)) 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]) if(!child->style.floating[axis])
{ {
sum += child->rect.c[2+axis]; sum += child->rect.c[2+axis];
count++;
} }
} }
} }
sum += maximum(0, count-1)*box->style.layout.spacing;
} }
else else
{ {
@ -950,10 +1077,11 @@ void ui_layout_downward_dependent_size(ui_context* ui, ui_box* box, int axis)
box->childrenSum[axis] = sum; 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) 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 layoutAxis = box->style.layout.axis;
ui_axis secondAxis = (layoutAxis == UI_AXIS_X) ? UI_AXIS_Y : UI_AXIS_X; 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, vec2 origin = {box->rect.x - box->scroll.x,
box->rect.y - box->scroll.y}; box->rect.y - box->scroll.y};
vec2 currentPos = origin; vec2 currentPos = origin;
vec2 margin = {0, 0}; vec2 margin = {box->style.layout.margin.x,
for(int i=0; i<UI_AXIS_COUNT; i++) box->style.layout.margin.y};
{
if(box->style.size[i].kind == UI_SIZE_CHILDREN)
{
margin.c[i] = box->style.size[i].value;
}
}
currentPos.x += margin.x; currentPos.x += margin.x;
currentPos.y += margin.y; 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]) 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) 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) if(box->flags & UI_FLAG_DRAW_BACKGROUND)
@ -1165,7 +1290,7 @@ void ui_draw_box(ui_box* box)
if(box->flags & UI_FLAG_CLIP) if(box->flags & UI_FLAG_CLIP)
{ {
mg_clip_pop(); // mg_clip_pop();
} }
if(box->flags & UI_FLAG_DRAW_BORDER) 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->clipStack = 0;
ui->z = 0; ui->z = 0;
// ui_push_size(UI_AXIS_X, UI_SIZE_PIXELS, width, 0); defaultStyle.size.s[UI_AXIS_X] = (ui_size){UI_SIZE_PIXELS, width, 0};
// ui_push_size(UI_AXIS_Y, UI_SIZE_PIXELS, height, 0); defaultStyle.size.s[UI_AXIS_Y] = (ui_size){UI_SIZE_PIXELS, height, 0};
ui->root = ui_box_begin("_root_", 0); ui->root = ui_box_begin("_root_", 0);
*ui->root->targetStyle = defaultStyle; *ui->root->targetStyle = defaultStyle;
ui_box* contents = ui_box_make("_contents_", 0); ui_style_next(ui_pattern_owner(),
// ui_box_set_floating(contents, UI_AXIS_X, 0); &(ui_style){.layout = {UI_AXIS_Y, UI_ALIGN_START, UI_ALIGN_START},
// ui_box_set_floating(contents, UI_AXIS_Y, 0); .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->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->nextBoxRules = (list_info){0};
ui->nextBoxTags = (list_info){0}; ui->nextBoxTags = (list_info){0};
@ -1300,11 +1428,14 @@ void ui_cleanup(void)
ui_sig ui_label_str8(str8 label) 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_flags flags = UI_FLAG_CLIP
| UI_FLAG_DRAW_TEXT; | UI_FLAG_DRAW_TEXT;
ui_box* box = ui_box_make_str8(label, flags); 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); ui_sig sig = ui_box_sig(box);
return(sig); return(sig);
@ -1317,18 +1448,48 @@ ui_sig ui_label(const char* label)
ui_sig ui_button_str8(str8 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_flags flags = UI_FLAG_CLICKABLE
| UI_FLAG_CLIP | UI_FLAG_CLIP
| UI_FLAG_DRAW_FOREGROUND | UI_FLAG_DRAW_BACKGROUND
| UI_FLAG_DRAW_BORDER | UI_FLAG_DRAW_BORDER
| UI_FLAG_DRAW_TEXT | UI_FLAG_DRAW_TEXT
| UI_FLAG_HOT_ANIMATION | UI_FLAG_HOT_ANIMATION
| UI_FLAG_ACTIVE_ANIMATION; | UI_FLAG_ACTIVE_ANIMATION;
ui_box* box = ui_box_make_str8(label, flags); 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); ui_sig sig = ui_box_sig(box);
if(sig.hovering) if(sig.hovering)
@ -1356,6 +1517,112 @@ ui_sig ui_button(const char* label)
return(ui_button_str8(str8_from_cstring((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* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue)
{ {
ui_box* frame = ui_box_begin(label, 0); 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 roundness = 0.5*frame->rect.c[2+secondAxis];
f32 animationTime = 0.5; f32 animationTime = 0.5;
/* ui_push_bg_color((mg_color){0, 0, 0, 0}); /* ui_push_bg_color((mg_color){0, 0, 0, 0});
ui_push_fg_color((mg_color){0, 0, 0, 0}); ui_push_fg_color((mg_color){0, 0, 0, 0});
ui_push_roundness(roundness); 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* track = ui_box_begin("track", trackFlags);
// ui_box_set_size(track, trackAxis, 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_RATIO, 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); //ui_box_set_layout(track, trackAxis, UI_ALIGN_START, UI_ALIGN_START);
f32 beforeRatio = (*scrollValue) * (1. - thumbRatio); f32 beforeRatio = (*scrollValue) * (1. - thumbRatio);
f32 afterRatio = (1. - *scrollValue) * (1. - thumbRatio); f32 afterRatio = (1. - *scrollValue) * (1. - thumbRatio);
ui_box* beforeSpacer = ui_box_make("before", 0); 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, trackAxis, UI_SIZE_PARENT, beforeRatio, 0);
// ui_box_set_size(beforeSpacer, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); // ui_box_set_size(beforeSpacer, secondAxis, UI_SIZE_PARENT, 1., 0);
ui_flags thumbFlags = UI_FLAG_CLICKABLE ui_flags thumbFlags = UI_FLAG_CLICKABLE
| UI_FLAG_DRAW_FOREGROUND | UI_FLAG_DRAW_FOREGROUND
@ -1400,12 +1668,12 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue)
| UI_FLAG_ACTIVE_ANIMATION; | UI_FLAG_ACTIVE_ANIMATION;
ui_box* thumb = ui_box_make("thumb", thumbFlags); 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, trackAxis, UI_SIZE_PARENT, thumbRatio, 0);
// ui_box_set_size(thumb, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); // ui_box_set_size(thumb, secondAxis, UI_SIZE_PARENT, 1., 0);
ui_box* afterSpacer = ui_box_make("after", 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, trackAxis, UI_SIZE_PARENT, afterRatio, 0);
// ui_box_set_size(afterSpacer, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0); // ui_box_set_size(afterSpacer, secondAxis, UI_SIZE_PARENT, 1., 0);
ui_box_end(); ui_box_end();
/* /*
@ -1500,7 +1768,7 @@ void ui_panel_end()
f32 sliderX = panel->scroll.x /(contentsW - panel->rect.w); f32 sliderX = panel->scroll.x /(contentsW - panel->rect.w);
scrollBarX = ui_scrollbar("scrollerX", thumbRatioX, &sliderX); 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); // ui_box_set_size(scrollBarX, UI_AXIS_Y, UI_SIZE_PIXELS, 10, 0);
panel->scroll.x = sliderX * (contentsW - panel->rect.w); panel->scroll.x = sliderX * (contentsW - panel->rect.w);
@ -1518,7 +1786,7 @@ void ui_panel_end()
scrollBarY = ui_scrollbar("scrollerY", thumbRatioY, &sliderY); 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_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); panel->scroll.y = sliderY * (contentsH - panel->rect.h);
if(sig.hovering) if(sig.hovering)
@ -1575,7 +1843,7 @@ void ui_tooltip_end(void)
void ui_menu_bar_begin(const char* name) void ui_menu_bar_begin(const char* name)
{ {
ui_box* bar = ui_box_begin(name, UI_FLAG_DRAW_BACKGROUND); 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_size(bar, UI_AXIS_Y, UI_SIZE_CHILDREN, 0, 0);
//ui_box_set_layout(bar, UI_AXIS_X, UI_ALIGN_START, UI_ALIGN_START); //ui_box_set_layout(bar, UI_AXIS_X, UI_ALIGN_START, UI_ALIGN_START);
/* /*

175
src/ui.h
View File

@ -52,19 +52,39 @@ typedef enum
UI_ALIGN_CENTER, UI_ALIGN_CENTER,
} ui_align; } 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 typedef struct ui_layout
{ {
ui_axis axis; 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; } ui_layout;
typedef enum typedef enum ui_size_kind
{ {
UI_SIZE_TEXT, UI_SIZE_TEXT,
UI_SIZE_PIXELS, UI_SIZE_PIXELS,
UI_SIZE_CHILDREN, UI_SIZE_CHILDREN,
UI_SIZE_PARENT_RATIO, UI_SIZE_PARENT,
} ui_size_kind; } ui_size_kind;
@ -75,6 +95,16 @@ typedef struct ui_size
f32 strictness; f32 strictness;
} ui_size; } 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, typedef enum { UI_STYLE_ANIMATE_SIZE_X = 1<<1,
UI_STYLE_ANIMATE_SIZE_Y = 1<<2, UI_STYLE_ANIMATE_SIZE_Y = 1<<2,
UI_STYLE_ANIMATE_COLOR = 1<<3, UI_STYLE_ANIMATE_COLOR = 1<<3,
@ -88,7 +118,7 @@ typedef enum { UI_STYLE_ANIMATE_SIZE_X = 1<<1,
typedef struct ui_style typedef struct ui_style
{ {
ui_size size[UI_AXIS_COUNT]; ui_box_size size;
ui_layout layout; ui_layout layout;
bool floating[UI_AXIS_COUNT]; bool floating[UI_AXIS_COUNT];
vec2 floatTarget; vec2 floatTarget;
@ -109,16 +139,31 @@ enum
UI_STYLE_NONE = 0, UI_STYLE_NONE = 0,
UI_STYLE_SIZE_X = 1<<1, UI_STYLE_SIZE_X = 1<<1,
UI_STYLE_SIZE_Y = 1<<2, UI_STYLE_SIZE_Y = 1<<2,
UI_STYLE_COLOR = 1<<3, UI_STYLE_LAYOUT_AXIS = 1<<3,
UI_STYLE_BG_COLOR = 1<<4, UI_STYLE_LAYOUT_ALIGN_X = 1<<4,
UI_STYLE_BORDER_COLOR = 1<<6, UI_STYLE_LAYOUT_ALIGN_Y = 1<<5,
UI_STYLE_BORDER_SIZE = 1<<7, UI_STYLE_LAYOUT_SPACING = 1<<6,
UI_STYLE_ROUNDNESS = 1<<8, UI_STYLE_LAYOUT_MARGIN_X = 1<<7,
UI_STYLE_FONT = 1<<9, UI_STYLE_LAYOUT_MARGIN_Y = 1<<8,
UI_STYLE_FONT_SIZE = 1<<10, UI_STYLE_FLOAT_X = 1<<9,
UI_STYLE_ANIMATION_TIME = 1<<11, UI_STYLE_FLOAT_Y = 1<<10,
UI_STYLE_ANIMATION_FLAGS = 1<<12, 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_MASK_INHERITED = UI_STYLE_COLOR
| UI_STYLE_FONT | UI_STYLE_FONT
@ -132,43 +177,62 @@ typedef struct ui_tag { u64 hash; } ui_tag;
typedef enum typedef enum
{ {
UI_SEL_ANY,
UI_SEL_OWNER,
UI_SEL_TEXT, UI_SEL_TEXT,
UI_SEL_TAG, UI_SEL_TAG,
UI_SEL_HOVER, UI_SEL_STATUS,
UI_SEL_ACTIVE,
UI_SEL_DRAGGING,
UI_SEL_KEY, UI_SEL_KEY,
//... //...
} ui_selector_kind; } 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 typedef struct ui_selector
{ {
list_elt listElt; list_elt listElt;
ui_selector_kind kind; ui_selector_kind kind;
ui_selector_op op;
union union
{ {
str8 text; str8 text;
ui_key key; ui_key key;
ui_tag tag; ui_tag tag;
ui_status status;
//... //...
}; };
} ui_selector; } ui_selector;
typedef struct ui_pattern { list_info l; } ui_pattern; typedef struct ui_pattern { list_info l; } ui_pattern;
typedef struct ui_box ui_box;
typedef struct ui_style_rule typedef struct ui_style_rule
{ {
list_elt boxElt; list_elt boxElt;
list_elt buildElt; list_elt buildElt;
list_elt tmpElt; list_elt tmpElt;
ui_box* owner;
ui_pattern pattern; ui_pattern pattern;
ui_style_mask mask; ui_style_mask mask;
ui_style* style; ui_style* style;
} ui_style_rule; } ui_style_rule;
typedef struct ui_box ui_box;
typedef struct ui_sig typedef struct ui_sig
{ {
ui_box* box; ui_box* box;
@ -242,22 +306,38 @@ struct ui_box
typedef struct ui_context ui_context; typedef struct ui_context ui_context;
//-------------------------------------------------------------------------------------
// UI context initialization and frame cycle
//-------------------------------------------------------------------------------------
void ui_init(void); void ui_init(void);
ui_context* ui_get_context(void); ui_context* ui_get_context(void);
void ui_set_context(ui_context* context); void ui_set_context(ui_context* context);
void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle); void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle);
void ui_end_frame(void); 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); 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_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_make_str8(str8 string, ui_flags flags);
ui_box* ui_box_begin_str8(str8 string, ui_flags flags); ui_box* ui_box_begin_str8(str8 string, ui_flags flags);
ui_box* ui_box_end(void); ui_box* ui_box_end(void);
#define ui_container(name, flags) defer_loop(ui_box_begin(name, flags), ui_box_end()) #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); void ui_box_pop(void);
ui_box* ui_box_top(void); ui_box* ui_box_top(void);
ui_tag ui_tag_make(str8 string); void ui_box_set_render_proc(ui_box* box, ui_box_render_proc proc, void* data);
void ui_tag_box(ui_box* box, str8 string);
void ui_tag_next(str8 string);
// 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); bool ui_box_closed(ui_box* box);
void ui_box_set_closed(ui_box* box, bool closed); 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); bool ui_box_hot(ui_box* box);
void ui_box_set_hot(ui_box* box, bool hot); 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); 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 //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, //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! // 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_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); 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 // Basic widget helpers
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -309,10 +412,10 @@ enum {
}; };
ui_sig ui_label(const char* label); 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(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_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue);
ui_box* ui_panel_begin(const char* name); ui_box* ui_panel_begin(const char* name);