initial refactor

font_atlas
Alessandro Mauri 2 months ago
parent 39e78ea078
commit f48151b38e
  1. 82
      src/main.c3
  2. 48
      src/ugui_button.c3
  3. 1
      src/ugui_data.c3
  4. 149
      src/ugui_impl.c3
  5. 61
      src/ugui_input.c3

@ -15,18 +15,15 @@ fn int main(String[] args)
rl::init_window(width, height, "Ugui Test");
ctx.input_window_size(width, height)!!;
double[10][4] median_times;
isz frame;
double median_input, median_layout, median_draw, median_tot;
// Main loop
while (!rl::window_should_close()) {
const int PARTIAL_INPUT = 0;
const int PARTIAL_LAYOUT = 1;
const int PARTIAL_DRAW = 2;
//timer_start();
/*** Start Input Handling ***/
/* Start Input Handling */
if (rl::is_window_resized()) {
width = (short)rl::get_screen_width();
height = (short)rl::get_screen_height();
@ -48,47 +45,40 @@ fn int main(String[] args)
buttons.btn_right = rl::is_mouse_button_down(rl::MOUSE_BUTTON_RIGHT);
buttons.btn_middle = rl::is_mouse_button_down(rl::MOUSE_BUTTON_MIDDLE);
ctx.input_mouse_button(buttons);
/* End Input Handling */
//timer_partial(PARTIAL_INPUT);
/*** End Input Handling ***/
/*** Start UI Handling ***/
/* Start UI Handling */
ctx.frame_begin()!!;
// main div, fill the whole window
ctx.div_begin("main", ugui::DIV_FILL)!!;
{|
ctx.layout_set_column()!!;
if (ctx.button("button0", ugui::Rect{.y = 100, .x = 100, .w = 30, .h = 30})!!.mouse_hold) {
io::printn("HOLDING button0");
if (ctx.button("button0", ugui::Rect{100,100,30,30})!!.mouse_press) {
io::printn("press button0");
}
ctx.layout_next_column()!!;
ctx.button("button1", ugui::Rect{.w = 30, .h = 30})!!;
if (ctx.button("button1", ugui::Rect{0,0,30,30})!!.mouse_press) {
io::printn("press button1");
}
ctx.layout_next_column()!!;
ctx.button("button2", ugui::Rect{.w = 30, .h = 30})!!;
if (ctx.button("button2", ugui::Rect{0,0,30,30})!!.mouse_release) {
io::printn("release button2");
}
|};
ctx.div_end()!!;
ctx.frame_end()!!;
//timer_partial(PARTIAL_LAYOUT);
/*** End UI Handling ***/
/* End UI Handling */
/*** Start UI Drawing ***/
/* Start UI Drawing */
rl::begin_drawing();
// ClearBackground(BLACK);
io::printn("----- Draw Begin -----");
rl::Color c;
for (Cmd* cmd; (cmd = ctx.cmd_queue.dequeue() ?? null) != null;) {
switch (cmd.type) {
case ugui::CmdType.CMD_RECT:
io::printfn(
"draw rect x=%d y=%d w=%d h=%d",
cmd.rect.rect.x,
cmd.rect.rect.y,
cmd.rect.rect.w,
cmd.rect.rect.h
);
c = rl::Color{
.r = cmd.rect.color.r,
.g = cmd.rect.color.g,
@ -106,52 +96,10 @@ fn int main(String[] args)
io::printfn("Unknown cmd type: %d", cmd.type);
}
}
io::printf("----- Draw End -----\n\n");
rl::end_drawing();
//timer_partial(PARTIAL_DRAW);
//timer_stop();
/*** End UI Drawing ***/
/*
median_times[frame][PARTIAL_INPUT] =
1e3 * timer_get_sec(PARTIAL_INPUT);
median_times[frame][PARTIAL_LAYOUT] =
1e3 * timer_get_sec(PARTIAL_LAYOUT);
median_times[frame][PARTIAL_DRAW] =
1e3 * timer_get_sec(PARTIAL_DRAW);
median_times[frame][3] = 1e3 * timer_get_sec(-1);
*/
frame += 1;
frame %= 10;
/*
if (frame == 0) {
median_input = 0;
median_layout = 0;
median_draw = 0;
median_tot = 0;
for (size_t i = 0; i < 10; i++) {
median_input += median_times[i][PARTIAL_INPUT];
median_layout += median_times[i][PARTIAL_LAYOUT];
median_draw += median_times[i][PARTIAL_DRAW];
median_tot += median_times[i][3];
}
median_input /= 10;
median_layout /= 10;
median_draw /= 10;
median_tot /= 10;
}
printf("input time: %lfms\n", median_input);
printf("layout time: %lfms\n", median_layout);
printf("draw time: %lfms\n", median_draw);
printf("total time: %lfms\n", median_tot);
// Throttle Frames
// TODO: add an fps limit, time frame generation and log it
const float TARGET_FPS = 100;
float wait_time = MAX((1.0 / TARGET_FPS) - timer_get_sec(-1), 0);
WaitTime(wait_time);
*/
// TODO: throttle FPS
}
rl::close_window();
@ -160,6 +108,7 @@ fn int main(String[] args)
return 0;
}
/*
fn void! test_vtree() @test
{
vtree::VTree(<String>) vt;
@ -194,3 +143,4 @@ fn void! test_cache() @test
r = cc.get_or_insert(&&"Ciao Mamma", 1)!;
assert(*r!! == "Ciao Mamma", "incorrect string");
}
*/

@ -0,0 +1,48 @@
module ugui;
// draw a button, return the events on that button
fn ElemEvents! Ctx.button(&ctx, String label, Rect size)
{
Id id = hash(label);
Elem *parent = ctx.get_parent()!;
Elem *c_elem = ctx.get_elem(id)!;
// add it to the tree
ctx.tree.add(id, ctx.active_div)!;
// 1. Fill the element fields
// this resets the flags
c_elem.type = ETYPE_BUTTON;
Color bg_color = uint_to_rgba(0x0000ffff);
// if the element is new or the parent was updated then redo layout
if (c_elem.flags.is_new || parent.flags.updated) {
// 2. Layout
c_elem.rect = ctx.position_element(parent, size, true);
// TODO: 3. Fill the button specific fields
}
// TODO: Check for interactions
// FIXME: this is not how normal buttons work, usually focus follows both
// mouse hover and mouse down
c_elem.events = ctx.get_elem_events(c_elem);
if (parent.flags.has_focus) {
if (c_elem.events.mouse_hover) {
c_elem.flags.has_focus = true;
bg_color = uint_to_rgba(0x00ff00ff);
}
}
// Draw the button
Cmd cmd = {
.type = CMD_RECT,
.rect = {
.rect = c_elem.rect,
.color = bg_color,
},
};
ctx.cmd_queue.enqueue(&cmd)!;
return c_elem.events;
}

@ -28,6 +28,7 @@ enum ElemType {
bitstruct ElemFlags : uint {
bool updated : 0;
bool has_focus : 1;
bool is_new : 2;
}
bitstruct ElemEvents : uint {

@ -11,6 +11,19 @@ fn Elem*! Ctx.get_parent(&ctx)
return ctx.cache.search(parent_id);
}
// 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;
}
fn void! Ctx.init(&ctx)
{
ctx.tree.init(MAX_ELEMENTS)!;
@ -39,56 +52,40 @@ fn void Ctx.free(&ctx)
fn void! Ctx.frame_begin(&ctx)
{
// 1. Create the root div element
// NOTE: in c3 everythong is zero initialized by default
Rect space = {
.w = ctx.width,
.h = ctx.height,
};
Elem root = {
.id = ROOT_ID,
.type = ETYPE_DIV,
.rect = space,
.div = {
.layout = LAYOUT_ROW,
},
};
// 2. Get the root element from the cache and update it
Elem* c_elem = ctx.get_elem(ROOT_ID)!;
// 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
if (ctx.input.events.resize) {
root.flags.updated = true;
}
c_elem.flags.updated = ctx.input.events.resize;
// 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
if (ctx.has_focus) {
root.flags.has_focus = true;
}
// FIXME: check errors
// 2. Get the root element from the cache and update it
bool is_new;
Elem empty_elem;
Elem* c_elem = ctx.cache.get_or_insert(&empty_elem, root.id, &is_new)!;
// flags always need to be set to the new flags
c_elem.flags = root.flags;
if (is_new || root.flags.updated) {
*c_elem = root;
c_elem.flags.has_focus = ctx.has_focus;
if (c_elem.flags.is_new || c_elem.flags.updated) {
Elem def_root = {
.id = ROOT_ID,
.type = ETYPE_DIV,
.rect = {
.w = ctx.width,
.h = ctx.height,
},
.div = {
.layout = LAYOUT_ROW,
},
.flags = c_elem.flags,
};
*c_elem = def_root;
}
// 3. Push the root element into the element tree
ctx.active_div = ctx.tree.add(root.id, 0)!;
// print_tree(ctx);
ctx.active_div = ctx.tree.add(ROOT_ID, 0)!;
// The root element does not push anything to the stack
// TODO: add a background color taken from a theme or config
io::printn("##### Frame Begin #####");
}
fn void! Ctx.frame_end(&ctx)
@ -113,36 +110,22 @@ $if 1:
};
ctx.cmd_queue.enqueue(&cmd)!;
$endif
io::printn("##### Frame End #####");
}
fn void! Ctx.div_begin(&ctx, String label, Rect size)
{
Id id = hash(label);
bool is_new;
Elem empty_elem;
Elem* c_elem = ctx.cache.get_or_insert(&empty_elem, id, &is_new)!;
// FIXME: why save the id in the tree and not something more direct like
// the element pointer or the index into the cache vector?
isz div_node = ctx.tree.add(id, ctx.active_div)!;
Elem *parent = ctx.get_parent()!;
ctx.tree.print();
// Use the current div
Elem* c_elem = ctx.get_elem(id)!;
isz div_node = ctx.tree.add(id, ctx.active_div)!;
ctx.active_div = div_node;
// 1. Fill the element fields
// this resets the flags
c_elem.type = ETYPE_DIV;
c_elem.flags = (ElemFlags)0;
// do layout and update flags only if the element was updated
if (is_new || parent.flags.updated) {
if (c_elem.flags.is_new || parent.flags.updated) {
// 2. layout the element
c_elem.rect = ctx.position_element(parent, size);
@ -183,62 +166,10 @@ fn void! Ctx.div_end(&ctx)
ctx.active_div = ctx.tree.parentof(ctx.active_div)!;
}
// @ensure elem != null
/**
* @ensure elem != null
**/
fn bool Ctx.is_hovered(&ctx, Elem *elem)
{
return point_in_rect(ctx.input.mouse.pos, elem.rect);
}
fn ElemEvents! Ctx.button(&ctx, String label, Rect size)
{
Id id = hash(label);
// TODO: do layouting if the element is new or the parent has updated
bool is_new;
Elem empty_elem;
Elem *c_elem = ctx.cache.get_or_insert(&empty_elem, id, &is_new)!;
// add it to the tree
ctx.tree.add(id, ctx.active_div)!;
ctx.tree.print();
Elem *parent = ctx.get_parent()!;
// 1. Fill the element fields
// this resets the flags
c_elem.type = ETYPE_BUTTON;
c_elem.flags = (ElemFlags)0;
Color bg_color = uint_to_rgba(0x0000ffff);
// if the element is new or the parent was updated then redo layout
if (is_new || parent.flags.updated) {
// 2. Layout
c_elem.rect = ctx.position_element(parent, size, true);
// TODO: 3. Fill the button specific fields
}
// TODO: Check for interactions
if (parent.flags.has_focus) {
if (ctx.is_hovered(c_elem)) {
c_elem.flags.has_focus = true;
c_elem.events.mouse_hover = true;
bg_color = uint_to_rgba(0x00ff00ff);
c_elem.events.mouse_hold = ctx.input.mouse.down.btn_left;
} else {
c_elem.events.mouse_hover = false;
}
}
// Draw the button
Cmd cmd = {
.type = CMD_RECT,
.rect = {
.rect = c_elem.rect,
.color = bg_color,
},
};
ctx.cmd_queue.enqueue(&cmd)!;
return c_elem.events;
}

@ -40,21 +40,64 @@ bitstruct MouseButtons : uint {
bool btn_5 : 4;
}
macro Ctx.mouse_pressed(&ctx)
{
return ctx.input.mouse.updated & ctx.input.mouse.down;
}
macro Ctx.mouse_released(&ctx)
{
return ctx.input.mouse.updated & ~ctx.input.mouse.down;
}
macro Ctx.mouse_down(&ctx)
{
return ctx.input.mouse.down;
}
const MouseButtons BTN_NONE = (MouseButtons)0u;
const MouseButtons BTN_ANY = (MouseButtons)(uint.max);
const MouseButtons BTN_LEFT = {.btn_left = true};
const MouseButtons BTN_MIDDLE = {.btn_middle = true};
const MouseButtons BTN_RIGHT = {.btn_right = true};
const MouseButtons BTN_4 = {.btn_4 = true};
const MouseButtons BTN_5 = {.btn_5 = true};
// FIXME: hthis compairson could be done with a cast using MouseButtons.inner
// property but I could not figure out how
macro Ctx.is_mouse_pressed(&ctx, MouseButtons btn)
{
return (ctx.mouse_pressed() & btn) != BTN_NONE;
}
macro Ctx.is_mouse_released(&ctx, MouseButtons btn)
{
return (ctx.mouse_released() & btn) != BTN_NONE;
}
macro Ctx.is_mouse_down(&ctx, MouseButtons btn)
{
return (ctx.mouse_down() & btn) != BTN_NONE;
}
macro ElemEvents Ctx.get_elem_events(&ctx, Elem *elem)
{
// TODO: add the other events
ElemEvents ev = {
.mouse_hover = ctx.is_hovered(elem),
.mouse_press = ctx.is_mouse_pressed(BTN_ANY),
.mouse_release = ctx.is_mouse_released(BTN_ANY),
.mouse_hold = ctx.is_mouse_down(BTN_ANY),
};
return ev;
}
// Mouse Button moved
fn void Ctx.input_mouse_button(&ctx, MouseButtons buttons)
{
ctx.input.mouse.updated = ctx.input.mouse.down ^ buttons;
ctx.input.mouse.down = buttons;
ctx.input.events.mouse_btn = true;
io::printfn(
"Mouse Down: %s%s%s%s%s",
buttons.btn_left ? "BTN_LEFT " : "",
buttons.btn_right ? "BTN_RIGHT " : "",
buttons.btn_middle ? "BTN_MIDDLE " : "",
buttons.btn_4 ? "BTN_4 " : "",
buttons.btn_5 ? "BTN_5 " : ""
);
}
// Mouse was moved, report absolute position

Loading…
Cancel
Save