[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);
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;
str8 options[] = {STR8("option 1"),
STR8("option 2"),

View File

@ -77,6 +77,7 @@ typedef struct ui_context
ui_box* root;
ui_box* overlay;
list_info overlayList;
ui_stack_elt* boxStack;
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);
box->parentClosed = box->parent->closed || box->parent->parentClosed;
}
if(box->flags & UI_FLAG_OVERLAY)
{
list_append(&ui->overlayList, &box->overlayElt);
}
}
else
{
@ -1269,8 +1275,20 @@ void ui_solve_layout(ui_context* ui)
list_info beforeRules = {0};
list_info afterRules = {0};
//NOTE: style and compute static sizes
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++)
{
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->overlay = ui_box_make("_overlay_", 0);
ui->overlayList = (list_info){0};
ui->nextBoxBeforeRules = (list_info){0};
ui->nextBoxAfterRules = (list_info){0};
@ -1980,21 +1999,22 @@ void ui_menu_bar_end(void)
ui_box_end(); // menu bar
}
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},
.size.height = {UI_SIZE_TEXT}},
UI_STYLE_SIZE);
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 barSig = ui_box_sig(bar);
ui_context* ui = ui_get_context();
ui_box_push(ui->overlay);
ui_style style = {.size.width = {UI_SIZE_CHILDREN},
.size.height = {UI_SIZE_CHILDREN},
@ -2013,11 +2033,12 @@ void ui_menu_begin(const char* label)
| UI_STYLE_LAYOUT
| 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_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))
{
@ -2047,7 +2068,7 @@ void ui_menu_begin(const char* label)
void ui_menu_end(void)
{
ui_box_pop(); // menu
ui_box_pop(); // overlay;
ui_box_pop(); // container
}
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_box* button = ui_box_make(name,
ui_container(name, 0)
{
ui_box* button = ui_box_make("button",
UI_FLAG_CLICKABLE
|UI_FLAG_DRAW_BACKGROUND
|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_set_draw_proc(arrow, ui_select_popup_draw_arrow, 0);
} ui_box_pop();
ui_box_push(ui->overlay);
ui_box* panel = ui_box_make(name,
//panel
ui_box* panel = ui_box_make("panel",
UI_FLAG_DRAW_BACKGROUND
|UI_FLAG_BLOCK_MOUSE);
|UI_FLAG_BLOCK_MOUSE
|UI_FLAG_OVERLAY);
//TODO: set width to max(button.w, max child...)
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();
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_set_closed(panel, !ui_box_active(panel));
}
return(result);
}

View File

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