actually correct input handling

This commit is contained in:
Alessandro Mauri 2025-10-10 22:31:28 +02:00
parent fb3a964f7f
commit b5ef86d092
7 changed files with 144 additions and 181 deletions

17
TODO
View File

@ -4,12 +4,11 @@
[x] Implement div.view and scrollbars [x] Implement div.view and scrollbars
[x] Port font system from C to C3 (rewrite1) [x] Port font system from C to C3 (rewrite1)
[ ] Update ARCHITECTURE.md [ ] Update ARCHITECTURE.md
[ ] Write a README.md [x] Write a README.md
[x] Use an arena allocator for cache [x] Use an arena allocator for cache
[ ] Do not redraw if there was no update (no layout and no draw) [ ] 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) [ ] Do command buffer damage tracking based on a context grid (see rxi writeup)
[x] Better handling of the active and focused widgets, try [x] Better handling of the active and focused widgets, try to maintain focus until mouse release (fix scroll bars)
to maintain focus until mouse release (fix scroll bars)
[x] Clip element bounds to parent div, specifically text [x] Clip element bounds to parent div, specifically text
[ ] Resizeable divs [ ] Resizeable divs
[x] Implement a z index and sort command buffer based on that [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 [ ] .png
[ ] .jpg [ ] .jpg
[ ] gif support? [ ] 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] Maybe SDF sprites??
[x] Stylesheets and stylesheet import [x] Stylesheets and stylesheet import
[x] use SDF to draw anti-aliased rounded rectangles https://zed.dev/blog/videogame [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 [ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
internal use functions (used to create widgets) internal use functions (used to create widgets)
[x] The render loop RAPES the gpu, valve pls fix [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 each struct Elem, struct Cmd, etc. is as big as the largest element. It would
be better to use a different allcation strategy. be better to use a different allcation strategy.
[ ] Add a way to handle time events like double clicks [ ] 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] Center elements to the row/column
[x] Text wrapping / reflow [x] Text wrapping / reflow
[x] Implement a better and unified way to place a glyph and get the cursor position, maybe with a struct [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) [x] Consider a multi-pass recursive approach to layout (like https://github.com/nicbarker/clay)
instead of the curren multi-frame approach. instead of the curren multi-frame approach.
[x] Implement column/row sizing (min, max) [x] Implement column/row sizing (min, max)
@ -79,8 +78,8 @@ to maintain focus until mouse release (fix scroll bars)
[x] Mouse scroll wheel [x] Mouse scroll wheel
[ ] Touch input [ ] Touch input
[x] Do not set input event to true if the movement was zero (like no mouse movement) [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 [x] 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] 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 of the context, this causes a bug where only the first char is actually used
## Commands ## Commands
@ -89,7 +88,7 @@ to maintain focus until mouse release (fix scroll bars)
- border width - border width
- border radius - border radius
[x] add a command to update an atlas [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 [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) -> ... draw_text(a, pos) -> off = compute_bounds(a) -> draw_text(b, pos+off) -> ...
[ ] Rounded rectangle with different radius for each corner [ ] Rounded rectangle with different radius for each corner

View File

@ -90,20 +90,8 @@ alias ElemCache = cache::Cache{Id, Elem, MAX_ELEMENTS};
faultdef INVALID_SIZE, EVENT_UNSUPPORTED, WRONG_ELEMENT_TYPE, WRONG_ID; faultdef INVALID_SIZE, EVENT_UNSUPPORTED, WRONG_ELEMENT_TYPE, WRONG_ID;
struct Ctx { struct InputData {
IdTree tree;
ElemCache cache;
CmdQueue cmd_queue;
StyleMap styles;
// total size in pixels of the context
ushort width, height;
Font font;
SpriteAtlas sprite_atlas;
bool has_focus;
struct input {
InputEvents events; InputEvents events;
int z_index; // the layer the input events need to be propageted to
struct mouse { struct mouse {
Point pos, delta; Point pos, delta;
// mouse_down: bitmap of mouse buttons that are held // mouse_down: bitmap of mouse buttons that are held
@ -122,6 +110,19 @@ struct Ctx {
} }
} }
struct Ctx {
IdTree tree;
ElemCache cache;
CmdQueue cmd_queue;
StyleMap styles;
// total size in pixels of the context
ushort width, height;
Font font;
SpriteAtlas sprite_atlas;
bool has_focus;
InputData input, current_input;
Id hover_id; Id hover_id;
Id focus_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 // 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 // computation needed, child elements need to check the mouse positon and
// other stuff // other stuff
ctx.input.z_index = 0;
//elem.flags.has_focus = ctx.has_focus; //elem.flags.has_focus = ctx.has_focus;
elem.bounds = {0, 0, ctx.width, ctx.height}; elem.bounds = {0, 0, ctx.width, ctx.height};
@ -262,30 +262,41 @@ fn void? Ctx.frame_begin(&ctx)
elem.layout.h = @exact(ctx.height); elem.layout.h = @exact(ctx.height);
ctx.div_scissor = elem.bounds; 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) 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()!; Elem* root = ctx.get_active_div()!;
if (root.id != ROOT_ID) { if (root.id != ROOT_ID) {
io::printn(root.id);
return WRONG_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 // DO THE LAYOUT
ctx.layout_element_tree()!; 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 // 1. clear the tree
ctx.tree.nuke(); ctx.tree.nuke();
// 2. clear input fields
ctx.input.events = (InputEvents)0;
ctx.input.keyboard.text_len = 0;
// send atlas updates // send atlas updates
if (ctx.font.should_update) { if (ctx.font.should_update) {
ctx.push_update_atlas(&ctx.font.atlas)!; ctx.push_update_atlas(&ctx.font.atlas)!;
@ -325,27 +336,47 @@ $endif
// } // }
} }
// TODO: add other events
// FIXME: this does not work with touch macro bool Ctx.is_hovered(&ctx, Elem *elem) => ctx.input.mouse.pos.in_rect(elem.bounds);
// FIXME: hacked together, please do better
/* // Check if the element is hovered and/or focused, if it is update the context ids.
fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem) // 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 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){ if (hover) {
focus = false; Elem* prev_hover = ctx.find_elem(ctx.hover_id);
if (ctx.focus_id == elem.id) ctx.focus_id = 0; 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 (hover) { ctx.hover_id = elem.id; } if (focus) {
if (focus) { ctx.focus_id = elem.id; } 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;
ElemEvents ev = { focus = !(different && shown && above);
}
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);
e.events = {
.has_focus = focus, .has_focus = focus,
.mouse_hover = hover, .mouse_hover = hover,
.mouse_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY), .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, .key_repeat = focus && ctx.input.events.key_repeat,
.text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT), .text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT),
}; };
return ev;
} }
*/

View File

@ -71,7 +71,7 @@ const ModKeys KEY_ANY = (ModKeys)(ModKeys.inner.max);
fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String ...keys) 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; bool is_keys = true;
String haystack = (String)ctx.get_keys(); String haystack = (String)ctx.get_keys();
foreach (needle: keys) { foreach (needle: keys) {
@ -86,7 +86,7 @@ fn void? Ctx.input_window_size(&ctx, short width, short height)
if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {
return INVALID_SIZE?; 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.width = width;
ctx.height = height; 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 // FIXME: raylib only has an API to query the focus status so we have to
// update the input flag only if the focus changed // 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; ctx.has_focus = has_focus;
} }
macro Ctx.mouse_pressed(&ctx) => ctx.input.mouse.updated & 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.input.mouse.updated & ~ctx.input.mouse.down; macro Ctx.mouse_released(&ctx) => ctx.current_input.mouse.updated & ~ctx.current_input.mouse.down;
macro Ctx.mouse_down(&ctx) => ctx.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 // FIXME: hthis compairson could be done with a cast using MouseButtons.inner
// property but I could not figure out how // 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 // Mouse Buttons down
fn void Ctx.input_mouse_button(&ctx, MouseButtons buttons) fn void Ctx.input_mouse_button(&ctx, MouseButtons buttons)
{ {
ctx.input.mouse.updated = ctx.input.mouse.down ^ buttons; ctx.current_input.mouse.updated = ctx.current_input.mouse.down ^ buttons;
ctx.input.mouse.down = buttons; ctx.current_input.mouse.down = buttons;
ctx.input.events.mouse_btn = (uint)ctx.input.mouse.down != 0 || (uint)ctx.input.mouse.updated != 0; 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 // Mouse was moved, report absolute position
fn void Ctx.input_mouse_abs(&ctx, short x, short y) fn void Ctx.input_mouse_abs(&ctx, short x, short y)
{ {
ctx.input.mouse.pos.x = math::clamp(x, (short)0, ctx.width); ctx.current_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.y = math::clamp(y, (short)0, ctx.height);
short dx, dy; short dx, dy;
dx = x - ctx.input.mouse.pos.x; dx = x - ctx.current_input.mouse.pos.x;
dy = y - ctx.input.mouse.pos.y; dy = y - ctx.current_input.mouse.pos.y;
ctx.input.mouse.delta.x = dx; ctx.current_input.mouse.delta.x = dx;
ctx.input.mouse.delta.y = dy; 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 // Mouse was moved, report relative motion
fn void Ctx.input_mouse_delta(&ctx, short dx, short dy) fn void Ctx.input_mouse_delta(&ctx, short dx, short dy)
{ {
ctx.input.mouse.delta.x = dx; ctx.current_input.mouse.delta.x = dx;
ctx.input.mouse.delta.y = dy; ctx.current_input.mouse.delta.y = dy;
short mx, my; short mx, my;
mx = ctx.input.mouse.pos.x + dx; mx = ctx.current_input.mouse.pos.x + dx;
my = ctx.input.mouse.pos.y + dy; my = ctx.current_input.mouse.pos.y + dy;
ctx.input.mouse.pos.x = math::clamp(mx, (short)0, ctx.width); ctx.current_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.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) 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.current_input.mouse.scroll.x = (short)((float)-x*scale);
ctx.input.mouse.scroll.y = (short)((float)-y*scale); ctx.current_input.mouse.scroll.y = (short)((float)-y*scale);
ctx.input.events.mouse_scroll = x !=0 || y != 0; ctx.current_input.events.mouse_scroll = x !=0 || y != 0;
} }
fn void Ctx.input_key_press(&ctx) 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) 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) 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 // 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; } 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; 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]; s[..len-1] = text[..len-1];
ctx.input.keyboard.text_len += len; ctx.current_input.keyboard.text_len += len;
ctx.input.events.text_input = true; ctx.current_input.events.text_input = true;
} }
fn void Ctx.input_text_unicode(&ctx, char[] text) 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; } if (text.ptr == null || text.len == 0) { return; }
char[32] tmp; char[32] tmp;
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;
char[] s = ctx.input.keyboard.text[ctx.input.keyboard.text_len ..]; char[] s = ctx.current_input.keyboard.text[ctx.current_input.keyboard.text_len ..];
usz off; usz off;
foreach (idx, cp: text) { foreach (idx, cp: text) {
@ -200,9 +200,9 @@ fn void Ctx.input_text_unicode(&ctx, char[] text)
s[off..off+enc] = tmp[..enc]; s[off..off+enc] = tmp[..enc];
off += 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) 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 // TODO: make this call repetible to input modkeys one by one
fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys) fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys)
{ {
ctx.input.keyboard.modkeys = modkeys; ctx.current_input.keyboard.modkeys = modkeys;
ctx.input.events.mod_key = (uint)ctx.input.keyboard.modkeys != 0; ctx.current_input.events.mod_key = (uint)ctx.current_input.keyboard.modkeys != 0;
} }

View File

@ -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) fn void? Ctx.layout_element_tree(&ctx)
{ {
int current; int current;
@ -400,11 +334,11 @@ fn void? Ctx.layout_element_tree(&ctx)
} else { } else {
resolve_placement(c, p); resolve_placement(c, p);
update_children_bounds(c, p); update_children_bounds(c, p);
}
// reset shown flag // FIXME: this stuff would be better elsewhere but we are already iteraring through all
} // elements so here it fits really well
ctx.update_elem_events(c); ctx.update_hover_and_focus(c);
c.flags.shown = false;
} }
} }
} }

