first draft

This commit is contained in:
Alessandro Mauri 2025-10-08 22:16:17 +02:00
parent 884105a4e2
commit fb3a964f7f
7 changed files with 120 additions and 28 deletions

View File

@ -34,7 +34,8 @@ enum ElemType {
bitstruct ElemFlags : uint { bitstruct ElemFlags : uint {
bool updated : 0; bool updated : 0;
bool is_new : 1; bool is_new : 1; // element is new in the cache
bool shown : 2; // element has been shown (drawn) this frame
} }
bitstruct ElemEvents : uint { bitstruct ElemEvents : uint {
@ -175,6 +176,7 @@ fn PElemTuple? Ctx.get_elem(&ctx, Id id, ElemType type)
elem = ctx.cache.get_or_insert(&&(Elem){}, id, &is_new)!; elem = ctx.cache.get_or_insert(&&(Elem){}, id, &is_new)!;
elem.flags = (ElemFlags)0; elem.flags = (ElemFlags)0;
elem.flags.is_new = is_new; elem.flags.is_new = is_new;
elem.flags.shown = true;
elem.id = id; elem.id = id;
elem.layout = {}; elem.layout = {};
if (is_new == false && elem.type != type) { if (is_new == false && elem.type != type) {
@ -323,24 +325,13 @@ $endif
// } // }
} }
<*
* @ensure elem != null
*>
macro bool Ctx.is_hovered(&ctx, Elem *elem)
{
return ctx.input.mouse.pos.in_rect(elem.bounds);
}
macro bool Ctx.elem_focus(&ctx, Elem *elem)
{
return ctx.focus_id == elem.id;
}
// TODO: add other events // TODO: add other events
// FIXME: this does not work with touch // FIXME: this does not work with touch
// FIXME: hacked together, please do better // FIXME: hacked together, please do better
/*
fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem) fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem)
{ {
// FIXME: gross hack to not leak events under a pop-up
if (ctx.input.z_index != elem.z_index) return {}; if (ctx.input.z_index != elem.z_index) return {};
bool hover = ctx.is_hovered(elem); bool hover = ctx.is_hovered(elem);
@ -367,3 +358,4 @@ fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem)
}; };
return ev; return ev;
} }
*/

View File

@ -297,6 +297,72 @@ 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;
@ -334,7 +400,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
}
ctx.update_elem_events(c);
c.flags.shown = false;
} }
} }
} }

View File

@ -46,7 +46,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
update_parent_size(elem, parent); update_parent_size(elem, parent);
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
// if (ctx.input.z_index == elem.z_index) println("true ", elem.z_index); // if (ctx.input.z_index == elem.z_index) println("true ", elem.z_index);
Rect content_bounds = elem.content_bounds(); Rect content_bounds = elem.content_bounds();
@ -66,7 +66,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
}; };
//text_bounds = text_size.center_to(text_bounds); //text_bounds = text_size.center_to(text_bounds);
bool is_active = ctx.elem_focus(elem) || elem.events.mouse_hover; bool is_active = elem.events.has_focus || elem.events.mouse_hover;
Style s = *style; Style s = *style;
if (is_active) { if (is_active) {
s.secondary = s.primary; s.secondary = s.primary;
@ -115,7 +115,7 @@ fn void? Ctx.checkbox_id(&ctx, Id id, String description, bool* active, String t
update_parent_size(elem, parent); update_parent_size(elem, parent);
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active); if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
@ -189,7 +189,7 @@ fn void? Ctx.toggle_id(&ctx, Id id, String description, bool* active)
update_parent_size(elem, parent); update_parent_size(elem, parent);
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active); if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset); Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);

View File

