[ui, styling] First draft of rule-based styling to replace old stack-based system

This commit is contained in:
Martin Fouilleul 2023-03-06 19:33:01 +01:00
parent b52a35c753
commit 7b10a99a8c
6 changed files with 567 additions and 331 deletions

View File

@ -122,7 +122,7 @@ int main()
.font = font,
.fontSize = 32};
ui_begin_frame(frame.w, frame.h, defaultStyle);
ui_frame(frame.w, frame.h, defaultStyle)
{
ui_push_size(UI_AXIS_X, UI_SIZE_CHILDREN, 10, 0);
ui_push_size(UI_AXIS_Y, UI_SIZE_CHILDREN, 10, 0);
@ -173,7 +173,7 @@ int main()
ui_pop_size(UI_AXIS_X);
ui_pop_size(UI_AXIS_Y);
}
ui_end_frame();
ui_draw();
mg_flush();

View File

@ -0,0 +1,4 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/test_ui.exe

11
examples/ui_style_test/build.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
BINDIR=../../bin
RESDIR=../../resources
SRCDIR=../../src
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
LIBS="-L$BINDIR -lmilepost"
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG"
clang -g $FLAGS $LIBS $INCLUDES -o $BINDIR/test_ui main.c

View File

@ -0,0 +1,177 @@
/************************************************************//**
*
* @file: main.cpp
* @author: Martin Fouilleul
* @date: 30/07/2022
* @revision:
*
*****************************************************************/
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
#include<math.h>
#include"milepost.h"
#define LOG_SUBSYSTEM "Main"
void debug_print_indent(int indent)
{
for(int i=0; i<indent; i++)
{
printf(" ");
}
}
void debug_print_rule(ui_style_rule* rule)
{
for_each_in_list(&rule->pattern.l, selector, ui_selector, listElt)
{
switch(selector->kind)
{
case UI_SEL_TEXT:
printf("text='%.*s': ", (int)selector->text.len, selector->text.ptr);
break;
case UI_SEL_TAG:
printf("tag=0x%llx: ", selector->tag.hash);
break;
default:
printf("unknown: ");
break;
}
}
printf("=> font size = %f\n", rule->style->fontSize);
}
void debug_print_styles(ui_box* box, int indent)
{
debug_print_indent(indent);
printf("### box '%.*s'\n", (int)box->string.len, box->string.ptr);
indent++;
debug_print_indent(indent);
printf("font size: %f\n", box->targetStyle->fontSize);
if(!ListEmpty(&box->beforeRules))
{
debug_print_indent(indent);
printf("before rules:\n");
for_each_in_list(&box->beforeRules, rule, ui_style_rule, boxElt)
{
debug_print_indent(indent+1);
debug_print_rule(rule);
}
}
if(!ListEmpty(&box->afterRules))
{
debug_print_indent(indent);
printf("after rules:\n");
for_each_in_list(&box->afterRules, rule, ui_style_rule, boxElt)
{
debug_print_indent(indent+1);
debug_print_rule(rule);
}
}
if(!ListEmpty(&box->children))
{
debug_print_indent(indent);
printf("children:\n");
indent++;
for_each_in_list(&box->children, child, ui_box, listElt)
{
debug_print_styles(child, indent);
}
}
}
int main()
{
LogLevel(LOG_LEVEL_WARNING);
mp_init();
mp_clock_init(); //TODO put that in mp_init()?
ui_init();
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
mp_window window = mp_window_create(windowRect, "test", 0);
mp_rect contentRect = mp_window_get_content_rect(window);
//NOTE: create surface
mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT);
mg_surface_swap_interval(surface, 0);
//TODO: create canvas
mg_canvas canvas = mg_canvas_create(surface);
if(mg_canvas_is_nil(canvas))
{
printf("Error: couldn't create canvas\n");
return(-1);
}
//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};
ui_box* root = 0;
ui_frame(800, 800, defaultStyle)
{
root = ui_box_top();
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);
ui_container("a", 0)
{
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)
{
ui_container("c", 0)
{
ui_box_make("d", 0);
}
ui_container("e", 0)
{
ui_tag_next(str8_lit("foo"));
ui_box_make("f", 0);
}
}
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);
}
debug_print_styles(root, 0);
mp_terminate();
return(0);
}

557
src/ui.c
View File