View File

@ -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); 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.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.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}; 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 // // 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 { struct Size {
short min, max; short min, max;
} }
macro Size @grow() => {.min = 0, .max = 0}; macro Size @grow() => {.min = 0, .max = 0};
macro Size @exact(short s) => {.min = s, .max = s}; macro Size @exact(short s) => {.min = s, .max = s};
// PROBLEM WE ARE OVERFLOWING macro Size @fit(short min = 0, short max = short.max) => {.min = min, .max = max};
macro Size @fit(short min = 0, short max = 999) => {.min = min, .max = max};
macro bool Size.@is_grow(s) => (s.min == 0 && s.max == 0); 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_exact(s) => (s.min == s.max && s.min != 0);
macro bool Size.@is_fit(s) => (s.min != s.max); 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.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-b.min, .max = a.max-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.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)}; macro Size Size.comb_max(a, Size b) => {.min = max(a.min, b.min), .max = max(a.max, b.max)};

View File

@ -87,7 +87,6 @@ fn void? Ctx.div_begin_id(&ctx,
elem.div.scroll_x.enabled = scroll_x; elem.div.scroll_x.enabled = scroll_x;
elem.div.scroll_y.enabled = scroll_y; 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 // update layout with correct info
elem.layout = { elem.layout = {
@ -199,7 +198,6 @@ fn bool? Ctx.popup_begin_id(&ctx,
elem.div.scroll_x.enabled = scroll_x; elem.div.scroll_x.enabled = scroll_x;
elem.div.scroll_y.enabled = scroll_y; elem.div.scroll_y.enabled = scroll_y;
elem.z_index++; elem.z_index++;
if (ctx.input.z_index < elem.z_index) ctx.input.z_index = elem.z_index;
// update layout with correct info // update layout with correct info
elem.layout = { elem.layout = {
@ -222,7 +220,7 @@ fn bool? Ctx.popup_begin_id(&ctx,
//elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
// check close condition, mouse release anywhere outside the div bounds // 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; return false;
} }

View File

@ -208,7 +208,7 @@ fn int main(String[] args)
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true; if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true;
const String APPLICATION = "popup"; const String APPLICATION = "calculator";
$switch APPLICATION: $switch APPLICATION:
$case "debug": $case "debug":
debug_app(&ui); debug_app(&ui);
@ -397,6 +397,7 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
ui.button("Quattro")!!; ui.button("Quattro")!!;
ui.div_end()!!; ui.div_end()!!;
} }
// ui input/output // ui input/output
ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) { // center everything on the screen ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) { // center everything on the screen
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT) { ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT) {
@ -468,19 +469,17 @@ fn void popup(ugui::Ctx* ui)
static bool toggle; static bool toggle;
static ugui::Point pos; static ugui::Point pos;
ui.@center(COLUMN) {
if (toggle) { if (toggle) {
toggle = ui.popup_begin(pos, ugui::@fit(), ugui::@fit(), COLUMN)!!; toggle = ui.popup_begin(pos, ugui::@fit(), ugui::@fit(), COLUMN)!!;
ui.button("POP")!!; ui.button("POP")!!;
ui.button("UP")!!; ui.button("UP")!!;
ui.div_end()!!; ui.div_end()!!;
} }
ui.@center(COLUMN) {
if (ui.button("ciao")!!.mouse_release) { if (ui.button("ciao")!!.mouse_release) {
pos = ui.input.mouse.pos; pos = ui.input.mouse.pos;
toggle = ~toggle; toggle = ~toggle;
} }
if (ui.button("mamma")!!.mouse_press) ugui::println("pressed!"); if (ui.button("mamma")!!.mouse_press) ugui::println("pressed!");
}!!; }!!;
ugui::println("focus id: ", ui.focus_id, " hover id: ", ui.hover_id);
} }