From b5ef86d092f85d35326cb473582ac09dd44a968e Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Fri, 10 Oct 2025 22:31:28 +0200 Subject: [PATCH] actually correct input handling --- TODO | 17 +++-- lib/ugui.c3l/src/core.c3 | 123 ++++++++++++++++++++------------ lib/ugui.c3l/src/input.c3 | 78 ++++++++++---------- lib/ugui.c3l/src/layout.c3 | 74 ++----------------- lib/ugui.c3l/src/shapes.c3 | 12 ++-- lib/ugui.c3l/src/widgets/div.c3 | 4 +- src/main.c3 | 17 +++-- 7 files changed, 144 insertions(+), 181 deletions(-) diff --git a/TODO b/TODO index 36963d9..d9bcb08 100644 --- a/TODO +++ b/TODO @@ -4,12 +4,11 @@ [x] Implement div.view and scrollbars [x] Port font system from C to C3 (rewrite1) [ ] Update ARCHITECTURE.md -[ ] Write a README.md +[x] Write a README.md [x] Use an arena allocator for cache [ ] Do not redraw if there was no update (no layout and no draw) [ ] Do command buffer damage tracking based on a context grid (see rxi writeup) -[x] Better handling of the active and focused widgets, try -to maintain focus until mouse release (fix scroll bars) +[x] Better handling of the active and focused widgets, try to maintain focus until mouse release (fix scroll bars) [x] Clip element bounds to parent div, specifically text [ ] Resizeable divs [x] Implement a z index and sort command buffer based on that @@ -30,14 +29,14 @@ to maintain focus until mouse release (fix scroll bars) [ ] .png [ ] .jpg [ ] gif support? -[ ] layout_set_max_rows() and layout_set_max_columns() +[x] layout_set_max_rows() and layout_set_max_columns() [x] Maybe SDF sprites?? [x] Stylesheets and stylesheet import [x] use SDF to draw anti-aliased rounded rectangles https://zed.dev/blog/videogame [ ] Subdivide modules into ugui::ug for exported functions and ugui::core for internal use functions (used to create widgets) [x] The render loop RAPES the gpu, valve pls fix -[ ] The way the element structures are implemented wastes a lot of memory since +[x] The way the element structures are implemented wastes a lot of memory since each struct Elem, struct Cmd, etc. is as big as the largest element. It would be better to use a different allcation strategy. [ ] Add a way to handle time events like double clicks @@ -52,7 +51,7 @@ to maintain focus until mouse release (fix scroll bars) [x] Center elements to the row/column [x] Text wrapping / reflow [x] Implement a better and unified way to place a glyph and get the cursor position, maybe with a struct -[ ] Correct whitespace handling in text (\t \r etc) +[x] Correct whitespace handling in text (\t \r etc) [x] Consider a multi-pass recursive approach to layout (like https://github.com/nicbarker/clay) instead of the curren multi-frame approach. [x] Implement column/row sizing (min, max) @@ -79,8 +78,8 @@ to maintain focus until mouse release (fix scroll bars) [x] Mouse scroll wheel [ ] Touch input [x] Do not set input event to true if the movement was zero (like no mouse movement) -[ ] Use input event flags, for example to consume the input event -[ ] Fix bug in text box: when spamming keys you can get multiple characters in the text input field +[x] Use input event flags, for example to consume the input event +[x] Fix bug in text box: when spamming keys you can get multiple characters in the text input field of the context, this causes a bug where only the first char is actually used ## Commands @@ -89,7 +88,7 @@ to maintain focus until mouse release (fix scroll bars) - border width - border radius [x] add a command to update an atlas -[ ] New window command, useful for popups +[x] New window command, useful for popups [x] Text command returns the text bounds, this way we can avoid the pattern draw_text(a, pos) -> off = compute_bounds(a) -> draw_text(b, pos+off) -> ... [ ] Rounded rectangle with different radius for each corner diff --git a/lib/ugui.c3l/src/core.c3 b/lib/ugui.c3l/src/core.c3 index 05d3483..aafb30b 100644 --- a/lib/ugui.c3l/src/core.c3 +++ b/lib/ugui.c3l/src/core.c3 @@ -90,6 +90,26 @@ alias ElemCache = cache::Cache{Id, Elem, MAX_ELEMENTS}; faultdef INVALID_SIZE, EVENT_UNSUPPORTED, WRONG_ELEMENT_TYPE, WRONG_ID; +struct InputData { + InputEvents events; + struct mouse { + Point pos, delta; + // mouse_down: bitmap of mouse buttons that are held + // mouse_updated: bitmap of mouse buttons that have been updated + // mouse_released = mouse_updated & ~mouse_down + // mouse_pressed = mouse_updated & mouse_down + MouseButtons down; + MouseButtons updated; + // scroll wheel + Point scroll; + } + struct keyboard { + char[TEXT_MAX] text; + usz text_len; + ModKeys modkeys; + } +} + struct Ctx { IdTree tree; ElemCache cache; @@ -101,26 +121,7 @@ struct Ctx { SpriteAtlas sprite_atlas; 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 - // mouse_updated: bitmap of mouse buttons that have been updated - // mouse_released = mouse_updated & ~mouse_down - // mouse_pressed = mouse_updated & mouse_down - MouseButtons down; - MouseButtons updated; - // scroll wheel - Point scroll; - } - struct keyboard { - char[TEXT_MAX] text; - usz text_len; - ModKeys modkeys; - } - } + InputData input, current_input; Id hover_id; Id focus_id; @@ -249,7 +250,6 @@ 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}; @@ -262,30 +262,41 @@ fn void? Ctx.frame_begin(&ctx) elem.layout.h = @exact(ctx.height); ctx.div_scissor = elem.bounds; - - // The root element does not push anything to the stack - // TODO: add a background color taken from a theme or config } fn void? Ctx.frame_end(&ctx) { - // FIXME: this is not guaranteed to be root. the user might forget to close a div or some other element Elem* root = ctx.get_active_div()!; if (root.id != ROOT_ID) { - io::printn(root.id); return WRONG_ID?; } + // 2. clear input fields + ctx.input = ctx.current_input; + ctx.current_input.events = {}; + ctx.current_input.keyboard.text_len = 0; + // DO THE LAYOUT ctx.layout_element_tree()!; + foreach (idx, id : ctx.tree.elem_vec) { + if (!ctx.tree.is_used((int)idx)) continue; + Elem* c = ctx.find_elem(id); + // reset events + c.events = {}; + // reset shown flag + // TODO: use shown_last_frame to avoid this loop entirely + c.flags.shown = false; + } + + // Propagate input events to the right elements + ctx.set_elem_events(ctx.hover_id); + ctx.set_elem_events(ctx.focus_id); + + // 1. clear the tree ctx.tree.nuke(); - // 2. clear input fields - ctx.input.events = (InputEvents)0; - ctx.input.keyboard.text_len = 0; - // send atlas updates if (ctx.font.should_update) { ctx.push_update_atlas(&ctx.font.atlas)!; @@ -325,27 +336,47 @@ $endif // } } -// TODO: add other events -// FIXME: this does not work with touch -// FIXME: hacked together, please do better -/* -fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem) + +macro bool Ctx.is_hovered(&ctx, Elem *elem) => ctx.input.mouse.pos.in_rect(elem.bounds); + +// Check if the element is hovered and/or focused, if it is update the context ids. +// The order in which the elements are passed to this function is not relevant +fn void Ctx.update_hover_and_focus(&ctx, Elem* elem) { - // FIXME: gross hack to not leak events under a pop-up - 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)); + bool focus = ctx.focus_id == elem.id || (hover && ctx.is_mouse_pressed(BTN_ANY)); - if (ctx.is_mouse_pressed(BTN_ANY) && !hover){ - focus = false; - if (ctx.focus_id == elem.id) ctx.focus_id = 0; + if (hover) { + Elem* prev_hover = ctx.find_elem(ctx.hover_id); + bool different = prev_hover.id != elem.id; + bool still_hovered = ctx.is_hovered(prev_hover); + bool shown = prev_hover.flags.shown; + bool above = prev_hover.z_index > elem.z_index; + + hover = !(different && still_hovered && shown && above); } + + if (focus) { + Elem* prev_focus = ctx.find_elem(ctx.hover_id); + bool different = prev_focus.id != elem.id; + bool shown = prev_focus.flags.shown; + bool above = prev_focus.z_index > elem.z_index; + + focus = !(different && shown && above); + } + + if (hover) ctx.hover_id = elem.id; + if (focus) ctx.focus_id = elem.id; +} - if (hover) { ctx.hover_id = elem.id; } - if (focus) { ctx.focus_id = elem.id; } +// FIXME: this does not work with touch +fn void Ctx.set_elem_events(&ctx, Id id) +{ + bool hover = id == ctx.hover_id; + bool focus = id == ctx.focus_id; + Elem* e = ctx.find_elem(id); - ElemEvents ev = { + e.events = { .has_focus = focus, .mouse_hover = hover, .mouse_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY), @@ -356,6 +387,4 @@ fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem) .key_repeat = focus && ctx.input.events.key_repeat, .text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT), }; - return ev; } -*/ diff --git a/lib/ugui.c3l/src/input.c3 b/lib/ugui.c3l/src/input.c3 index 3040db2..0721d36 100644 --- a/lib/ugui.c3l/src/input.c3 +++ b/lib/ugui.c3l/src/input.c3 @@ -71,7 +71,7 @@ const ModKeys KEY_ANY = (ModKeys)(ModKeys.inner.max); fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String ...keys) { - bool is_mod = (bool)(ctx.input.keyboard.modkeys & mod); + bool is_mod = (bool)(ctx.current_input.keyboard.modkeys & mod); bool is_keys = true; String haystack = (String)ctx.get_keys(); foreach (needle: keys) { @@ -86,7 +86,7 @@ fn void? Ctx.input_window_size(&ctx, short width, short height) if (width <= 0 || height <= 0) { return INVALID_SIZE?; } - ctx.input.events.resize = ctx.width != width || ctx.height != height; + ctx.current_input.events.resize = ctx.width != width || ctx.height != height; ctx.width = width; ctx.height = height; } @@ -96,13 +96,13 @@ fn void Ctx.input_changefocus(&ctx, bool has_focus) { // FIXME: raylib only has an API to query the focus status so we have to // update the input flag only if the focus changed - ctx.input.events.change_focus = ctx.has_focus != has_focus; + ctx.current_input.events.change_focus = ctx.has_focus != has_focus; ctx.has_focus = has_focus; } -macro Ctx.mouse_pressed(&ctx) => ctx.input.mouse.updated & ctx.input.mouse.down; -macro Ctx.mouse_released(&ctx) => ctx.input.mouse.updated & ~ctx.input.mouse.down; -macro Ctx.mouse_down(&ctx) => ctx.input.mouse.down; +macro Ctx.mouse_pressed(&ctx) => ctx.current_input.mouse.updated & ctx.current_input.mouse.down; +macro Ctx.mouse_released(&ctx) => ctx.current_input.mouse.updated & ~ctx.current_input.mouse.down; +macro Ctx.mouse_down(&ctx) => ctx.current_input.mouse.down; // FIXME: hthis compairson could be done with a cast using MouseButtons.inner // property but I could not figure out how @@ -113,63 +113,63 @@ macro Ctx.is_mouse_down(&ctx, MouseButtons btn) => (ctx.mouse_down() & btn) != B // Mouse Buttons down fn void Ctx.input_mouse_button(&ctx, MouseButtons buttons) { - ctx.input.mouse.updated = ctx.input.mouse.down ^ buttons; - ctx.input.mouse.down = buttons; - ctx.input.events.mouse_btn = (uint)ctx.input.mouse.down != 0 || (uint)ctx.input.mouse.updated != 0; + ctx.current_input.mouse.updated = ctx.current_input.mouse.down ^ buttons; + ctx.current_input.mouse.down = buttons; + ctx.current_input.events.mouse_btn = (uint)ctx.current_input.mouse.down != 0 || (uint)ctx.current_input.mouse.updated != 0; } // Mouse was moved, report absolute position fn void Ctx.input_mouse_abs(&ctx, short x, short y) { - ctx.input.mouse.pos.x = math::clamp(x, (short)0, ctx.width); - ctx.input.mouse.pos.y = math::clamp(y, (short)0, ctx.height); + ctx.current_input.mouse.pos.x = math::clamp(x, (short)0, ctx.width); + ctx.current_input.mouse.pos.y = math::clamp(y, (short)0, ctx.height); short dx, dy; - dx = x - ctx.input.mouse.pos.x; - dy = y - ctx.input.mouse.pos.y; + dx = x - ctx.current_input.mouse.pos.x; + dy = y - ctx.current_input.mouse.pos.y; - ctx.input.mouse.delta.x = dx; - ctx.input.mouse.delta.y = dy; + ctx.current_input.mouse.delta.x = dx; + ctx.current_input.mouse.delta.y = dy; - ctx.input.events.mouse_move = dx != 0 || dy != 0; + ctx.current_input.events.mouse_move = dx != 0 || dy != 0; } // Mouse was moved, report relative motion fn void Ctx.input_mouse_delta(&ctx, short dx, short dy) { - ctx.input.mouse.delta.x = dx; - ctx.input.mouse.delta.y = dy; + ctx.current_input.mouse.delta.x = dx; + ctx.current_input.mouse.delta.y = dy; short mx, my; - mx = ctx.input.mouse.pos.x + dx; - my = ctx.input.mouse.pos.y + dy; + mx = ctx.current_input.mouse.pos.x + dx; + my = ctx.current_input.mouse.pos.y + dy; - ctx.input.mouse.pos.x = math::clamp(mx, (short)0, ctx.width); - ctx.input.mouse.pos.y = math::clamp(my, (short)0, ctx.height); + ctx.current_input.mouse.pos.x = math::clamp(mx, (short)0, ctx.width); + ctx.current_input.mouse.pos.y = math::clamp(my, (short)0, ctx.height); - ctx.input.events.mouse_move = dx != 0 || dy != 0; + ctx.current_input.events.mouse_move = dx != 0 || dy != 0; } fn void Ctx.input_mouse_wheel(&ctx, short x, short y, float scale = 1.0) { - ctx.input.mouse.scroll.x = (short)((float)-x*scale); - ctx.input.mouse.scroll.y = (short)((float)-y*scale); - ctx.input.events.mouse_scroll = x !=0 || y != 0; + ctx.current_input.mouse.scroll.x = (short)((float)-x*scale); + ctx.current_input.mouse.scroll.y = (short)((float)-y*scale); + ctx.current_input.events.mouse_scroll = x !=0 || y != 0; } fn void Ctx.input_key_press(&ctx) { - ctx.input.events.key_press = true; + ctx.current_input.events.key_press = true; } fn void Ctx.input_key_release(&ctx) { - ctx.input.events.key_release = true; + ctx.current_input.events.key_release = true; } fn void Ctx.input_key_repeat(&ctx) { - ctx.input.events.key_repeat = true; + ctx.current_input.events.key_repeat = true; } // append utf-8 encoded text to the context text input @@ -177,12 +177,12 @@ fn void Ctx.input_text_utf8(&ctx, char[] text) { if (text.len == 0) { return; } - usz remaining = ctx.input.keyboard.text.len - ctx.input.keyboard.text_len; + usz remaining = ctx.current_input.keyboard.text.len - ctx.current_input.keyboard.text_len; usz len = text.len > remaining ? remaining : text.len; - char[] s = ctx.input.keyboard.text[ctx.input.keyboard.text_len ..]; + char[] s = ctx.current_input.keyboard.text[ctx.current_input.keyboard.text_len ..]; s[..len-1] = text[..len-1]; - ctx.input.keyboard.text_len += len; - ctx.input.events.text_input = true; + ctx.current_input.keyboard.text_len += len; + ctx.current_input.events.text_input = true; } fn void Ctx.input_text_unicode(&ctx, char[] text) @@ -190,8 +190,8 @@ fn void Ctx.input_text_unicode(&ctx, char[] text) if (text.ptr == null || text.len == 0) { return; } char[32] tmp; - usz remaining = ctx.input.keyboard.text.len - ctx.input.keyboard.text_len; - char[] s = ctx.input.keyboard.text[ctx.input.keyboard.text_len ..]; + usz remaining = ctx.current_input.keyboard.text.len - ctx.current_input.keyboard.text_len; + char[] s = ctx.current_input.keyboard.text[ctx.current_input.keyboard.text_len ..]; usz off; foreach (idx, cp: text) { @@ -200,9 +200,9 @@ fn void Ctx.input_text_unicode(&ctx, char[] text) s[off..off+enc] = tmp[..enc]; off += enc; } - ctx.input.keyboard.text_len += off; + ctx.current_input.keyboard.text_len += off; - ctx.input.events.text_input = true; + ctx.current_input.events.text_input = true; } fn void Ctx.input_char(&ctx, char c) @@ -218,6 +218,6 @@ fn ModKeys Ctx.get_mod(&ctx) => ctx.input.keyboard.modkeys; // TODO: make this call repetible to input modkeys one by one fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys) { - ctx.input.keyboard.modkeys = modkeys; - ctx.input.events.mod_key = (uint)ctx.input.keyboard.modkeys != 0; + ctx.current_input.keyboard.modkeys = modkeys; + ctx.current_input.events.mod_key = (uint)ctx.current_input.keyboard.modkeys != 0; } diff --git a/lib/ugui.c3l/src/layout.c3 b/lib/ugui.c3l/src/layout.c3 index e7d4606..8f63c5f 100644 --- a/lib/ugui.c3l/src/layout.c3 +++ b/lib/ugui.c3l/src/layout.c3 @@ -297,72 +297,6 @@ fn void resolve_placement(Elem* c, Elem* p) } } -macro bool Ctx.is_hovered(&ctx, Elem *elem) => ctx.input.mouse.pos.in_rect(elem.bounds); - -fn void Ctx.update_elem_events(&ctx, Elem* elem) -{ - bool hover = ctx.is_hovered(elem); - bool focus = ctx.focus_id == elem.id || (hover && ctx.is_mouse_pressed(BTN_ANY)); - - if (ctx.is_mouse_pressed(BTN_ANY) && !hover){ - focus = false; - if (ctx.focus_id == elem.id) ctx.focus_id = 0; - } - - if (hover) { - // TODO: this constant cache lookup could be slow - Elem* prev_hover = ctx.find_elem(ctx.hover_id); - if (prev_hover.id != elem.id && prev_hover.flags.shown) { - if (prev_hover.z_index <= elem.z_index) { - // remove hover events from prev_hover - prev_hover.events.mouse_hover = false; - prev_hover.events.mouse_press = false; - prev_hover.events.mouse_release = false; - prev_hover.events.mouse_hold = false; - } else { - hover = false; - } - } - } - - if (focus) { - // TODO: this constant cache lookup could be slow - Elem* prev_focus = ctx.find_elem(ctx.hover_id); - // update the focus id and element only if the current element is at the same level - // or above the previous element that held focus - if (prev_focus.z_index <= elem.z_index) { - // remove focus events from prev_focus - prev_focus.events.has_focus = false; - prev_focus.events.mouse_press = false; - prev_focus.events.mouse_release = false; - prev_focus.events.mouse_hold = false; - prev_focus.events.key_press = false; - prev_focus.events.key_release = false; - prev_focus.events.key_repeat = false; - prev_focus.events.text_input = false; - // update the hover id - ctx.focus_id = elem.id; - } else if (prev_focus.flags.shown) { - focus = false; - } - } - - if (hover) ctx.hover_id = elem.id; - - ElemEvents ev = { - .has_focus = focus, - .mouse_hover = hover, - .mouse_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY), - .mouse_release = hover && focus && ctx.is_mouse_released(BTN_ANY), - .mouse_hold = hover && focus && ctx.is_mouse_down(BTN_ANY), - .key_press = focus && ctx.input.events.key_press, - .key_release = focus && ctx.input.events.key_release, - .key_repeat = focus && ctx.input.events.key_repeat, - .text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT), - }; - elem.events = ev; -} - fn void? Ctx.layout_element_tree(&ctx) { int current; @@ -400,11 +334,11 @@ fn void? Ctx.layout_element_tree(&ctx) } else { resolve_placement(c, p); update_children_bounds(c, p); - - // reset shown flag } - ctx.update_elem_events(c); - c.flags.shown = false; + + // FIXME: this stuff would be better elsewhere but we are already iteraring through all + // elements so here it fits really well + ctx.update_hover_and_focus(c); } } } diff --git a/lib/ugui.c3l/src/shapes.c3 b/lib/ugui.c3l/src/shapes.c3 index f76479d..e57f494 100644 --- a/lib/ugui.c3l/src/shapes.c3 +++ b/lib/ugui.c3l/src/shapes.c3 @@ -199,6 +199,8 @@ macro bool Point.in_rect(Point p, Rect r) return (p.x >= r.x && p.x <= r.x + r.w) && (p.y >= r.y && p.y <= r.y + r.h); } +macro bool Point.outside(Point p, Rect r) => !p.in_rect(r); + macro Point Point.add(Point a, Point b) @operator_s(+) => {.x = a.x+b.x, .y = a.y+b.y}; macro Point Point.sub(Point a, Point b) @operator_s(-) => {.x = a.x-b.x, .y = a.y-b.y}; macro Point Point.neg(Point p) @operator_s(-) => {-p.x, -p.y}; @@ -241,20 +243,22 @@ macro uint Color.to_uint(c) => c.r | (c.g << 8) | (c.b << 16) | (c.a << 24); // SIZE // // ---------------------------------------------------------------------------------- // +macro short short.add_no_of(short a, short b) => (short)max(min((int)a + (int)b, short.max), short.min) @inline; + struct Size { short min, max; } macro Size @grow() => {.min = 0, .max = 0}; macro Size @exact(short s) => {.min = s, .max = s}; - // PROBLEM WE ARE OVERFLOWING -macro Size @fit(short min = 0, short max = 999) => {.min = min, .max = max}; +macro Size @fit(short min = 0, short max = short.max) => {.min = min, .max = max}; + macro bool Size.@is_grow(s) => (s.min == 0 && s.max == 0); macro bool Size.@is_exact(s) => (s.min == s.max && s.min != 0); macro bool Size.@is_fit(s) => (s.min != s.max); -macro Size Size.add(a, Size b) @operator_s(+) => {.min = a.min+b.min, .max = a.max+b.max}; -macro Size Size.sub(a, Size b) @operator_s(-) => {.min = a.min-b.min, .max = a.max-b.max}; +macro Size Size.add(a, Size b) @operator_s(+) => {.min = a.min.add_no_of(b.min), .max = a.max.add_no_of(b.max)}; +macro Size Size.sub(a, Size b) @operator_s(-) => {.min = a.min.add_no_of(-b.min), .max = a.max.add_no_of(-b.max)}; macro Size Size.combine(a, Size b) => {.min = max(a.min, b.min), .max = min(a.max, b.max)}; macro Size Size.comb_max(a, Size b) => {.min = max(a.min, b.min), .max = max(a.max, b.max)}; diff --git a/lib/ugui.c3l/src/widgets/div.c3 b/lib/ugui.c3l/src/widgets/div.c3 index 37dd5b3..a00dd1f 100644 --- a/lib/ugui.c3l/src/widgets/div.c3 +++ b/lib/ugui.c3l/src/widgets/div.c3 @@ -87,7 +87,6 @@ fn void? Ctx.div_begin_id(&ctx, elem.div.scroll_x.enabled = scroll_x; elem.div.scroll_y.enabled = scroll_y; - if (ctx.input.z_index < elem.z_index) ctx.input.z_index = elem.z_index; // update layout with correct info elem.layout = { @@ -199,7 +198,6 @@ fn bool? Ctx.popup_begin_id(&ctx, 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 = { @@ -222,7 +220,7 @@ fn bool? Ctx.popup_begin_id(&ctx, //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) { + if ((ctx.mouse_released() & BTN_ANY) && ctx.input.mouse.pos.outside(elem.bounds)) { return false; } diff --git a/src/main.c3 b/src/main.c3 index f1d31f2..90b068a 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -208,7 +208,7 @@ fn int main(String[] args) if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true; -const String APPLICATION = "popup"; +const String APPLICATION = "calculator"; $switch APPLICATION: $case "debug": debug_app(&ui); @@ -397,6 +397,7 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te) 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) { @@ -468,19 +469,17 @@ fn void popup(ugui::Ctx* ui) static bool toggle; static ugui::Point pos; + if (toggle) { + toggle = ui.popup_begin(pos, ugui::@fit(), ugui::@fit(), COLUMN)!!; + ui.button("POP")!!; + ui.button("UP")!!; + ui.div_end()!!; + } ui.@center(COLUMN) { - if (toggle) { - toggle = ui.popup_begin(pos, ugui::@fit(), ugui::@fit(), COLUMN)!!; - ui.button("POP")!!; - ui.button("UP")!!; - ui.div_end()!!; - } if (ui.button("ciao")!!.mouse_release) { pos = ui.input.mouse.pos; toggle = ~toggle; } if (ui.button("mamma")!!.mouse_press) ugui::println("pressed!"); }!!; - - ugui::println("focus id: ", ui.focus_id, " hover id: ", ui.hover_id); }