@ -26,20 +26,6 @@ typedef struct ui_input_text
} ui_input_text;
typedef struct ui_style_attr
{
ui_style_tag tag;
ui_style_selector selector;
union
{
ui_size size;
mg_color color;
mg_font font;
f32 value;
u32 flags;
};
} ui_style_attr;
typedef struct ui_stack_elt ui_stack_elt;
struct ui_stack_elt
{
@ -49,10 +35,15 @@ struct ui_stack_elt
ui_box* box;
ui_size size;
mp_rect clip;
ui_style_attr attr;
};
};
typedef struct ui_tag_elt
{
list_elt listElt;
ui_tag tag;
} ui_tag_elt;
const u64 UI_BOX_MAP_BUCKET_COUNT = 1024;
typedef struct ui_context
@ -75,17 +66,10 @@ typedef struct ui_context
ui_stack_elt* boxStack;
ui_stack_elt* clipStack;
ui_stack_elt* sizeStack[UI_AXIS_COUNT];
ui_stack_elt* bgColorStack;
ui_stack_elt* fgColorStack;
ui_stack_elt* borderColorStack;
ui_stack_elt* fontColorStack;
ui_stack_elt* fontStack;
ui_stack_elt* fontSizeStack;
ui_stack_elt* borderSizeStack;
ui_stack_elt* roundnessStack;
ui_stack_elt* animationTimeStack;
ui_stack_elt* animationFlagsStack;
list_info nextBoxRules;
ui_box* lastBox;
list_info nextBoxTags;
u32 z;
ui_box* hovered;
@ -205,167 +189,30 @@ void ui_box_pop(void)
}
}
ui_style_attr* ui_push_style_attr(ui_context* ui, ui_stack_elt** stack)
//-----------------------------------------------------------------------------
// tagging
//-----------------------------------------------------------------------------
ui_tag ui_tag_make(str8 string)
{
ui_stack_elt* elt = ui_stack_push(ui, stack);
return(&elt->attr);
ui_tag tag = {.hash = mp_hash_aes_string(string)};
return(tag);
}
void ui_push_size(ui_axis axis, ui_size_kind kind, f32 value, f32 strictness)
{
ui_push_size_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, axis, kind, value, strictness);
}
void ui_push_size_ext(ui_style_tag tag, ui_style_selector selector, ui_axis axis, ui_size_kind kind, f32 value, f32 strictness)
void ui_tag_box(ui_box* box, str8 string)
{
ui_context* ui = ui_get_context();
ui_style_attr* attr = ui_push_style_attr(ui, &ui->sizeStack[axis]);
attr->tag = tag;
attr->selector = selector;
attr->size = (ui_size){kind, value, strictness};
ui_tag_elt* elt = mem_arena_alloc_type(&ui->frameArena, ui_tag_elt);
elt->tag = ui_tag_make(string);
ListAppend(&box->tags, &elt->listElt);
}
void ui_pop_size(ui_axis axis)
void ui_tag_next(str8 string)
{
ui_context* ui = ui_get_context();
ui_stack_pop(&ui->sizeStack[axis]);
}
#define UI_STYLE_STACK_OP_DEF(name, stack, type, arg) \
void _cat3_(ui_push_, name, _ext)(ui_style_tag tag, ui_style_selector selector, type arg) \
{ \
ui_context* ui = ui_get_context(); \
ui_style_attr* attr = ui_push_style_attr(ui, &ui->stack); \
attr->tag = tag; \
attr->selector = selector; \
attr->arg = arg; \
} \
void _cat2_(ui_push_, name)(type arg) \
{ \
_cat3_(ui_push_, name, _ext)(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, arg); \
} \
void _cat2_(ui_pop_, name)(void) \
{ \
ui_context* ui = ui_get_context(); \
ui_stack_pop(&ui->stack); \
}
UI_STYLE_STACK_OP_DEF(bg_color, bgColorStack, mg_color, color)
UI_STYLE_STACK_OP_DEF(fg_color, fgColorStack, mg_color, color)
UI_STYLE_STACK_OP_DEF(border_color, borderColorStack, mg_color, color)
UI_STYLE_STACK_OP_DEF(font_color, fontColorStack, mg_color, color)
UI_STYLE_STACK_OP_DEF(font, fontStack, mg_font, font)
UI_STYLE_STACK_OP_DEF(font_size, fontSizeStack, f32, value)
UI_STYLE_STACK_OP_DEF(border_size, borderSizeStack, f32, value)
UI_STYLE_STACK_OP_DEF(roundness, roundnessStack, f32, value)
UI_STYLE_STACK_OP_DEF(animation_time, animationTimeStack, f32, value)
UI_STYLE_STACK_OP_DEF(animation_flags, animationFlagsStack, u32, flags)
ui_style_attr* ui_style_attr_query(ui_stack_elt* stack, ui_style_tag tag, ui_style_selector selector)
{
ui_style_attr* result = 0;
while(stack)
{
ui_style_attr* attr = &stack->attr;
bool matchTag = (attr->tag == UI_STYLE_TAG_ANY)
||(attr->tag == tag);
bool matchSel = attr->selector & selector;
if(matchTag && matchSel)
{
result = attr;
break;
}
stack = stack->parent;
}
return(result);
}
mg_color ui_style_color_query(ui_stack_elt* stack, ui_style_tag tag, ui_style_selector selector)
{
ui_style_attr* attr = ui_style_attr_query(stack, tag, selector);
if(attr)
{
return(attr->color);
}
else
{
return((mg_color){});
}
}
mg_font ui_style_font_query(ui_stack_elt* stack, ui_style_tag tag, ui_style_selector selector)
{
ui_style_attr* attr = ui_style_attr_query(stack, tag, selector);
if(attr)
{
return(attr->font);
}
else
{
return(mg_font_nil());
}
}
f32 ui_style_float_query(ui_stack_elt* stack, ui_style_tag tag, ui_style_selector selector)
{
ui_style_attr* attr = ui_style_attr_query(stack, tag, selector);
if(attr)
{
return(attr->value);
}
else
{
return(0);
}
}
u32 ui_style_flags_query(ui_stack_elt* stack, ui_style_tag tag, ui_style_selector selector)
{
ui_style_attr* attr = ui_style_attr_query(stack, tag, selector);
if(attr)
{
return(attr->flags);
}
else
{
return(0);
}
}
ui_size ui_style_size_query(ui_stack_elt* stack, ui_style_tag tag, ui_style_selector selector)
{
ui_style_attr* attr = ui_style_attr_query(stack, tag, selector);
if(attr)
{
return(attr->size);
}
else
{
return((ui_size){});
}
}
ui_style* ui_collate_style(ui_context* context, ui_style_tag tag, ui_style_selector selector)
{
ui_style* style = mem_arena_alloc_type(&context->frameArena, ui_style);
style->size[UI_AXIS_X] = ui_style_size_query(context->sizeStack[UI_AXIS_X], tag, selector);
style->size[UI_AXIS_Y] = ui_style_size_query(context->sizeStack[UI_AXIS_Y], tag, selector);
style->bgColor = ui_style_color_query(context->bgColorStack, tag, selector);
style->fgColor = ui_style_color_query(context->fgColorStack, tag, selector);
style->borderColor = ui_style_color_query(context->borderColorStack, tag, selector);
style->fontColor = ui_style_color_query(context->fontColorStack, tag, selector);
style->font = ui_style_font_query(context->fontStack, tag, selector);
style->fontSize = ui_style_float_query(context->fontSizeStack, tag, selector);
style->borderSize = ui_style_float_query(context->borderSizeStack, tag, selector);
style->roundness = ui_style_float_query(context->roundnessStack, tag, selector);
style->animationTime = ui_style_float_query(context->animationTimeStack, tag, selector);
style->animationFlags = ui_style_flags_query(context->animationFlagsStack, tag, selector);
return(style);
ui_tag_elt* elt = mem_arena_alloc_type(&ui->frameArena, ui_tag_elt);
elt->tag = ui_tag_make(string);
ListAppend(&ui->nextBoxTags, &elt->listElt);
}
//-----------------------------------------------------------------------------
@ -423,6 +270,47 @@ ui_box* ui_box_lookup(const char* string)
return(ui_box_lookup_str8(str8_from_cstring((char*)string)));
}
//-----------------------------------------------------------------------------
// styling
//-----------------------------------------------------------------------------
void ui_pattern_push(mem_arena* arena, ui_pattern* pattern, ui_selector selector)
{
ui_selector* copy = mem_arena_alloc_type(arena, ui_selector);
*copy = selector;
ListAppend(&pattern->l, &copy->listElt);
}
void ui_style_next(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(&ui->nextBoxRules, &rule->boxElt);
}
}
void ui_style_prev(ui_pattern pattern, ui_style* style, ui_style_mask mask)
{
ui_context* ui = ui_get_context();
if(ui && ui->lastBox)
{
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(&ui->lastBox->afterRules, &rule->boxElt);
}
}
//-----------------------------------------------------------------------------
// ui boxes
//-----------------------------------------------------------------------------
@ -518,17 +406,21 @@ ui_box* ui_box_make_str8(str8 string, ui_flags flags)
box->floating[UI_AXIS_Y] = false;
box->layout = box->parent ? box->parent->layout : (ui_layout){0};
//NOTE: compute style
ui_style_selector selector = UI_STYLE_SEL_NORMAL;
if(box->hot)
//TODO: inherit style with mask
box->targetStyle = mem_arena_alloc_type(&ui->frameArena, ui_style);
if(box->parent)
{
selector = UI_STYLE_SEL_HOT;
*box->targetStyle = *(box->parent->targetStyle);
}
if(box->active)
{
selector = UI_STYLE_SEL_ACTIVE;
}
box->targetStyle = ui_collate_style(ui, box->tag, selector);
//NOTE: set tags, before rules and last box
box->tags = ui->nextBoxTags;
ui->nextBoxTags = (list_info){0};
box->beforeRules = ui->nextBoxRules;
ui->nextBoxRules = (list_info){0};
ui->lastBox = box;
return(box);
}
@ -560,6 +452,10 @@ ui_box* ui_box_end(void)
DEBUG_ASSERT(box, "box stack underflow");
ui_box_pop();
//NOTE: set last box so that subsequent ui_style_prev() targets this box
ui->lastBox = box;
return(box);
}
@ -569,16 +465,16 @@ void ui_box_set_render_proc(ui_box* box, ui_box_render_proc proc, void* data)
box->renderData = data;
}
void ui_box_set_layout(ui_box* box, ui_axis axis, ui_align alignX, ui_align alignY)
{
box->layout = (ui_layout){axis, {alignX, alignY}};
}
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_layout(ui_box* box, ui_axis axis, ui_align alignX, ui_align alignY)
{
box->layout = (ui_layout){axis, {alignX, alignY}};
}
void ui_box_set_floating(ui_box* box, ui_axis axis, f32 pos)
{
box->floating[axis] = true;
@ -620,11 +516,6 @@ bool ui_box_hot(ui_box* box)
return(box->hot);
}
void ui_box_set_tag(ui_box* box, ui_style_tag tag)
{
box->tag = tag;
}
ui_sig ui_box_sig(ui_box* box)
{
//NOTE: compute input signals
@ -757,6 +648,16 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box)
box->computedStyle.size[UI_AXIS_Y] = targetStyle->size[UI_AXIS_Y];
}
if(flags & UI_STYLE_ANIMATE_COLOR)
{
ui_animate_color(ui, &box->computedStyle.color, targetStyle->color, animationTime);
}
else
{
box->computedStyle.color = targetStyle->color;
}
if(flags & UI_STYLE_ANIMATE_BG_COLOR)
{
ui_animate_color(ui, &box->computedStyle.bgColor, targetStyle->bgColor, animationTime);
@ -766,15 +667,6 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box)
box->computedStyle.bgColor = targetStyle->bgColor;
}
if(flags & UI_STYLE_ANIMATE_FG_COLOR)
{
ui_animate_color(ui, &box->computedStyle.fgColor, targetStyle->fgColor, animationTime);
}
else
{
box->computedStyle.fgColor = targetStyle->fgColor;
}
if(flags & UI_STYLE_ANIMATE_BORDER_COLOR)
{
ui_animate_color(ui, &box->computedStyle.borderColor, targetStyle->borderColor, animationTime);
@ -784,15 +676,6 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box)
box->computedStyle.borderColor = targetStyle->borderColor;
}
if(flags & UI_STYLE_ANIMATE_FONT_COLOR)
{
ui_animate_color(ui, &box->computedStyle.fontColor, targetStyle->fontColor, animationTime);
}
else
{
box->computedStyle.fontColor = targetStyle->fontColor;
}
if(flags & UI_STYLE_ANIMATE_FONT_SIZE)
{
ui_animate_f32(ui, &box->computedStyle.fontSize, targetStyle->fontSize, animationTime);
@ -823,16 +706,158 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box)
}
}
void ui_layout_prepass(ui_context* ui, ui_box* box)
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];
}
if(mask & UI_STYLE_SIZE_Y)
{
dst->size[UI_AXIS_Y] = src->size[UI_AXIS_Y];
}
if(mask & UI_STYLE_COLOR)
{
dst->color = src->color;
}
if(mask & UI_STYLE_BG_COLOR)
{
dst->bgColor = src->bgColor;
}
if(mask & UI_STYLE_BORDER_COLOR)
{
dst->borderColor = src->borderColor;
}
if(mask & UI_STYLE_BORDER_SIZE)
{
dst->borderSize = src->borderSize;
}
if(mask & UI_STYLE_ROUNDNESS)
{
dst->roundness = src->roundness;
}
if(mask & UI_STYLE_FONT)
{
dst->font = src->font;
}
if(mask & UI_STYLE_FONT_SIZE)
{
dst->fontSize = src->fontSize;
}
if(mask & UI_STYLE_ANIMATION_TIME)
{
dst->animationTime = src->animationTime;
}
if(mask & UI_STYLE_ANIMAION_FLAGS)
{
dst->animationFlags = src->animationFlags;
}
}
bool ui_style_selector_match(ui_box* box, ui_selector* selector)
{
bool res = false;
switch(selector->kind)
{
case UI_SEL_TEXT:
res = !str8_cmp(box->string, selector->text);
break;
case UI_SEL_TAG:
{
for_each_in_list(&box->tags, elt, ui_tag_elt, listElt)
{
if(elt->tag.hash == selector->tag.hash)
{
res = true;
break;
}
}
} 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_KEY:
res = ui_key_equal(box->key, selector->key);
default:
break;
}
return(res);
}
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))
{
if(!selector->listElt.next)
{
ui_apply_style_with_mask(box->targetStyle, rule->style, rule->mask);
}
else
{
//NOTE create derived rule if there's more than one selector
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};
ListPush(buildList, &derived->buildElt);
ListPush(tmpList, &derived->tmpElt);
}
}
}
void ui_styling_prepass(ui_context* ui, ui_box* box, list_info* before, list_info* after)
{
//NOTE: inherit style from parent
if(box->parent)
{
*box->targetStyle = *box->parent->targetStyle;
}
//NOTE: match rules
list_info tmpBefore = {0};
for_each_in_list(before, rule, ui_style_rule, buildElt)
{
ui_style_rule_match(ui, box, rule, before, &tmpBefore);
}
for_each_in_list(&box->beforeRules, rule, ui_style_rule, boxElt)
{
ListAppend(before, &rule->buildElt);
ui_style_rule_match(ui, box, rule, before, &tmpBefore);
}
list_info tmpAfter = {0};
for_each_in_list(after, rule, ui_style_rule, buildElt)
{
ui_style_rule_match(ui, box, rule, after, &tmpAfter);
}
for_each_in_list(&box->afterRules, rule, ui_style_rule, boxElt)
{
ListAppend(after, &rule->buildElt);
ui_style_rule_match(ui, box, rule, after, &tmpAfter);
}
//NOTE: compute static sizes
ui_box_compute_styling(ui, box);
if(ui_box_hidden(box))
{
return;
}
//NOTE: compute styling and static sizes
ui_box_compute_styling(ui, box);
ui_style* style = &box->computedStyle;
mp_rect textBox = {};
@ -859,9 +884,20 @@ void ui_layout_prepass(ui_context* ui, ui_box* box)
}
}
//NOTE: descend in children
for_each_in_list(&box->children, child, ui_box, listElt)
{
ui_layout_prepass(ui, child);
ui_styling_prepass(ui, child, before, after);
}
//NOTE: remove temporary rules
for_each_in_list(&tmpBefore, rule, ui_style_rule, tmpElt)
{
ListRemove(before, &rule->buildElt);
}
for_each_in_list(&tmpAfter, rule, ui_style_rule, tmpElt)
{
ListRemove(after, &rule->buildElt);
}
}
@ -1047,7 +1083,10 @@ void ui_layout_find_next_hovered(ui_context* ui, vec2 p)
void ui_solve_layout(ui_context* ui)
{
ui_layout_prepass(ui, ui->root);
list_info beforeRules = {0};
list_info afterRules = {0};
ui_styling_prepass(ui, ui->root, &beforeRules, &afterRules);
for(int axis=0; axis<UI_AXIS_COUNT; axis++)
{
@ -1119,12 +1158,6 @@ void ui_draw_box(ui_box* box)
ui_draw_box(child);
}
if(box->flags & UI_FLAG_DRAW_FOREGROUND)
{
mg_set_color(style->fgColor);
ui_rectangle_fill(box->rect, style->roundness);
}
if(box->flags & UI_FLAG_DRAW_TEXT)
{
mp_rect textBox = mg_text_bounding_box(style->font, style->fontSize, box->string);
@ -1134,7 +1167,7 @@ void ui_draw_box(ui_box* box)
mg_set_font(style->font);
mg_set_font_size(style->fontSize);
mg_set_color(style->fontColor);
mg_set_color(style->color);
mg_move_to(x, y);
mg_text_outlines(box->string);
@ -1191,43 +1224,14 @@ void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle)
ui->lastFrameDuration = time - ui->frameTime;
ui->frameTime = time;
ui->boxStack = 0;
ui->sizeStack[UI_AXIS_X] = 0;
ui->sizeStack[UI_AXIS_Y] = 0;
ui->bgColorStack = 0;
ui->fgColorStack = 0;
ui->borderColorStack = 0;
ui->fontColorStack = 0;
ui->fontStack = 0;
ui->fontSizeStack = 0;
ui->borderSizeStack = 0;
ui->roundnessStack = 0;
ui->clipStack = 0;
ui->z = 0;
for(int i=0; i<UI_AXIS_COUNT; i++)
{
ui_push_size(i,
defaultStyle.size[i].kind,
defaultStyle.size[i].value,
defaultStyle.size[i].strictness);
}
ui_push_bg_color(defaultStyle.bgColor);
ui_push_fg_color(defaultStyle.fgColor);
ui_push_border_color(defaultStyle.borderColor);
ui_push_font_color(defaultStyle.fontColor);
ui_push_font(defaultStyle.font);
ui_push_font_size(defaultStyle.fontSize);
ui_push_border_size(defaultStyle.borderSize);
ui_push_roundness(defaultStyle.roundness);
ui_push_size(UI_AXIS_X, UI_SIZE_PIXELS, width, 0);
ui_push_size(UI_AXIS_Y, UI_SIZE_PIXELS, height, 0);
// ui_push_size(UI_AXIS_X, UI_SIZE_PIXELS, width, 0);
// ui_push_size(UI_AXIS_Y, 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);
@ -1238,8 +1242,11 @@ void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle)
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_pop_size(UI_AXIS_X);
// ui_pop_size(UI_AXIS_Y);
ui->nextBoxRules = (list_info){0};
ui->nextBoxTags = (list_info){0};
ui_box_push(contents);
}
@ -1329,7 +1336,9 @@ ui_sig ui_button_str8(str8 label)
| UI_FLAG_ACTIVE_ANIMATION;
ui_box* box = ui_box_make_str8(label, flags);
ui_box_set_tag(box, UI_STYLE_TAG_BUTTON);
//TODO
// ui_box_set_tag(box, UI_STYLE_TAG_BUTTON);
ui_sig sig = ui_box_sig(box);
if(sig.hovering)
@ -1367,7 +1376,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_bg_color((mg_color){0, 0, 0, 0});
ui_push_fg_color((mg_color){0, 0, 0, 0});
ui_push_roundness(roundness);
@ -1376,7 +1385,7 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue)
ui_push_roundness_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_HOT|UI_STYLE_SEL_ACTIVE, roundness);
ui_push_animation_time_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, 1.);
ui_push_animation_flags_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, UI_STYLE_ANIMATE_BG_COLOR|UI_STYLE_ANIMATE_FG_COLOR);
*/
ui_flags trackFlags = UI_FLAG_CLIP
| UI_FLAG_DRAW_BACKGROUND
| UI_FLAG_HOT_ANIMATION
@ -1409,7 +1418,7 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue)
ui_box_set_size(afterSpacer, secondAxis, UI_SIZE_PARENT_RATIO, 1., 0);
ui_box_end();
/*
ui_pop_bg_color();
ui_pop_fg_color();
ui_pop_roundness();
@ -1418,7 +1427,7 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue)
ui_pop_roundness();
ui_pop_animation_time();
ui_pop_animation_flags();
*/
//NOTE: interaction
ui_sig thumbSig = ui_box_sig(thumb);
if(thumbSig.dragging)
@ -1579,10 +1588,10 @@ void ui_menu_bar_begin(const char* name)
ui_box_set_size(bar, UI_AXIS_X, UI_SIZE_PARENT_RATIO, 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);
/*
ui_push_size(UI_AXIS_X, UI_SIZE_TEXT, 0, 0);
ui_push_size(UI_AXIS_Y, UI_SIZE_TEXT, 0, 0);
*/
ui_sig sig = ui_box_sig(bar);
if(!sig.hovering && mp_mouse_released(MP_MOUSE_LEFT))
{
@ -1592,8 +1601,10 @@ void ui_menu_bar_begin(const char* name)
void ui_menu_bar_end(void)
{
/*
ui_pop_size(UI_AXIS_X);
ui_pop_size(UI_AXIS_Y);
*/
ui_box_end(); // menu bar
}
@ -2057,7 +2068,7 @@ void ui_text_box_render(ui_box* box, void* data)
mg_set_font(style->font);
mg_set_font_size(style->fontSize);
mg_set_color(style->fontColor);
mg_set_color(style->color);
mg_move_to(textX, textY);
mg_codepoints_outlines(beforeSelect);
@ -2067,7 +2078,7 @@ void ui_text_box_render(ui_box* box, void* data)
mg_codepoints_outlines(select);
mg_fill();
mg_set_color(style->fontColor);
mg_set_color(style->color);
mg_codepoints_outlines(afterSelect);
mg_fill();
}
@ -2082,7 +2093,7 @@ void ui_text_box_render(ui_box* box, void* data)
}
mg_set_font(style->font);
mg_set_font_size(style->fontSize);
mg_set_color(style->fontColor);
mg_set_color(style->color);
mg_move_to(textX, textY);
mg_codepoints_outlines(codepoints);
@ -2093,7 +2104,7 @@ void ui_text_box_render(ui_box* box, void* data)
{
mg_set_font(style->font);
mg_set_font_size(style->fontSize);
mg_set_color(style->fontColor);
mg_set_color(style->color);
mg_move_to(textX, textY);
mg_codepoints_outlines(codepoints);

145
src/ui.h
View File

@ -75,34 +75,23 @@ typedef struct ui_size
f32 strictness;
} ui_size;
typedef enum { UI_STYLE_SEL_NORMAL = 1<<0,
UI_STYLE_SEL_HOT = 1<<1,
UI_STYLE_SEL_ACTIVE = 1<<2,
UI_STYLE_SEL_ANY = UI_STYLE_SEL_NORMAL|UI_STYLE_SEL_HOT|UI_STYLE_SEL_ACTIVE,
} ui_style_selector;
typedef u32 ui_style_tag;
#define UI_STYLE_TAG_ANY ((ui_style_tag)0)
typedef enum { UI_STYLE_ANIMATE_SIZE_X = 1<<1,
UI_STYLE_ANIMATE_SIZE_Y = 1<<2,
UI_STYLE_ANIMATE_BG_COLOR = 1<<3,
UI_STYLE_ANIMATE_FG_COLOR = 1<<4,
UI_STYLE_ANIMATE_COLOR = 1<<3,
UI_STYLE_ANIMATE_BG_COLOR = 1<<4,
UI_STYLE_ANIMATE_BORDER_COLOR = 1<<5,
UI_STYLE_ANIMATE_FONT_COLOR = 1<<6,
UI_STYLE_ANIMATE_BORDER_SIZE = 1<<6,
UI_STYLE_ANIMATE_FONT_SIZE = 1<<7,
UI_STYLE_ANIMATE_BORDER_SIZE = 1<<8,
UI_STYLE_ANIMATE_ROUNDNESS = 1<<9,
UI_STYLE_ANIMATE_POS = 1<<10,
UI_STYLE_ANIMATE_ROUNDNESS = 1<<8,
UI_STYLE_ANIMATE_POS = 1<<9,
} ui_style_animation_flags;
typedef struct ui_style
{
ui_size size[UI_AXIS_COUNT];
mg_color color;
mg_color bgColor;
mg_color fgColor;
mg_color borderColor;
mg_color fontColor;
mg_font font;
f32 fontSize;
f32 borderSize;
@ -111,6 +100,64 @@ typedef struct ui_style
ui_style_animation_flags animationFlags;
} 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_ANIMAION_FLAGS = 1<<12,
//...
};
typedef struct ui_tag { u64 hash; } ui_tag;
typedef enum
{
UI_SEL_TEXT,
UI_SEL_TAG,
UI_SEL_HOVER,
UI_SEL_ACTIVE,
UI_SEL_DRAGGING,
UI_SEL_KEY,
//...
} ui_selector_kind;
typedef struct ui_selector
{
list_elt listElt;
ui_selector_kind kind;
union
{
str8 text;
ui_key key;
ui_tag tag;
//...
};
} ui_selector;
typedef struct ui_pattern { list_info l; } ui_pattern;
typedef struct ui_style_rule
{
list_elt boxElt;
list_elt buildElt;
list_elt tmpElt;
ui_pattern pattern;
ui_style_mask mask;
ui_style* style;
} ui_style_rule;
typedef struct ui_box ui_box;
typedef struct ui_sig
@ -149,12 +196,16 @@ struct ui_box
// builder-provided info
ui_flags flags;
str8 string;
list_info tags;
// styling and layout
ui_box_render_proc renderProc;
void* renderData;
ui_style_tag tag;
// styling and layout
list_info beforeRules;
list_info afterRules;
//ui_style_tag tag;
ui_style* targetStyle;
ui_style computedStyle;
u32 z;
@ -190,6 +241,8 @@ 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);
@ -205,6 +258,10 @@ 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);
bool ui_box_closed(ui_box* box);
void ui_box_set_closed(ui_box* box, bool closed);
@ -219,46 +276,16 @@ 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);
//void ui_box_set_style_selector(ui_box* box, ui_style_selector selector);
ui_sig ui_box_sig(ui_box* box);
void ui_push_size(ui_axis axis, ui_size_kind kind, f32 value, f32 strictness);
void ui_push_size_ext(ui_style_tag tag, ui_style_selector selector, ui_axis axis, ui_size_kind kind, f32 value, f32 strictness);
void ui_pop_size(ui_axis axis);
void ui_push_bg_color(mg_color color);
void ui_push_fg_color(mg_color color);
void ui_push_font(mg_font font);
void ui_push_font_size(f32 size);
void ui_push_font_color(mg_color color);
void ui_push_border_size(f32 size);
void ui_push_border_color(mg_color color);
void ui_push_roundness(f32 roundness);
void ui_push_animation_time(f32 time);
void ui_push_animation_flags(u32 flags);
void ui_push_bg_color_ext(ui_style_tag tag, ui_style_selector selector, mg_color color);
void ui_push_fg_color_ext(ui_style_tag tag, ui_style_selector selector, mg_color color);
void ui_push_font_ext(ui_style_tag tag, ui_style_selector selector, mg_font font);
void ui_push_font_size_ext(ui_style_tag tag, ui_style_selector selector, f32 size);
void ui_push_font_color_ext(ui_style_tag tag, ui_style_selector selector, mg_color color);
void ui_push_border_size_ext(ui_style_tag tag, ui_style_selector selector, f32 size);
void ui_push_border_color_ext(ui_style_tag tag, ui_style_selector selector, mg_color color);
void ui_push_roundness_ext(ui_style_tag tag, ui_style_selector selector, f32 roundness);
void ui_push_animation_time_ext(ui_style_tag tag, ui_style_selector selector, f32 time);
void ui_push_animation_flags_ext(ui_style_tag tag, ui_style_selector selector, u32 flags);
void ui_pop_bg_color(void);
void ui_pop_fg_color(void);
void ui_pop_font(void);
void ui_pop_font_size(void);
void ui_pop_font_color(void);
void ui_pop_border_size(void);
void ui_pop_border_color(void);
void ui_pop_roundness(void);
void ui_pop_animation_time(void);
void ui_pop_animation_flags(void);
//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!
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);
//-------------------------------------------------------------------------
// Basic widget helpers
@ -306,6 +333,12 @@ typedef struct ui_text_box_result
ui_text_box_result ui_text_box(const char* name, mem_arena* arena, str8 text);
////////////////////////////////////////// WIP styling //////////////////////////////////////////
#ifdef __cplusplus
} // extern "C"
#endif