semi-working vertical slider

c3
Alessandro Mauri 5 days ago
parent 763e9ba8d6
commit 73bc933eb5
  1. 74
      src/main.c3
  2. 2
      src/ugui_button.c3
  3. 32
      src/ugui_data.c3
  4. 76
      src/ugui_div.c3
  5. 32
      src/ugui_impl.c3
  6. 57
      src/ugui_layout.c3
  7. 4
      src/ugui_slider.c3

@ -6,14 +6,15 @@ import rl;
fn int main(String[] args) fn int main(String[] args)
{ {
ugui::Ctx ctx; ugui::Ctx ui;
ctx.init()!!; ui.init()!!;
short width = 800; short width = 800;
short height = 450; short height = 450;
rl::set_config_flags(rl::FLAG_WINDOW_RESIZABLE); rl::set_config_flags(rl::FLAG_WINDOW_RESIZABLE);
rl::init_window(width, height, "Ugui Test"); rl::init_window(width, height, "Ugui Test");
ctx.input_window_size(width, height)!!; ui.input_window_size(width, height)!!;
rl::set_target_fps(30);
isz frame; isz frame;
@ -27,61 +28,68 @@ fn int main(String[] args)
if (rl::is_window_resized()) { if (rl::is_window_resized()) {
width = (short)rl::get_screen_width(); width = (short)rl::get_screen_width();
height = (short)rl::get_screen_height(); height = (short)rl::get_screen_height();
ctx.input_window_size(width, height)!!; ui.input_window_size(width, height)!!;
} }
ctx.input_changefocus(rl::is_window_focused()); ui.input_changefocus(rl::is_window_focused());
// FIXME: In raylib it doesn't seem to be a quick way to check if rl::Vector2 mpos = rl::get_mouse_position();
// a mouse input event was received, so for now just use ui.input_mouse_abs((short)mpos.x, (short)mpos.y);
// the delta information
rl::Vector2 mousedelta = rl::get_mouse_delta();
if (mousedelta.x || mousedelta.y) {
ctx.input_mouse_delta((short)mousedelta.x, (short)mousedelta.y);
}
ugui::MouseButtons buttons; ugui::MouseButtons buttons;
buttons.btn_left = rl::is_mouse_button_down(rl::MOUSE_BUTTON_LEFT); buttons.btn_left = rl::is_mouse_button_down(rl::MOUSE_BUTTON_LEFT);
buttons.btn_right = rl::is_mouse_button_down(rl::MOUSE_BUTTON_RIGHT); buttons.btn_right = rl::is_mouse_button_down(rl::MOUSE_BUTTON_RIGHT);
buttons.btn_middle = rl::is_mouse_button_down(rl::MOUSE_BUTTON_MIDDLE); buttons.btn_middle = rl::is_mouse_button_down(rl::MOUSE_BUTTON_MIDDLE);
ctx.input_mouse_button(buttons); ui.input_mouse_button(buttons);
/* End Input Handling */ /* End Input Handling */
/* Start UI Handling */ /* Start UI Handling */
ctx.frame_begin()!!; ui.frame_begin()!!;
/*
// main div, fill the whole window // main div, fill the whole window
ctx.div_begin("main", ugui::Rect{.w=ctx.width/2})!!; ui.div_begin("main", ugui::Rect{.w=ui.width/2})!!;
{| {|
ctx.layout_set_row()!!; ui.layout_set_row()!!;
if (ctx.button("button0", ugui::Rect{0,0,30,30})!!.mouse_press) { if (ui.button("button0", ugui::Rect{0,0,30,30})!!.mouse_press) {
io::printn("press button0"); io::printn("press button0");
} }
//ctx.layout_next_column()!!; //ui.layout_next_column()!!;
if (ctx.button("button1", ugui::Rect{0,0,30,30})!!.mouse_press) { if (ui.button("button1", ugui::Rect{0,0,30,30})!!.mouse_press) {
io::printn("press button1"); io::printn("press button1");
} }
//ctx.layout_next_column()!!; //ui.layout_next_column()!!;
if (ctx.button("button2", ugui::Rect{0,0,30,30})!!.mouse_release) { if (ui.button("button2", ugui::Rect{0,0,30,30})!!.mouse_release) {
io::printn("release button2"); io::printn("release button2");
} }
if (ctx.slider_ver("slider", ugui::Rect{0,0,30,100})!!.update) { if (ui.slider_ver("slider", ugui::Rect{0,0,30,100})!!.update) {
ugui::Elem* e = ctx.get_elem_by_label("slider")!!; ugui::Elem* e = ui.get_elem_by_label("slider")!!;
io::printfn("slider: %f", e.slider.value); io::printfn("slider: %f", e.slider.value);
} }
|}; |};
ctx.div_end()!!; ui.div_end()!!;
*/
ctx.div_begin("second", ugui::DIV_FILL)!!; ui.div_begin("second", ugui::DIV_FILL)!!;
ugui::Elem* de = ui.get_elem_by_label("second")!!;
de.div.scroll.can_y = true;
{| {|
if (ctx.slider_ver("slider_other", ugui::Rect{0,0,30,100})!!.update) { ui.layout_set_column()!!;
ugui::Elem* e = ctx.get_elem_by_label("slider_other")!!; if (ui.slider_ver("slider_other", ugui::Rect{0,0,30,100})!!.update) {
ugui::Elem* e = ui.get_elem_by_label("slider_other")!!;
io::printfn("other slider: %f", e.slider.value); io::printfn("other slider: %f", e.slider.value);
} }
ui.button("button10", ugui::Rect{0,0,50,50})!!;
ui.button("button11", ugui::Rect{0,0,50,50})!!;
ui.button("button12", ugui::Rect{0,0,50,50})!!;
ui.button("button13", ugui::Rect{0,0,50,50})!!;
ui.button("button14", ugui::Rect{0,0,50,50})!!;
ui.button("button15", ugui::Rect{0,0,50,50})!!;
ui.button("button16", ugui::Rect{0,0,50,50})!!;
ui.button("button17", ugui::Rect{0,0,50,50})!!;
|}; |};
ctx.div_end()!!; ui.div_end()!!;
ctx.frame_end()!!; ui.frame_end()!!;
/* End UI Handling */ /* End UI Handling */
/* Start UI Drawing */ /* Start UI Drawing */
@ -89,7 +97,7 @@ fn int main(String[] args)
// ClearBackground(BLACK); // ClearBackground(BLACK);
rl::Color c; rl::Color c;
for (Cmd* cmd; (cmd = ctx.cmd_queue.dequeue() ?? null) != null;) { for (Cmd* cmd; (cmd = ui.cmd_queue.dequeue() ?? null) != null;) {
switch (cmd.type) { switch (cmd.type) {
case ugui::CmdType.CMD_RECT: case ugui::CmdType.CMD_RECT:
c = rl::Color{ c = rl::Color{
@ -111,13 +119,11 @@ fn int main(String[] args)
} }
rl::end_drawing(); rl::end_drawing();
// TODO: throttle FPS
} }
rl::close_window(); rl::close_window();
ctx.free(); ui.free();
return 0; return 0;
} }

@ -1,5 +1,7 @@
module ugui; module ugui;
import std::io;
// draw a button, return the events on that button // draw a button, return the events on that button
fn ElemEvents! Ctx.button(&ctx, String label, Rect size) fn ElemEvents! Ctx.button(&ctx, String label, Rect size)
{ {

@ -1,9 +1,13 @@
module ugui; module ugui;
import std::io;
import std::core::string;
import vtree; import vtree;
import cache; import cache;
import fifo; import fifo;
struct Rect { struct Rect {
short x, y, w, h; short x, y, w, h;
} }
@ -52,10 +56,14 @@ enum DivLayout {
// div element // div element
struct Div { struct Div {
DivLayout layout; DivLayout layout;
bool can_scroll_x; struct scroll {
bool can_scroll_y; bool can_x;
isz vertical_scroll_bar; bool can_y;
isz horizontal_scroll_bar; bool on_x;
bool on_y;
float value_x;
float value_y;
}
Rect children_bounds; Rect children_bounds;
Point origin_r, origin_c; Point origin_r, origin_c;
Color color_bg; Color color_bg;
@ -201,21 +209,21 @@ macro point_in_rect(Point p, Rect r)
// return true if rect a contains b // return true if rect a contains b
macro rect_contains(Rect a, Rect b) macro rect_contains(Rect a, Rect b)
{ {
return (a.x <= b.x && a.y <= b.y && a.x+a.w >= b.x+b.w && a.y+a.h >= b.y+b.h); return (a.x <= b.x && a.y <= b.y && a.x+a.w >= b.x+b.w && a.y+a.h >= b.y+b.h);
} }
macro rect_intersection(Rect a, Rect b) macro rect_intersection(Rect a, Rect b)
{ {
return Rect{ return Rect{
.x = (short)max(a.x, b.x), .x = (short)max(a.x, b.x),
.y = (short)max(a.y, b.y), .y = (short)max(a.y, b.y),
.w = (short)min(a.x+a.w, b.x+b.w) - (short)max(a.x, b.x), .w = (short)min(a.x+a.w, b.x+b.w) - (short)max(a.x, b.x),
.h = (short)min(a.y+a.h, b.y+b.h) - (short)max(a.y, b.y), .h = (short)min(a.y+a.h, b.y+b.h) - (short)max(a.y, b.y),
}; };
} }
// rect intersection not null // rect intersection not null
macro rect_collision(Rect a, Rect b) macro rect_collision(Rect a, Rect b)
{ {
return !(a.x > b.x+b.w || a.x+a.w < b.x || a.y > b.y+b.h || a.y+a.h < b.y); return !(a.x > b.x+b.w || a.x+a.w < b.x || a.y > b.y+b.h || a.y+a.h < b.y);
} }

@ -1,5 +1,6 @@
module ugui; module ugui;
import std::io;
fn void! Ctx.div_begin(&ctx, String label, Rect size) fn void! Ctx.div_begin(&ctx, String label, Rect size)
{ {
@ -29,18 +30,18 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size)
.x = c_elem.bounds.x, .x = c_elem.bounds.x,
.y = c_elem.bounds.y, .y = c_elem.bounds.y,
}; };
c_elem.div.color_bg = uint_to_rgba(0xff0000ff);
c_elem.div.origin_r = c_elem.div.origin_c; c_elem.div.origin_r = c_elem.div.origin_c;
c_elem.div.can_scroll_x = false; c_elem.div.color_bg = uint_to_rgba(0xff0000ff);
c_elem.div.can_scroll_y = false; c_elem.div.scroll.can_x = false;
c_elem.div.vertical_scroll_bar = -1; c_elem.div.scroll.can_y = false;
c_elem.div.horizontal_scroll_bar = -1; c_elem.div.scroll.value_x = 0;
c_elem.div.scroll.value_y = 0;
} }
// Add the background to the draw stack // Add the background to the draw stack
Cmd cmd = { Cmd cmd = {
.type = CMD_RECT, .type = CMD_RECT,
.rect = { .rect = {
.rect = c_elem.bounds, .rect = c_elem.bounds,
.color = c_elem.div.color_bg, .color = c_elem.div.color_bg,
}, },
@ -50,7 +51,66 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size)
// TODO: check active // TODO: check active
// TODO: check resizeable // TODO: check resizeable
// TODO: check scrollbars // check and draw scroll bars
if (!rect_contains(c_elem.bounds, c_elem.div.children_bounds)) {
Point cbc = {
.x = c_elem.div.children_bounds.x + c_elem.div.children_bounds.w,
.y = c_elem.div.children_bounds.y + c_elem.div.children_bounds.h,
};
Point bc = {
.x = c_elem.bounds.x + c_elem.bounds.w,
.y = c_elem.bounds.y + c_elem.bounds.h,
};
// vertical overflow, check and draw scroll bar
if (cbc.y > bc.y && c_elem.div.scroll.can_y) {
// set the scrollbar flag, is used in layout
c_elem.div.scroll.on_y = true;
Rect vslider = {
.x = c_elem.bounds.x + c_elem.bounds.w - 10,
.y = c_elem.bounds.y,
.w = 10,
.h = c_elem.bounds.h,
};
float vh = max((float)bc.y / cbc.y, (float)0.15);
Rect vhandle = {
.x = c_elem.bounds.x + c_elem.bounds.w - 10,
.y = (short)(c_elem.bounds.y + (int)(c_elem.bounds.h*(1-vh) * c_elem.div.scroll.value_y)),
.w = 10,
.h = (short)(c_elem.bounds.h * vh),
};
c_elem.events = ctx.get_elem_events(c_elem);
if (parent.flags.has_focus && c_elem.events.mouse_hover && c_elem.events.mouse_hold && point_in_rect(ctx.input.mouse.pos, vhandle)) {
short y = (short)clamp(ctx.input.mouse.pos.y - vhandle.h/2, vslider.y, vslider.y + vslider.h - vhandle.h);
vhandle.y = y;
float v = (float)(vhandle.y-vslider.y) / (float)(vslider.h-vhandle.h);
c_elem.div.scroll.value_y = v;
c_elem.flags.updated = true;
c_elem.div.origin_c = Point{
.x = c_elem.bounds.x,
.y = c_elem.bounds.y,
};
c_elem.div.origin_r = c_elem.div.origin_c;
}
Cmd scrl = {
.type = CMD_RECT,
.rect.rect = vslider,
.rect.color = uint_to_rgba(0x999999ff),
};
ctx.cmd_queue.enqueue(&scrl)!;
scrl.rect.rect = vhandle;
scrl.rect.color = uint_to_rgba(0x9999ffff);
ctx.cmd_queue.enqueue(&scrl)!;
} else {
c_elem.div.scroll.on_y = false;
}
}
// if the bounds are outside of the view then allocate space for scrollbars // if the bounds are outside of the view then allocate space for scrollbars
DivLayout old_layout = c_elem.div.layout; DivLayout old_layout = c_elem.div.layout;
c_elem.div.layout = LAYOUT_FLOATING; c_elem.div.layout = LAYOUT_FLOATING;

@ -79,20 +79,24 @@ fn void! Ctx.frame_begin(&ctx)
c_elem.flags.has_focus = ctx.has_focus; c_elem.flags.has_focus = ctx.has_focus;
if (c_elem.flags.is_new || c_elem.flags.updated) { if (c_elem.flags.is_new || c_elem.flags.updated) {
Elem def_root = { Elem def_root = {
.id = ROOT_ID, .id = ROOT_ID,
.type = ETYPE_DIV, .type = ETYPE_DIV,
.bounds = { .bounds = {
.w = ctx.width, .w = ctx.width,
.h = ctx.height, .h = ctx.height,
}, },
.div = { .div = {
.layout = LAYOUT_ROW, .layout = LAYOUT_ROW,
}, .children_bounds = {
.flags = c_elem.flags, .w = ctx.width,
}; .h = ctx.height,
}
*c_elem = def_root; },
.flags = c_elem.flags,
};
*c_elem = def_root;
} }
// 3. Push the root element into the element tree // 3. Push the root element into the element tree

@ -78,17 +78,22 @@ fn void! Ctx.layout_next_column(&ctx)
// parent: parent div // parent: parent div
// rect: the requested size // rect: the requested size
// style: apply style // style: apply style
<*
@require ctx != null
@require parent.type == ETYPE_DIV
*>
fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
{ {
Rect placement; Rect placement;
Point origin; Point origin;
Div* div = &parent.div;
// 1. Select the right origin // 1. Select the right origin
switch (parent.div.layout) { switch (div.layout) {
case LAYOUT_ROW: case LAYOUT_ROW:
origin = parent.div.origin_r; origin = div.origin_r;
case LAYOUT_COLUMN: case LAYOUT_COLUMN:
origin = parent.div.origin_c; origin = div.origin_c;
case LAYOUT_FLOATING: // none case LAYOUT_FLOATING: // none
default: default:
// Error // Error
@ -100,8 +105,8 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
// 2. Calculate the placement // 2. Calculate the placement
placement.x = (short)max(origin.x + rect.x, 0); placement.x = (short)max(origin.x + rect.x, 0);
placement.y = (short)max(origin.y + rect.y, 0); placement.y = (short)max(origin.y + rect.y, 0);
placement.w = rect.w > 0 ? rect.w : parent.bounds.w - (placement.x - parent.bounds.x); placement.w = rect.w > 0 ? rect.w : (short)max(parent.bounds.w - (placement.x - parent.bounds.x), 0);
placement.h = rect.h > 0 ? rect.h : parent.bounds.h - (placement.y - parent.bounds.y); placement.h = rect.h > 0 ? rect.h : (short)max(parent.bounds.h - (placement.y - parent.bounds.y), 0);
pl_corner.x = placement.x + placement.w; pl_corner.x = placement.x + placement.w;
pl_corner.y = placement.y + placement.h; pl_corner.y = placement.y + placement.h;
@ -122,28 +127,23 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
} }
// 3. Update the origins of the parent // 3. Update the origins of the parent
parent.div.origin_r = Point{ div.origin_r = Point{
.x = pl_corner.x, .x = pl_corner.x,
.y = origin.y, .y = origin.y,
}; };
parent.div.origin_c = Point{ div.origin_c = Point{
.x = origin.x, .x = origin.x,
.y = pl_corner.y, .y = pl_corner.y,
}; };
// 4. Calculate the "scrolled" view // 4. Calculate the "scrolled" view
Point off; Point off;
if (parent.div.can_scroll_x && parent.div.horizontal_scroll_bar != -1) { Rect* cb = &div.children_bounds;
Elem*! sx = ctx.get_elem_by_tree_idx(parent.div.horizontal_scroll_bar); if (div.scroll.can_x && div.scroll.on_x) {
if (catch sx) { return Rect{}; } off.x = (short)(cb.w * div.scroll.value_x);
// TODO: assert that the element is a slider
off.x = (short)(parent.div.children_bounds.w * sx.slider.value);
} }
if (parent.div.can_scroll_y && parent.div.vertical_scroll_bar != -1) { if (div.scroll.can_y && div.scroll.on_y) {
Elem*! sy = ctx.get_elem_by_tree_idx(parent.div.vertical_scroll_bar); off.y = (short)((float)(cb.h - parent.bounds.h) * div.scroll.value_y);
if (catch sy) { return Rect{}; }
// TODO: assert that the element is a slider
off.y = (short)(parent.div.children_bounds.h * sy.slider.value);
} }
Rect view = { Rect view = {
.x = parent.bounds.x + off.x, .x = parent.bounds.x + off.x,
@ -152,7 +152,26 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
.h = parent.bounds.h, .h = parent.bounds.h,
}; };
// TODO: 5. check if the placement is inside the view // 5. check if the placement overflows the children ounds, if so update them
if (!point_in_rect(pl_corner, *cb)) {
if (pl_corner.y > cb.y+cb.h) {
cb.h = pl_corner.y - cb.y;
}
if (pl_corner.x > cb.x+cb.w) {
cb.w += pl_corner.x - (cb.x + cb.w);
}
}
return placement; // 6. check if the placement is inside the view
if (rect_collision(placement, view)) {
return Rect{
.x = placement.x - off.x,
.y = placement.y - off.y,
.w = placement.w,
.h = placement.h,
};
} else {
return Rect{};
}
} }

@ -1,5 +1,7 @@
module ugui; module ugui;
import std::io;
/* handle /* handle
* +----+-----+---------------------+ * +----+-----+---------------------+
* | |#####| | * | |#####| |
@ -79,7 +81,7 @@ fn ElemEvents! Ctx.slider_hor(&ctx, String label, Rect size)
*/ */
fn ElemEvents! Ctx.slider_ver(&ctx, String label, Rect size) fn ElemEvents! Ctx.slider_ver(&ctx, String label, Rect size)
{ {
Id id = hash(label); Id id = hash(label);
Elem *parent = ctx.get_parent()!; Elem *parent = ctx.get_parent()!;
Elem *c_elem = ctx.get_elem(id)!; Elem *c_elem = ctx.get_elem(id)!;

Loading…
Cancel
Save