This commit is contained in:
Alessandro Mauri 2025-10-12 13:19:52 +02:00
commit eb62e9ad72
10 changed files with 179 additions and 124 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

@ -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 {
@ -89,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
@ -121,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;
@ -175,6 +177,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) {
@ -247,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};
@ -260,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)!;
@ -323,38 +336,47 @@ $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) macro bool Ctx.is_hovered(&ctx, Elem *elem) => ctx.input.mouse.pos.in_rect(elem.bounds);
{
return ctx.focus_id == elem.id;
}
// TODO: add other events // Check if the element is hovered and/or focused, if it is update the context ids.
// FIXME: this does not work with touch // The order in which the elements are passed to this function is not relevant
// FIXME: hacked together, please do better fn void Ctx.update_hover_and_focus(&ctx, Elem* elem)
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 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),
@ -365,5 +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

@ -335,6 +335,10 @@ fn void? Ctx.layout_element_tree(&ctx)
resolve_placement(c, p); resolve_placement(c, p);
update_children_bounds(c, p); update_children_bounds(c, p);
} }
// 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);
} }
} }
} }

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

@ -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) {
@ -80,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 = {
@ -102,7 +108,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
@ -192,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 = {
@ -212,10 +217,10 @@ 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) && ctx.input.mouse.pos.outside(elem.bounds)) {
return false; return false;
} }

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

@ -214,6 +214,8 @@ $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
@ -395,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) {
@ -459,5 +462,24 @@ 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;
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 (ui.button("ciao")!!.mouse_release) {
pos = ui.input.mouse.pos;
toggle = ~toggle;
}
if (ui.button("mamma")!!.mouse_press) ugui::println("pressed!");
}!!;
} }