@ -18,6 +18,13 @@ struct ElemDiv {
} }
macro Ctx.@center(&ctx, LayoutDirection dir = ROW, ...; @body())
{
ctx.@div(@grow(), @grow(), dir, CENTER) {
@body();
}!;
}
macro Ctx.@row(&ctx, Anchor anchor = TOP_LEFT, ...; @body()) macro Ctx.@row(&ctx, Anchor anchor = TOP_LEFT, ...; @body())
{ {
ctx.@div(@fit(), @fit(), ROW, anchor: anchor) { ctx.@div(@fit(), @fit(), ROW, anchor: anchor) {
@ -102,7 +109,7 @@ fn void? Ctx.div_begin_id(&ctx,
ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset); ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset);
ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!; ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!;
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
// TODO: check active // TODO: check active
// TODO: check resizeable // TODO: check resizeable
@ -212,7 +219,7 @@ fn bool? Ctx.popup_begin_id(&ctx,
ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset); ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset);
ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!; ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!;
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) && !elem.events.mouse_hover) {

View File

@ -45,9 +45,9 @@ fn ElemEvents? Ctx.slider_hor_id(&ctx, Id id, Size w, Size h, float* value, floa
elem.slider.handle = handle; elem.slider.handle = handle;
Point m = ctx.input.mouse.pos; Point m = ctx.input.mouse.pos;
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
if (ctx.elem_focus(elem) && ctx.is_mouse_down(BTN_LEFT)) { if (elem.events.has_focus && ctx.is_mouse_down(BTN_LEFT)) {
*value = calc_value(content_bounds.x, m.x, content_bounds.w, hw); *value = calc_value(content_bounds.x, m.x, content_bounds.w, hw);
elem.slider.handle.x = calc_slider(content_bounds.x, content_bounds.w-hw, *value); elem.slider.handle.x = calc_slider(content_bounds.x, content_bounds.w-hw, *value);
elem.events.update = true; elem.events.update = true;
@ -109,9 +109,9 @@ fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Size w, Size h, float* value, floa
elem.slider.handle = handle; elem.slider.handle = handle;
Point m = ctx.input.mouse.pos; Point m = ctx.input.mouse.pos;
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
if (ctx.elem_focus(elem) && ctx.is_mouse_down(BTN_LEFT)) { if (elem.events.has_focus && ctx.is_mouse_down(BTN_LEFT)) {
*value = calc_value(content_bounds.y, m.y, content_bounds.h, hh); *value = calc_value(content_bounds.y, m.y, content_bounds.h, hh);
elem.slider.handle.y = calc_slider(content_bounds.y, content_bounds.h-hh, *value); elem.slider.handle.y = calc_slider(content_bounds.y, content_bounds.h-hh, *value);
elem.events.update = true; elem.events.update = true;
@ -156,7 +156,7 @@ fn void? Ctx.scrollbar(&ctx, Id id, float *value, float handle_percent, bool ver
update_parent_size(elem, parent); update_parent_size(elem, parent);
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset); Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
short o = vertical ? content_bounds.y : content_bounds.x; short o = vertical ? content_bounds.y : content_bounds.x;
short m = vertical ? ctx.input.mouse.pos.y : ctx.input.mouse.pos.x; short m = vertical ? ctx.input.mouse.pos.y : ctx.input.mouse.pos.x;

View File

@ -65,7 +65,7 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te, Anchor
update_parent_size(elem, parent); update_parent_size(elem, parent);
// check input and update the text // check input and update the text
elem.events = ctx.get_elem_events(elem); //elem.events = ctx.get_elem_events(elem);
if (elem.events.text_input || elem.events.key_press) { if (elem.events.text_input || elem.events.key_press) {
ctx.text_edit(elem.text.te); ctx.text_edit(elem.text.te);

View File

@ -208,12 +208,14 @@ 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 = "calculator"; const String APPLICATION = "popup";
$switch APPLICATION: $switch APPLICATION:
$case "debug": $case "debug":
debug_app(&ui); debug_app(&ui);
$case "calculator": $case "calculator":
calculator(&ui, &te); calculator(&ui, &te);
$case "popup":
popup(&ui);
$endswitch $endswitch
// Timings counter // Timings counter
@ -459,5 +461,26 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!; ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!;
}!!; }!!;
}!!; }!!; }!!; }!!;
}
fn void popup(ugui::Ctx* ui)
{
static bool toggle;
static ugui::Point pos;
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);
} }