improved the api to not require explicit labels everywhere

This commit is contained in:
Alessandro Mauri 2025-06-30 18:24:50 +02:00
parent 972c9b581d
commit 586e81935c
7 changed files with 123 additions and 80 deletions

View file

@ -7,12 +7,13 @@ struct ElemButton {
int filler;
}
// draw a button, return the events on that button
// FIXME: "state" should be renamed "active" to toggle between an usable button and
// an inactive (greyed-out) button
fn ElemEvents? Ctx.button(&ctx, String label, Rect size, bool state = false)
macro Ctx.button(&ctx, Rect size, bool state = false, ...)
=> ctx.button_id(@compute_id($vasplat), size, state);
fn ElemEvents? Ctx.button_id(&ctx, Id id, Rect size, bool active)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -33,7 +34,7 @@ fn ElemEvents? Ctx.button(&ctx, String label, Rect size, bool state = false)
Color col = 0x0000ffffu.to_rgba();
elem.events = ctx.get_elem_events(elem);
if (state) {
if (active) {
col = 0xff0000ffu.to_rgba();
} else if (ctx.elem_focus(elem) || elem.events.mouse_hover) {
col = 0xff00ffffu.to_rgba();
@ -45,9 +46,11 @@ fn ElemEvents? Ctx.button(&ctx, String label, Rect size, bool state = false)
return elem.events;
}
fn ElemEvents? Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,short.max}, bool state = false)
macro Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,short.max}, bool active = false, ...)
=> ctx.button_label_id(@compute_id($vasplat), label, size, active);
fn ElemEvents? Ctx.button_label_id(&ctx, Id id, String label, Rect size, bool active)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -68,7 +71,7 @@ fn ElemEvents? Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,s
Color col = 0x0000ffffu.to_rgba();
elem.events = ctx.get_elem_events(elem);
if (state) {
if (active) {
col = 0xff0000ffu.to_rgba();
} else if (ctx.elem_focus(elem) || elem.events.mouse_hover) {
col = 0xff00ffffu.to_rgba();
@ -86,9 +89,11 @@ fn ElemEvents? Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,s
return elem.events;
}
fn ElemEvents? Ctx.button_icon(&ctx, String label, String icon, String on_icon = "", bool state = false)
macro Ctx.button_icon(&ctx, String icon, String on_icon = "", bool active = false, ...)
=> ctx.button_icon_id(@compute_id($vasplat), icon, on_icon, active);
fn ElemEvents? Ctx.button_icon_id(&ctx, Id id, String icon, String on_icon, bool active)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -115,7 +120,7 @@ fn ElemEvents? Ctx.button_icon(&ctx, String label, String icon, String on_icon =
elem.events = ctx.get_elem_events(elem);
Id tex_id = ctx.sprite_atlas.id;
if (state && on_icon != "") {
if (active && on_icon != "") {
ctx.push_sprite(elem.bounds, on_sprite.uv(), tex_id, parent.div.z_index, type: on_sprite.type)!;
} else {
ctx.push_sprite(elem.bounds, def_sprite.uv(), tex_id, parent.div.z_index, type: def_sprite.type)!;
@ -129,9 +134,11 @@ fn ElemEvents? Ctx.button_icon(&ctx, String label, String icon, String on_icon =
// FIXME: this should be inside the style
const ushort DEFAULT_CHECKBOX_SIZE = 16;
fn void? Ctx.checkbox(&ctx, String label, String description, Point off, bool* state, String tick_sprite = {})
macro Ctx.checkbox(&ctx, String desc, Point off, bool* active, String tick_sprite = {}, ...)
=> ctx.checkbox_id(@compute_id($vasplat), desc, off, active, tick_sprite);
fn void? Ctx.checkbox_id(&ctx, Id id, String description, Point off, bool* active, String tick_sprite)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -154,17 +161,17 @@ fn void? Ctx.checkbox(&ctx, String label, String description, Point off, bool* s
if (elem.bounds.is_null()) return;
elem.events = ctx.get_elem_events(elem);
if (elem.events.mouse_hover && elem.events.mouse_release) *state = !(*state);
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
Color col;
if (tick_sprite != {}) {
col = ctx.style.bgcolor;
ctx.push_rect(elem.bounds, col, parent.div.z_index, do_border: true, do_radius: true)!;
if (*state) {
if (*active) {
ctx.draw_sprite_raw(tick_sprite, elem.bounds)!;
}
} else {
if (*state) {
if (*active) {
col = 0xff0000ffu.to_rgba();
} else {
col = 0xff00ffffu.to_rgba();
@ -176,9 +183,11 @@ fn void? Ctx.checkbox(&ctx, String label, String description, Point off, bool* s
// FIXME: this should be inside the style
const short DEFAULT_SWITCH_SIZE = 16;
fn void? Ctx.toggle(&ctx, String label, String description, Point off, bool* state)
macro Ctx.toggle(&ctx, String desc, Point off, bool* active)
=> ctx.toggle_id(@compute_id($vasplat), desc, off, active);
fn void? Ctx.toggle_id(&ctx, Id id, String description, Point off, bool* active)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -201,10 +210,10 @@ fn void? Ctx.toggle(&ctx, String label, String description, Point off, bool* sta
if (elem.bounds.is_null()) return;
elem.events = ctx.get_elem_events(elem);
if (elem.events.mouse_hover && elem.events.mouse_release) *state = !(*state);
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
Color col;
if (*state) {
if (*active) {
col = 0xff0000ffu.to_rgba();
} else {
col = 0xff00ffffu.to_rgba();
@ -212,6 +221,6 @@ fn void? Ctx.toggle(&ctx, String label, String description, Point off, bool* sta
// Draw the button
// FIXME: THIS IS SHIT
ctx.push_rect(elem.bounds, ctx.style.bgcolor, parent.div.z_index, do_border: true, do_radius: true)!;
Rect t = elem.bounds.add({*state ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6});
Rect t = elem.bounds.add({*active ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6});
ctx.push_rect(t, col, parent.div.z_index, do_border: false, do_radius: true)!;
}

View file

@ -136,10 +136,9 @@ const uint GOLDEN_RATIO = 0x9E3779B9;
// generate an id combining the hashes of the parent id and the label
// with the Cantor pairing function
macro Id? Ctx.gen_id(&ctx, String label)
fn Id? Ctx.gen_id(&ctx, Id id2)
{
Id id1 = ctx.tree.get(ctx.active_div)!;
Id id2 = label.hash();
// Mix the two IDs non-linearly
Id mixed = id1 ^ id2.rotate_left(13);
mixed ^= id1.rotate_left(7);
@ -147,6 +146,16 @@ macro Id? Ctx.gen_id(&ctx, String label)
return mixed;
}
// compute the id from arguments and the line of the call
macro Id @compute_id(...)
{
Id id = (Id)$$LINE.hash() ^ (Id)@str_hash($$FILE);
$for var $i = 0; $i < $vacount; $i++:
id ^= (Id)$vaconst[$i].hash();
$endfor
return id;
}
// get or push an element from the cache, return a pointer to it
// resets all flags except is_new which is set accordingly
fn Elem*? Ctx.get_elem(&ctx, Id id)

View file

@ -29,9 +29,11 @@ struct ElemDiv {
// if the width or height are negative the width or height will be calculated based on the children size
// sort similar to a flexbox, and the minimum size is set by the negative of the width or height
// FIXME: there is a bug if the size.w or size.h == -0
fn void? Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, bool scroll_y = false)
macro Ctx.div_begin(&ctx, Rect size, bool scroll_x = false, bool scroll_y = false, ...)
=> ctx.div_begin_id(@compute_id($vasplat), size, scroll_x, scroll_y);
fn void? Ctx.div_begin_id(&ctx, Id id, Rect size, bool scroll_x, bool scroll_y)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem* parent = ctx.get_parent()!;
Elem* elem = ctx.get_elem(id)!;
@ -110,8 +112,8 @@ fn void? Ctx.div_end(&ctx)
// vertical overflow
elem.div.scroll_y.on = cbc.y > bc.y && elem.div.scroll_y.enabled;
Id hsid = ctx.gen_id("div_scrollbar_horizontal")!;
Id vsid = ctx.gen_id("div_scrollbar_vertical")!;
Id hsid = ctx.gen_id("div_scrollbar_horizontal".hash())!;
Id vsid = ctx.gen_id("div_scrollbar_vertical".hash())!;
short wdim = elem.div.scroll_y.on ? (ctx.focus_id == vsid || ctx.is_hovered(ctx.find_elem(vsid)) ? SCROLLBAR_DIM*3 : SCROLLBAR_DIM) : 0;
short hdim = elem.div.scroll_x.on ? (ctx.focus_id == hsid || ctx.is_hovered(ctx.find_elem(hsid)) ? SCROLLBAR_DIM*3 : SCROLLBAR_DIM) : 0;
@ -128,7 +130,7 @@ fn void? Ctx.div_end(&ctx)
};
Layout prev_l = elem.div.layout;
elem.div.layout = LAYOUT_ABSOLUTE;
ctx.slider_ver("div_scrollbar_vertical", vslider, &elem.div.scroll_y.value, max((float)bc.y / cbc.y, (float)0.15))!;
ctx.slider_ver_id(vsid, vslider, &elem.div.scroll_y.value, max((float)bc.y / cbc.y, (float)0.15))!;
elem.div.layout = prev_l;
}
@ -145,7 +147,7 @@ fn void? Ctx.div_end(&ctx)
};
Layout prev_l = elem.div.layout;
elem.div.layout = LAYOUT_ABSOLUTE;
ctx.slider_hor("div_scrollbar_horizontal", hslider, &elem.div.scroll_x.value, max((float)bc.x / cbc.x, (float)0.15))!;
ctx.slider_hor_id(hsid, hslider, &elem.div.scroll_x.value, max((float)bc.x / cbc.x, (float)0.15))!;
elem.div.layout = prev_l;
}

View file

@ -8,23 +8,31 @@ struct ElemSlider {
Rect handle;
}
/* handle
* +----+-----+---------------------+
* | |#####| |
* +----+-----+---------------------+
*/
macro Ctx.slider_hor(&ctx,
Rect size,
float* value,
float hpercent = 0.25,
Color bgcolor = 0x0000ffffu.to_rgba(),
Color handlecolor = 0x0ff000ffu.to_rgba(), ...)
=> ctx.slider_hor_id(@compute_id($vasplat), size, value, hpercent, bgcolor, handlecolor);
<*
@require value != null
*>
fn ElemEvents? Ctx.slider_hor(&ctx,
String label,
fn ElemEvents? Ctx.slider_hor_id(&ctx,
Id id,
Rect size,
float* value,
float hpercent = 0.25,
Color bgcolor = 0x0000ffffu.to_rgba(),
Color handlecolor = 0x0ff000ffu.to_rgba())
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -67,27 +75,35 @@ fn ElemEvents? Ctx.slider_hor(&ctx,
return elem.events;
}
/*
* +-+
* | |
* | |
* +-+
* |#| handle
* |#|
* +-+
* | |
* | |
* +-+
* +--+
* | |
* | |
* +--+
* |##| handle
* |##|
* +--+
* | |
* | |
* +--+
*/
fn ElemEvents? Ctx.slider_ver(&ctx,
String label,
macro Ctx.slider_ver(&ctx,
Rect size,
float* value,
float hpercent = 0.25,
Color bgcolor = 0x0000ffffu.to_rgba(),
Color handlecolor = 0x0ff000ffu.to_rgba(), ...)
=> ctx.slider_ver_id(@compute_id($vasplat), size, value, hpercent, bgcolor, handlecolor);
fn ElemEvents? Ctx.slider_ver_id(&ctx,
Id id,
Rect size,
float* value,
float hpercent = 0.25,
Color bgcolor = 0x0000ffffu.to_rgba(),
Color handlecolor = 0x0ff000ffu.to_rgba())
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;

View file

@ -110,9 +110,12 @@ fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t
ctx.sprite_atlas.insert(name, type, pixels, (ushort)desc.width, (ushort)desc.height, (ushort)desc.width)!;
}
fn void? Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
macro Ctx.draw_sprite(&ctx, String name, Point off = {0,0}, ...)
=> ctx.draw_sprite_id(@compute_id($vasplat), name, off);
fn void? Ctx.draw_sprite_id(&ctx, Id id, String name, Point off)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;

View file

@ -7,9 +7,11 @@ struct ElemText {
usz cursor; // cursor offset
}
fn void? Ctx.text_unbounded(&ctx, String label, String text)
macro Ctx.text_unbounded(&ctx, String text, ...)
=> ctx.text_unbounded_id(@compute_id($vasplat), text);
fn void? Ctx.text_unbounded_id(&ctx, Id id, String text)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;
@ -30,9 +32,11 @@ fn void? Ctx.text_unbounded(&ctx, String label, String text)
ctx.push_string(elem.bounds, text, parent.div.z_index)!;
}
fn ElemEvents? Ctx.text_box(&ctx, String label, Rect size, char[] text, usz* text_len)
macro Ctx.text_box(&ctx, Rect size, char[] text, usz* text_len, ...)
=> ctx.text_box_id(@compute_id($vasplat), size, text, text_len);
fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Rect size, char[] text, usz* text_len)
{
Id id = ctx.gen_id(label)!;
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id)!;