[ui] Allow styling overlay boxes (e.g. menu and drop-down list panels) locally.

- Added an UI_FLAG_OVERLAY flag. When we make a box with that flag set, it is added the an overlayList in the ui context.
- After styling and static size pass, we reparent boxes in the overlayList to an overlay node just below the root node.
- Layout and drawing works uniformly on the whole tree. Overlay boxes get drawn last and steal mouse hover from boxes underneath them.
This commit is contained in:
Martin Fouilleul 2023-03-13 10:26:39 +01:00
parent 9e41d2b6fc
commit 5455c2a52b
3 changed files with 147 additions and 118 deletions

View File

@ -518,6 +518,10 @@ int main()
UI_STYLE_SIZE); UI_STYLE_SIZE);
widget_view("Test") widget_view("Test")
{ {
ui_pattern pattern = {};
ui_pattern_push(mem_scratch(), &pattern, (ui_selector){.kind = UI_SEL_TEXT, .text = STR8("panel")});
ui_style_match_after(pattern, &(ui_style){.bgColor = {0.3, 0.3, 1, 1}}, UI_STYLE_BG_COLOR);
static int selected = 0; static int selected = 0;
str8 options[] = {STR8("option 1"), str8 options[] = {STR8("option 1"),
STR8("option 2"), STR8("option 2"),

View File

@ -77,6 +77,7 @@ typedef struct ui_context
ui_box* root; ui_box* root;
ui_box* overlay; ui_box* overlay;
list_info overlayList;
ui_stack_elt* boxStack; ui_stack_elt* boxStack;
ui_stack_elt* clipStack; ui_stack_elt* clipStack;
@ -463,6 +464,11 @@ ui_box* ui_box_make_str8(str8 string, ui_flags flags)
list_append(&box->parent->children, &box->listElt); list_append(&box->parent->children, &box->listElt);
box->parentClosed = box->parent->closed || box->parent->parentClosed; box->parentClosed = box->parent->closed || box->parent->parentClosed;
} }
if(box->flags & UI_FLAG_OVERLAY)
{
list_append(&ui->overlayList, &box->overlayElt);
}
} }
else else
{ {
@ -1269,8 +1275,20 @@ void ui_solve_layout(ui_context* ui)
list_info beforeRules = {0}; list_info beforeRules = {0};
list_info afterRules = {0}; list_info afterRules = {0};
//NOTE: style and compute static sizes
ui_styling_prepass(ui, ui->root, &beforeRules, &afterRules); ui_styling_prepass(ui, ui->root, &beforeRules, &afterRules);
//NOTE: reparent overlay boxes
for_list(&ui->overlayList, box, ui_box, overlayElt)
{
if(box->parent)
{
list_remove(&box->parent->children, &box->listElt);
list_append(&ui->overlay->children, &box->listElt);
}
}
//NOTE: compute layout
for(int axis=0; axis<UI_AXIS_COUNT; axis++) for(int axis=0; axis<UI_AXIS_COUNT; axis++)
{ {
ui_layout_downward_dependent_size(ui, ui->root, axis); ui_layout_downward_dependent_size(ui, ui->root, axis);
@ -1463,6 +1481,7 @@ void ui_begin_frame(ui_style* defaultStyle, ui_style_mask defaultMask)
UI_STYLE_LAYOUT | UI_STYLE_FLOAT_X | UI_STYLE_FLOAT_Y); 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->overlayList = (list_info){0};
ui->nextBoxBeforeRules = (list_info){0}; ui->nextBoxBeforeRules = (list_info){0};
ui->nextBoxAfterRules = (list_info){0}; ui->nextBoxAfterRules = (list_info){0};
@ -1980,21 +1999,22 @@ void ui_menu_bar_end(void)
ui_box_end(); // menu bar ui_box_end(); // menu bar
} }
void ui_menu_begin(const char* label) void ui_menu_begin(const char* label)
{ {
ui_box* container = ui_box_make(label, 0);
ui_box_push(container);
ui_style_next(&(ui_style){.size.width = {UI_SIZE_TEXT}, ui_style_next(&(ui_style){.size.width = {UI_SIZE_TEXT},
.size.height = {UI_SIZE_TEXT}}, .size.height = {UI_SIZE_TEXT}},
UI_STYLE_SIZE); UI_STYLE_SIZE);
ui_box* button = ui_box_make(label, UI_FLAG_CLICKABLE | UI_FLAG_DRAW_TEXT); ui_box* button = ui_box_make(label, UI_FLAG_CLICKABLE | UI_FLAG_DRAW_TEXT);
ui_box* bar = button->parent; ui_box* bar = container->parent;
ui_sig sig = ui_box_sig(button); ui_sig sig = ui_box_sig(button);
ui_sig barSig = ui_box_sig(bar); ui_sig barSig = ui_box_sig(bar);
ui_context* ui = ui_get_context(); ui_context* ui = ui_get_context();
ui_box_push(ui->overlay);
ui_style style = {.size.width = {UI_SIZE_CHILDREN}, ui_style style = {.size.width = {UI_SIZE_CHILDREN},
.size.height = {UI_SIZE_CHILDREN}, .size.height = {UI_SIZE_CHILDREN},
@ -2013,11 +2033,12 @@ void ui_menu_begin(const char* label)
| UI_STYLE_LAYOUT | UI_STYLE_LAYOUT
| UI_STYLE_BG_COLOR; | UI_STYLE_BG_COLOR;
ui_flags flags = UI_FLAG_DRAW_BACKGROUND ui_flags flags = UI_FLAG_OVERLAY
| UI_FLAG_DRAW_BACKGROUND
| UI_FLAG_DRAW_BORDER; | UI_FLAG_DRAW_BORDER;
ui_style_next(&style, mask); ui_style_next(&style, mask);
ui_box* menu = ui_box_make(label, flags); ui_box* menu = ui_box_make("panel", flags);
if(ui_box_active(bar)) if(ui_box_active(bar))
{ {
@ -2047,7 +2068,7 @@ void ui_menu_begin(const char* label)
void ui_menu_end(void) void ui_menu_end(void)
{ {
ui_box_pop(); // menu ui_box_pop(); // menu
ui_box_pop(); // overlay; ui_box_pop(); // container
} }
ui_sig ui_menu_button(const char* name) ui_sig ui_menu_button(const char* name)
@ -2095,7 +2116,9 @@ ui_select_popup_info ui_select_popup(const char* name, ui_select_popup_info* inf
ui_context* ui = ui_get_context(); ui_context* ui = ui_get_context();
ui_box* button = ui_box_make(name, ui_container(name, 0)
{
ui_box* button = ui_box_make("button",
UI_FLAG_CLICKABLE UI_FLAG_CLICKABLE
|UI_FLAG_DRAW_BACKGROUND |UI_FLAG_DRAW_BACKGROUND
|UI_FLAG_DRAW_BORDER |UI_FLAG_DRAW_BORDER
@ -2143,12 +2166,14 @@ ui_select_popup_info ui_select_popup(const char* name, ui_select_popup_info* inf
ui_box* arrow = ui_box_make("arrow", UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_PROC); ui_box* arrow = ui_box_make("arrow", UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_PROC);
ui_box_set_draw_proc(arrow, ui_select_popup_draw_arrow, 0); ui_box_set_draw_proc(arrow, ui_select_popup_draw_arrow, 0);
} ui_box_pop(); } ui_box_pop();
ui_box_push(ui->overlay); //panel
ui_box* panel = ui_box_make(name, ui_box* panel = ui_box_make("panel",
UI_FLAG_DRAW_BACKGROUND UI_FLAG_DRAW_BACKGROUND
|UI_FLAG_BLOCK_MOUSE); |UI_FLAG_BLOCK_MOUSE
|UI_FLAG_OVERLAY);
//TODO: set width to max(button.w, max child...) //TODO: set width to max(button.w, max child...)
f32 containerWidth = maximum(maxOptionWidth + 2*panel->style.layout.margin.x, f32 containerWidth = maximum(maxOptionWidth + 2*panel->style.layout.margin.x,
@ -2204,7 +2229,6 @@ ui_select_popup_info ui_select_popup(const char* name, ui_select_popup_info* inf
} }
} }
ui_box_pop(); ui_box_pop();
ui_box_pop();
if(ui_box_active(panel) && mp_mouse_pressed(MP_MOUSE_LEFT)) if(ui_box_active(panel) && mp_mouse_pressed(MP_MOUSE_LEFT))
{ {
@ -2214,10 +2238,8 @@ ui_select_popup_info ui_select_popup(const char* name, ui_select_popup_info* inf
{ {
ui_box_activate(panel); ui_box_activate(panel);
} }
ui_box_set_closed(panel, !ui_box_active(panel)); ui_box_set_closed(panel, !ui_box_active(panel));
}
return(result); return(result);
} }

View File

@ -263,6 +263,7 @@ typedef enum
UI_FLAG_DRAW_TEXT = (1<<11), UI_FLAG_DRAW_TEXT = (1<<11),
UI_FLAG_DRAW_PROC = (1<<12), UI_FLAG_DRAW_PROC = (1<<12),
UI_FLAG_OVERLAY = (1<<13),
} ui_flags; } ui_flags;
struct ui_box struct ui_box
@ -272,6 +273,8 @@ struct ui_box
list_info children; list_info children;
ui_box* parent; ui_box* parent;
list_elt overlayElt;
// keying and caching // keying and caching
list_elt bucketElt; list_elt bucketElt;
ui_key key; ui_key key;