From 32a57d52937ecd1d6d0f48c679a543d5ecfca9b2 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Thu, 2 Oct 2025 23:19:42 +0200 Subject: [PATCH] popups --- lib/ugui.c3l/src/core.c3 | 15 ++++++- lib/ugui.c3l/src/widgets/button.c3 | 23 +++++----- lib/ugui.c3l/src/widgets/div.c3 | 69 +++++++++++++++++++++++++++--- lib/ugui.c3l/src/widgets/slider.c3 | 12 +++--- lib/ugui.c3l/src/widgets/sprite.c3 | 2 +- lib/ugui.c3l/src/widgets/text.c3 | 12 +++--- src/main.c3 | 17 +++++++- 7 files changed, 117 insertions(+), 33 deletions(-) diff --git a/lib/ugui.c3l/src/core.c3 b/lib/ugui.c3l/src/core.c3 index f7d4c49..1788110 100644 --- a/lib/ugui.c3l/src/core.c3 +++ b/lib/ugui.c3l/src/core.c3 @@ -58,6 +58,7 @@ struct Elem { Rect children_bounds; ElemType type; Layout layout; + int z_index; union { ElemDiv div; ElemButton button; @@ -100,6 +101,7 @@ struct Ctx { bool has_focus; struct input { InputEvents events; + int z_index; // the layer the input events need to be propageted to struct mouse { Point pos, delta; // mouse_down: bitmap of mouse buttons that are held @@ -178,6 +180,11 @@ fn Elem*? Ctx.get_elem(&ctx, Id id, ElemType type) } else { elem.type = type; } + // FIXME: this is crap + if (ctx.tree.is_used(ctx.active_div)) { + elem.z_index = ctx.get_active_div()!.z_index; + } + elem.tree_idx = ctx.tree.add(ctx.active_div, id); return elem; } @@ -242,10 +249,11 @@ fn void? Ctx.frame_begin(&ctx) // if the window has focus then the root element also has focus, no other // computation needed, child elements need to check the mouse positon and // other stuff + ctx.input.z_index = 0; //elem.flags.has_focus = ctx.has_focus; elem.bounds = {0, 0, ctx.width, ctx.height}; - elem.div.z_index = 0; + elem.z_index = 0; elem.div.scroll_x.enabled = false; elem.div.scroll_y.enabled = false; elem.layout.dir = ROW; @@ -309,7 +317,8 @@ $endif // sort the command buffer by the z-index // FIXME: sorting the buffer fucks with scissor commands that have to be kept in place - //ctx.cmd_queue.sort()!; + // TODO: instead of sorting at the end perform ordered inserts into the command buffer + ctx.cmd_queue.sort()!; // foreach (i, c: ctx.cmd_queue) { // io::printf("[%d]: ", i); @@ -335,6 +344,8 @@ macro bool Ctx.elem_focus(&ctx, Elem *elem) // FIXME: hacked together, please do better fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem) { + if (ctx.input.z_index != elem.z_index) return {}; + bool hover = ctx.is_hovered(elem); bool focus = ctx.elem_focus(elem) || (hover && ctx.is_mouse_pressed(BTN_LEFT)); diff --git a/lib/ugui.c3l/src/widgets/button.c3 b/lib/ugui.c3l/src/widgets/button.c3 index 8cee40b..d732dc6 100644 --- a/lib/ugui.c3l/src/widgets/button.c3 +++ b/lib/ugui.c3l/src/widgets/button.c3 @@ -47,6 +47,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon) update_parent_size(elem, parent); elem.events = ctx.get_elem_events(elem); +// if (ctx.input.z_index == elem.z_index) println("true ", elem.z_index); Rect content_bounds = elem.content_bounds(); Rect icon_bounds = { @@ -72,12 +73,12 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon) s.bg = s.accent; } - ctx.push_rect(elem.bounds.pad(style.margin), parent.div.z_index, &s)!; + ctx.push_rect(elem.bounds.pad(style.margin), parent.z_index, &s)!; if (icon != "") { - ctx.push_sprite(icon_bounds, sprite.uv(), ctx.sprite_atlas.id, parent.div.z_index, type: sprite.type)!; + ctx.push_sprite(icon_bounds, sprite.uv(), ctx.sprite_atlas.id, parent.z_index, type: sprite.type)!; } if (label != "") { - ctx.layout_string(label, text_bounds, CENTER, parent.div.z_index, style.fg)!; + ctx.layout_string(label, text_bounds, CENTER, parent.z_index, style.fg)!; } return elem.events; } @@ -138,20 +139,20 @@ fn void? Ctx.checkbox_id(&ctx, Id id, String description, bool* active, String t s.border = style.border; s.radius = style.radius; - ctx.layout_string(description, text_bounds, CENTER, parent.div.z_index, style.fg)!; + ctx.layout_string(description, text_bounds, CENTER, parent.z_index, style.fg)!; if (tick_sprite != "") { - ctx.push_rect(check_bounds, parent.div.z_index, &s)!; + ctx.push_rect(check_bounds, parent.z_index, &s)!; if (*active) { Sprite* sprite = ctx.sprite_atlas.get(tick_sprite)!; Id tex_id = ctx.sprite_atlas.id; - ctx.push_sprite(sprite.rect().center_to(check_bounds), sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!; + ctx.push_sprite(sprite.rect().center_to(check_bounds), sprite.uv(), tex_id, parent.z_index, type: sprite.type)!; } } else { if (*active) { s.bg = style.primary; - ctx.push_rect(check_bounds, parent.div.z_index, &s)!; + ctx.push_rect(check_bounds, parent.z_index, &s)!; } else { - ctx.push_rect(check_bounds, parent.div.z_index, &s)!; + ctx.push_rect(check_bounds, parent.z_index, &s)!; } } } @@ -216,10 +217,10 @@ fn void? Ctx.toggle_id(&ctx, Id id, String description, bool* active) s.secondary = style.secondary; s.border = style.border; s.radius = style.radius; - ctx.layout_string(description, text_bounds, CENTER, parent.div.z_index, style.fg)!; - ctx.push_rect(toggle_bounds, parent.div.z_index, &s)!; + ctx.layout_string(description, text_bounds, CENTER, parent.z_index, style.fg)!; + ctx.push_rect(toggle_bounds, parent.z_index, &s)!; s.bg = style.primary; s.border = {}; - ctx.push_rect(toggle.pad(style.border), parent.div.z_index, &s)!; + ctx.push_rect(toggle.pad(style.border), parent.z_index, &s)!; } diff --git a/lib/ugui.c3l/src/widgets/div.c3 b/lib/ugui.c3l/src/widgets/div.c3 index 8b581a2..566920f 100644 --- a/lib/ugui.c3l/src/widgets/div.c3 +++ b/lib/ugui.c3l/src/widgets/div.c3 @@ -15,7 +15,6 @@ struct ElemDiv { bool on; float value; } - int z_index; } @@ -81,8 +80,8 @@ fn void? Ctx.div_begin_id(&ctx, elem.div.scroll_x.enabled = scroll_x; elem.div.scroll_y.enabled = scroll_y; - elem.div.z_index = parent.div.z_index + 1; - + if (ctx.input.z_index < elem.z_index) ctx.input.z_index = elem.z_index; + // update layout with correct info elem.layout = { .w = width, @@ -97,11 +96,11 @@ fn void? Ctx.div_begin_id(&ctx, elem.layout.origin.y = off.y; } - ctx.push_rect(elem.bounds.pad(style.margin), elem.div.z_index, style)!; + ctx.push_rect(elem.bounds.pad(style.margin), elem.z_index, style)!; // update the ctx scissor, it HAS to be after drawing the background ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset); - ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.div.z_index)!; + ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!; elem.events = ctx.get_elem_events(elem); @@ -160,9 +159,67 @@ fn Id? Ctx.div_end(&ctx) ctx.active_div = ctx.tree.parentof(ctx.active_div); Elem* parent = ctx.get_parent()!; ctx.div_scissor = parent.bounds.pad(parent.layout.content_offset); - ctx.reset_scissor(elem.div.z_index)!; + ctx.reset_scissor(elem.z_index)!; update_parent_size(elem, parent); return elem.id; } + +macro bool? Ctx.popup_begin(&ctx, Point pos, + Size width, Size height, + LayoutDirection dir = ROW, Anchor anchor = TOP_LEFT, + bool scroll_x = false, bool scroll_y = false, + ... + ) + => ctx.popup_begin_id(@compute_id($vasplat), pos, width, height, dir, anchor, scroll_x, scroll_y); +fn bool? Ctx.popup_begin_id(&ctx, + Id id, + Point pos, + Size width, Size height, + LayoutDirection dir, Anchor anchor, + bool scroll_x, bool scroll_y + ) +{ + id = ctx.gen_id(id)!; + + Elem* elem = ctx.get_elem(id, ETYPE_DIV)!; + Elem* parent = ctx.find_elem(ctx.active_div); // pop-up parent is always root + ctx.active_div = elem.tree_idx; + + Style* style = ctx.styles.get_style(@str_hash("popup")); + + elem.div.scroll_x.enabled = scroll_x; + elem.div.scroll_y.enabled = scroll_y; + elem.z_index++; + if (ctx.input.z_index < elem.z_index) ctx.input.z_index = elem.z_index; + + // update layout with correct info + elem.layout = { + .w = width, + .h = height, + .dir = dir, + .anchor = anchor, + .content_offset = style.margin + style.border + style.padding, + .absolute = true, + .origin.x = pos.x - parent.bounds.x, + .origin.y = pos.y - parent.bounds.y, + }; + + ctx.push_rect(elem.bounds.pad(style.margin), elem.z_index, style)!; + + // update the ctx scissor, it HAS to be after drawing the background + ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset); + ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!; + + elem.events = ctx.get_elem_events(elem); + + // check close condition, mouse release anywhere outside the div bounds + if ((ctx.mouse_released() & BTN_ANY) && !elem.events.mouse_hover) { + return false; + } + + // TODO: check active + // TODO: check resizeable + return true; +} diff --git a/lib/ugui.c3l/src/widgets/slider.c3 b/lib/ugui.c3l/src/widgets/slider.c3 index 1fb8c32..31b961c 100644 --- a/lib/ugui.c3l/src/widgets/slider.c3 +++ b/lib/ugui.c3l/src/widgets/slider.c3 @@ -57,11 +57,11 @@ fn ElemEvents? Ctx.slider_hor_id(&ctx, Id id, Size w, Size h, float* value, floa Style s = *style; Rect padding = s.padding; s.padding = {}; - ctx.push_rect(bg_bounds, parent.div.z_index, &s)!; + ctx.push_rect(bg_bounds, parent.z_index, &s)!; s.bg = s.primary; s.padding = padding; s.border = {}; - ctx.push_rect(elem.slider.handle, parent.div.z_index, &s)!; + ctx.push_rect(elem.slider.handle, parent.z_index, &s)!; return elem.events; } @@ -121,11 +121,11 @@ fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Size w, Size h, float* value, floa Style s = *style; Rect padding = s.padding; s.padding = {}; - ctx.push_rect(bg_bounds, parent.div.z_index, &s)!; + ctx.push_rect(bg_bounds, parent.z_index, &s)!; s.bg = s.primary; s.padding = padding; s.border = {}; - ctx.push_rect(elem.slider.handle, parent.div.z_index, &s)!; + ctx.push_rect(elem.slider.handle, parent.z_index, &s)!; return elem.events; } @@ -177,8 +177,8 @@ fn void? Ctx.scrollbar(&ctx, Id id, float *value, float handle_percent, bool ver Rect bg_bounds = elem.bounds.pad(style.margin); - ctx.push_rect(bg_bounds, parent.div.z_index, style)!; - ctx.push_rect(elem.slider.handle, parent.div.z_index, &&(Style){.bg = style.primary, .radius = style.radius})!; + ctx.push_rect(bg_bounds, parent.z_index, style)!; + ctx.push_rect(elem.slider.handle, parent.z_index, &&(Style){.bg = style.primary, .radius = style.radius})!; } macro short calc_slider(short off, short dim, float value) => (short)off + (short)(dim * value); diff --git a/lib/ugui.c3l/src/widgets/sprite.c3 b/lib/ugui.c3l/src/widgets/sprite.c3 index 50e2ab8..1886a78 100644 --- a/lib/ugui.c3l/src/widgets/sprite.c3 +++ b/lib/ugui.c3l/src/widgets/sprite.c3 @@ -22,5 +22,5 @@ fn void? Ctx.sprite_id(&ctx, Id id, String name) update_parent_size(elem, parent); Id tex_id = ctx.sprite_atlas.id; - return ctx.push_sprite(elem.bounds, sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!; + return ctx.push_sprite(elem.bounds, sprite.uv(), tex_id, parent.z_index, type: sprite.type)!; } diff --git a/lib/ugui.c3l/src/widgets/text.c3 b/lib/ugui.c3l/src/widgets/text.c3 index 47a14f5..bd65c56 100644 --- a/lib/ugui.c3l/src/widgets/text.c3 +++ b/lib/ugui.c3l/src/widgets/text.c3 @@ -35,7 +35,7 @@ fn void? Ctx.text_id(&ctx, Id id, String text) update_parent_size(elem, parent); - ctx.layout_string(text, elem.bounds.pad(elem.layout.content_offset), TOP_LEFT, parent.div.z_index, style.fg)!; + ctx.layout_string(text, elem.bounds.pad(elem.layout.content_offset), TOP_LEFT, parent.z_index, style.fg)!; } @@ -75,17 +75,17 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te, Anchor Rect bg_bounds = elem.bounds.pad(style.margin); Rect text_bounds = elem.bounds.pad(elem.layout.content_offset); - ctx.push_rect(bg_bounds, parent.div.z_index, style)!; + ctx.push_rect(bg_bounds, parent.z_index, style)!; Rect cur; - cur = ctx.layout_string(elem.text.te.to_string(), text_bounds, text_alignment, parent.div.z_index, style.fg, elem.text.te.cursor)!; + cur = ctx.layout_string(elem.text.te.to_string(), text_bounds, text_alignment, parent.z_index, style.fg, elem.text.te.cursor)!; // draw the cursor if the element has focus cur.w = 2; cur.x -= 2; if (elem.events.has_focus) { - ctx.push_scissor(text_bounds, parent.div.z_index)!; - ctx.push_rect(cur, parent.div.z_index, &&(Style){.bg = style.fg})!; - ctx.reset_scissor(parent.div.z_index)!; + ctx.push_scissor(text_bounds, parent.z_index)!; + ctx.push_rect(cur, parent.z_index, &&(Style){.bg = style.fg})!; + ctx.reset_scissor(parent.z_index)!; } return elem.events; } \ No newline at end of file diff --git a/src/main.c3 b/src/main.c3 index e34c04e..ddc79b6 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -209,7 +209,7 @@ fn int main(String[] args) if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true; -const String APPLICATION = "debug"; +const String APPLICATION = "calculator"; $switch APPLICATION: $case "debug": debug_app(&ui); @@ -382,6 +382,20 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te) if (len > 0) len--; } + static bool toggle; + static ugui::Point pos; + if (ui.is_mouse_released(ugui::BTN_RIGHT)) { + pos = ui.input.mouse.pos; + toggle = !toggle; + } + if (toggle) { + toggle = ui.popup_begin(pos, ugui::@fit(50), ugui::@fit(100), COLUMN)!!; + ui.button("Uno")!!; + ui.button("Due")!!; + ui.button("Tre")!!; + ui.button("Quattro")!!; + ui.div_end()!!; + } // ui input/output ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) { // center everything on the screen ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT) { @@ -446,4 +460,5 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te) ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!; }!!; }!!; }!!; + }