ugui.c3l/src/ugui_impl.c3

160 lines
3.7 KiB
Plaintext
Raw Normal View History

2024-10-29 22:45:47 +01:00
module ugui;
import std::io;
// return a pointer to the parent of the current active div
fn Elem*! Ctx.get_parent(&ctx)
{
// FIXME: if the tree held pointers to the elements then no more
// redundant cache search
Id parent_id = ctx.tree.get(ctx.active_div)!;
return ctx.cache.search(parent_id);
}
2024-10-31 00:04:49 +01:00
// get or push an element from the cache, return a pointer to it
// resets all flags except is_new which is set accordingly
macro Ctx.get_elem(&ctx, Id id)
{
Elem empty_elem;
bool is_new;
Elem* c_elem;
c_elem = ctx.cache.get_or_insert(&empty_elem, id, &is_new)!;
c_elem.flags = (ElemFlags)0;
c_elem.flags.is_new = is_new;
return c_elem;
}
2024-10-31 13:04:30 +01:00
// this searches an element in the cache by label, it does not create a new element
// if it does't find one
macro Ctx.get_elem_by_label(&ctx, String label)
{
2024-12-01 00:26:56 +01:00
Id id = label.hash();
return ctx.cache.search(id);
2024-10-31 13:04:30 +01:00
}
2024-11-14 23:42:20 +01:00
macro Ctx.get_elem_by_tree_idx(&ctx, isz idx) @private
{
2024-12-01 00:26:56 +01:00
Id id = ctx.tree.get(ctx.active_div)!;
2024-11-14 23:42:20 +01:00
return ctx.cache.search(id);
}
2024-10-29 22:45:47 +01:00
fn void! Ctx.init(&ctx)
{
ctx.tree.init(MAX_ELEMENTS)!;
defer catch { (void)ctx.tree.free(); }
ctx.cache.init()!;
defer catch { (void)ctx.cache.free(); }
ctx.cmd_queue.init(MAX_ELEMENTS)!;
defer catch { (void)ctx.cmd_queue.free(); }
ctx.layout = Layout.ROW;
ctx.active_div = 0;
// TODO: add style config
2024-12-12 15:46:24 +01:00
ctx.style.margin = Rect{2, 2, 2, 2};
ctx.style.border = Rect{2, 2, 2, 2};
ctx.style.padding = Rect{1, 1, 1, 1};
2024-12-13 14:42:15 +01:00
ctx.style.radius = 5;
2024-12-12 15:46:24 +01:00
ctx.style.bgcolor = uint_to_rgba(0x282828ff);
ctx.style.fgcolor = uint_to_rgba(0xfbf1c7ff);
ctx.style.brcolor = uint_to_rgba(0xd79921ff);
2024-10-29 22:45:47 +01:00
}
fn void Ctx.free(&ctx)
{
(void)ctx.tree.free();
(void)ctx.cache.free();
(void)ctx.cmd_queue.free();
2024-12-15 21:39:26 +01:00
(void)ctx.font.free();
2024-10-29 22:45:47 +01:00
}
fn void! Ctx.frame_begin(&ctx)
{
2024-10-31 00:04:49 +01:00
// 2. Get the root element from the cache and update it
Elem* c_elem = ctx.get_elem(ROOT_ID)!;
2024-10-29 22:45:47 +01:00
// The root should have the updated flag only if the size of the window
// was changed between frames, this propagates an element size recalculation
// down the element tree
2024-12-01 00:26:56 +01:00
c_elem.flags.updated = ctx.input.events.resize | ctx.input.events.force_update;
ctx.input.events.force_update = false;
2024-10-29 22:45:47 +01:00
// 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
2024-10-31 00:04:49 +01:00
c_elem.flags.has_focus = ctx.has_focus;
if (c_elem.flags.is_new || c_elem.flags.updated) {
2024-12-01 00:26:56 +01:00
Elem def_root = {
.id = ROOT_ID,
.type = ETYPE_DIV,
.bounds = {
2024-11-17 21:36:46 +01:00
.w = ctx.width,
.h = ctx.height,
2024-12-01 00:26:56 +01:00
},
.div = {
.layout = LAYOUT_ROW,
.children_bounds = {
.w = ctx.width,
.h = ctx.height,
}
},
.flags = c_elem.flags,
};
*c_elem = def_root;
2024-10-29 22:45:47 +01:00
}
// 3. Push the root element into the element tree
2024-10-31 00:04:49 +01:00
ctx.active_div = ctx.tree.add(ROOT_ID, 0)!;
2024-10-29 22:45:47 +01:00
// The root element does not push anything to the stack
// TODO: add a background color taken from a theme or config
}
2024-12-01 00:26:56 +01:00
fn void! Ctx.force_update(&ctx)
{
ctx.input.events.force_update = true;
}
2024-10-29 22:45:47 +01:00
fn void! Ctx.frame_end(&ctx)
{
// 1. clear the tree
ctx.tree.prune(0)!;
// 2. clear input fields
2024-12-01 00:26:56 +01:00
bool f = ctx.input.events.force_update;
2024-10-29 22:45:47 +01:00
ctx.input.events = (InputEvents)0;
2024-12-01 00:26:56 +01:00
ctx.input.events.force_update = f;
2024-10-29 22:45:47 +01:00
2024-12-15 21:39:26 +01:00
// send atlas updates
if (ctx.font.should_update) {
ctx.push_update_atlas(&ctx.font.atlas)!;
ctx.font.should_update = false;
}
2024-10-29 22:45:47 +01:00
$if 1:
2024-12-15 21:39:26 +01:00
// draw mouse position
2024-10-29 22:45:47 +01:00
Cmd cmd = {
.type = CMD_RECT,
.rect.rect = {
.x = ctx.input.mouse.pos.x - 2,
.y = ctx.input.mouse.pos.y - 2,
.w = 4,
.h = 4,
2024-10-29 22:45:47 +01:00
},
.rect.color = uint_to_rgba(0xff00ffff)
};
ctx.cmd_queue.enqueue(&cmd)!;
$endif
}
2024-12-01 00:26:56 +01:00
<*
2024-10-31 00:04:49 +01:00
* @ensure elem != null
2024-12-01 00:26:56 +01:00
*>
2024-10-29 22:45:47 +01:00
fn bool Ctx.is_hovered(&ctx, Elem *elem)
{
2024-11-02 09:44:53 +01:00
return point_in_rect(ctx.input.mouse.pos, elem.bounds);
2024-10-29 22:45:47 +01:00
}