This commit is contained in:
Alessandro Mauri 2025-09-30 21:39:36 +02:00
commit 225f61079d
6 changed files with 133 additions and 62 deletions

View File

@ -28,9 +28,8 @@ struct Layout {
TextSize text; TextSize text;
ushort grow_children; ushort grow_children;
short occupied; short occupied;
struct origin { Point origin;
short x, y; Point scroll_offset;
}
// false: the element is laid out according to the parent // false: the element is laid out according to the parent
// true: the element is laid out separate from all other children and the relative position to // true: the element is laid out separate from all other children and the relative position to
// the parent is the .origin field // the parent is the .origin field
@ -108,7 +107,7 @@ macro Point Elem.content_space(&e)
.x = e.bounds.w - e.layout.content_offset.x - e.layout.content_offset.w, .x = e.bounds.w - e.layout.content_offset.x - e.layout.content_offset.w,
.y = e.bounds.h - e.layout.content_offset.y - e.layout.content_offset.h, .y = e.bounds.h - e.layout.content_offset.y - e.layout.content_offset.h,
}; };
} }
// Update the parent element's children size // Update the parent element's children size
fn void update_parent_size(Elem* child, Elem* parent) fn void update_parent_size(Elem* child, Elem* parent)
@ -135,32 +134,12 @@ fn void update_parent_size(Elem* child, Elem* parent)
fn void update_children_bounds(Elem* child, Elem* parent) fn void update_children_bounds(Elem* child, Elem* parent)
{ {
parent.children_bounds = containing_rect(child.bounds, parent.bounds); if (child.layout.absolute) return;
parent.children_bounds = containing_rect(child.bounds + parent.layout.scroll_offset, parent.bounds);
} }
macro Rect Elem.content_bounds(&elem) => elem.bounds.pad(elem.layout.content_offset); macro Rect Elem.content_bounds(&elem) => elem.bounds.pad(elem.layout.content_offset);
/*
macro Rect Elem.get_view(&elem)
{
Rect off;
if (elem.div.scroll_x.enabled && elem.div.scroll_x.on) {
off.x = (short)((float)(elem.div.pcb.w - elem.bounds.w) * elem.div.scroll_x.value);
off.w = -elem.div.scroll_size;
}
if (elem.div.scroll_y.enabled && elem.div.scroll_y.on) {
off.y = (short)((float)(elem.div.pcb.h - elem.bounds.h) * elem.div.scroll_y.value);
off.h = -elem.div.scroll_size;
}
return elem.bounds.add(off);
}
macro Point Elem.get_view_off(&elem)
{
return elem.get_view().sub(elem.bounds).position();
}
*/
// Assign the width and height of an element in the directions that it doesn't need to grow // Assign the width and height of an element in the directions that it doesn't need to grow
fn void resolve_dimensions(Elem* e, Elem* p) fn void resolve_dimensions(Elem* e, Elem* p)
{ {
@ -186,7 +165,9 @@ fn void resolve_grow_elements(Elem* e, Elem* p)
{ {
// WIDTH // WIDTH
if (e.layout.w.@is_grow()) { if (e.layout.w.@is_grow()) {
if (p.layout.dir == ROW) { // grow along the axis, divide the parent size if (e.layout.absolute) { // absolute children do not need to share space
e.bounds.w = p.content_space().x;
} else if (p.layout.dir == ROW) { // grow along the axis, divide the parent size
short slot = (short)((p.content_space().x - p.layout.occupied) / p.layout.grow_children); short slot = (short)((p.content_space().x - p.layout.occupied) / p.layout.grow_children);
e.bounds.w = slot; e.bounds.w = slot;
p.layout.grow_children--; p.layout.grow_children--;
@ -198,7 +179,9 @@ fn void resolve_grow_elements(Elem* e, Elem* p)
// HEIGHT // HEIGHT
if (e.layout.h.@is_grow()) { if (e.layout.h.@is_grow()) {
if (p.layout.dir == COLUMN) { // grow along the axis, divide the parent size if (e.layout.absolute) { // absolute children do not need to share space
e.bounds.h = p.content_space().y;
} else if (p.layout.dir == COLUMN) { // grow along the axis, divide the parent size
short slot = (short)((p.content_space().y - p.layout.occupied) / p.layout.grow_children); short slot = (short)((p.content_space().y - p.layout.occupied) / p.layout.grow_children);
e.bounds.h = slot; e.bounds.h = slot;
p.layout.grow_children--; p.layout.grow_children--;
@ -319,7 +302,7 @@ fn void Ctx.layout_element_tree(&ctx)
int current; int current;
for (int n; (current = ctx.tree.level_order_it(0, n)) >= 0; n++) { for (int n; (current = ctx.tree.level_order_it(0, n)) >= 0; n++) {
Elem* p = ctx.find_elem(ctx.tree.get(current)); Elem* p = ctx.find_elem(ctx.tree.get(current));
//if (ctx.tree.is_root(current)!!) p = &&{}; p.layout.origin = -p.layout.scroll_offset;
int ch; int ch;
// RESOLVE KNOWN DIMENSIONS // RESOLVE KNOWN DIMENSIONS

View File

@ -124,7 +124,7 @@ macro Rect Rect.min(Rect a, Rect b)
} }
// Offset a rect by a point // Offset a rect by a point
macro Rect Rect.off(Rect r, Point p) macro Rect Rect.off(Rect r, Point p) @operator_s(+)
{ {
return { return {
.x = r.x + p.x, .x = r.x + p.x,

View File

@ -15,7 +15,6 @@ struct ElemDiv {
bool on; bool on;
float value; float value;
} }
ushort scroll_size;
int z_index; int z_index;
} }
@ -78,12 +77,10 @@ fn void? Ctx.div_begin_id(&ctx,
Elem* parent = ctx.get_parent()!; Elem* parent = ctx.get_parent()!;
ctx.active_div = elem.tree_idx; ctx.active_div = elem.tree_idx;
Style* style = ctx.styles.get_style(@str_hash("default")); Style* style = ctx.styles.get_style(@str_hash("div"));
Style* slider_style = ctx.styles.get_style(@str_hash("slider"));
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.div.scroll_size = slider_style.size ? slider_style.size : (style.size ? style.size : DEFAULT_STYLE.size);
elem.div.z_index = parent.div.z_index + 1; elem.div.z_index = parent.div.z_index + 1;
// update layout with correct info // update layout with correct info
@ -115,8 +112,9 @@ fn void? Ctx.div_begin_id(&ctx,
fn Id? Ctx.div_end(&ctx) fn Id? Ctx.div_end(&ctx)
{ {
Elem* elem = ctx.get_active_div()!; Elem* elem = ctx.get_active_div()!;
Style* style = ctx.styles.get_style(@str_hash("div"));
Rect bounds = elem.bounds.pad(style.margin + style.border);
/* FIXME: this needs the absolute positioning to work
// set the scrollbar flag, is used in layout // set the scrollbar flag, is used in layout
Point cbc = elem.children_bounds.bottom_right(); Point cbc = elem.children_bounds.bottom_right();
Point bc = elem.bounds.bottom_right(); Point bc = elem.bounds.bottom_right();
@ -127,27 +125,36 @@ fn Id? Ctx.div_end(&ctx)
Id hsid_raw = @str_hash("div_scrollbar_horizontal"); Id hsid_raw = @str_hash("div_scrollbar_horizontal");
Id vsid_raw = @str_hash("div_scrollbar_vertical"); Id vsid_raw = @str_hash("div_scrollbar_vertical");
Id hsid_real = ctx.gen_id(@str_hash("div_scrollbar_horizontal"))!; Id hsid_real = ctx.gen_id(hsid_raw)!;
Id vsid_real = ctx.gen_id(@str_hash("div_scrollbar_vertical"))!; Id vsid_real = ctx.gen_id(vsid_raw)!;
short wdim = elem.div.scroll_y.on ? (ctx.focus_id == vsid_real || ctx.is_hovered(ctx.find_elem(vsid_real)) ? elem.div.scroll_size*2 : elem.div.scroll_size) : 0;
short hdim = elem.div.scroll_x.on ? (ctx.focus_id == hsid_real || ctx.is_hovered(ctx.find_elem(hsid_real)) ? elem.div.scroll_size*2 : elem.div.scroll_size) : 0;
if (elem.div.scroll_y.on) { if (elem.div.scroll_y.on) {
if (ctx.input.events.mouse_scroll && ctx.hover_id == elem.id) { if (ctx.input.events.mouse_scroll && ctx.hover_id == elem.id && !(ctx.get_mod() & KMOD_SHIFT)) {
elem.div.scroll_y.value += ctx.input.mouse.scroll.y * 0.07f; elem.div.scroll_y.value += ctx.input.mouse.scroll.y * 0.07f;
elem.div.scroll_y.value = math::clamp(elem.div.scroll_y.value, 0.0f, 1.0f); elem.div.scroll_y.value = math::clamp(elem.div.scroll_y.value, 0.0f, 1.0f);
} }
ctx.slider_ver_id(vsid_raw, @exact(wdim), @exact(elem.bounds.h - hdim), &elem.div.scroll_y.value, max((float)bc.y / cbc.y, (float)0.15))!; ctx.scrollbar(vsid_raw, &elem.div.scroll_y.value, max((float)bc.y / cbc.y, (float)0.15))!;
elem.layout.scroll_offset.y = (short)(elem.div.scroll_y.value*(float)(elem.children_bounds.h-elem.bounds.h));
} else {
elem.div.scroll_y.value = 0;
} }
if (elem.div.scroll_x.on) { if (elem.div.scroll_x.on) {
if (ctx.input.events.mouse_scroll && ctx.hover_id == elem.id) { if (ctx.input.events.mouse_scroll && ctx.hover_id == elem.id) {
if (ctx.get_mod() & KMOD_SHIFT) { // horizontal scroll with shift
elem.div.scroll_x.value += ctx.input.mouse.scroll.y * 0.07f;
elem.div.scroll_x.value = math::clamp(elem.div.scroll_x.value, 0.0f, 1.0f);
} else {
elem.div.scroll_x.value += ctx.input.mouse.scroll.x * 0.07f; elem.div.scroll_x.value += ctx.input.mouse.scroll.x * 0.07f;
elem.div.scroll_x.value = math::clamp(elem.div.scroll_x.value, 0.0f, 1.0f); elem.div.scroll_x.value = math::clamp(elem.div.scroll_x.value, 0.0f, 1.0f);
} }
ctx.slider_hor_id(hsid_raw, @exact(elem.bounds.w - wdim), @exact(hdim), &elem.div.scroll_x.value, max((float)bc.x / cbc.x, (float)0.15))!;
} }
*/ ctx.scrollbar(vsid_raw, &elem.div.scroll_x.value, max((float)bc.x / cbc.x, (float)0.15), false)!;
elem.layout.scroll_offset.x = (short)(elem.div.scroll_x.value*(float)(elem.children_bounds.w-elem.bounds.w));
} else {
elem.div.scroll_x.value = 0;
}
// the active_div returns to the parent of the current one // the active_div returns to the parent of the current one
ctx.active_div = ctx.tree.parentof(ctx.active_div); ctx.active_div = ctx.tree.parentof(ctx.active_div);

View File

@ -130,6 +130,57 @@ fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Size w, Size h, float* value, floa
return elem.events; return elem.events;
} }
fn void? Ctx.scrollbar(&ctx, Id id, float *value, float handle_percent, bool vertical = true)
{
id = ctx.gen_id(id)!;
Elem *parent = ctx.get_parent()!;
Elem *elem = ctx.get_elem(id, ETYPE_SLIDER)!;
Style* style = ctx.styles.get_style(@str_hash("scrollbar"));
Rect pb = parent.bounds.pad(parent.layout.content_offset);
elem.bounds.x = vertical ? pb.bottom_right().x - style.size: pb.x;
elem.bounds.y = vertical ? pb.y : pb.bottom_right().y - style.size;
if (vertical) {
elem.layout.w = @exact(style.size);
elem.layout.h = @grow();
elem.bounds.x -= style.margin.x + style.margin.w + style.border.x + style.border.w;
} else {
elem.layout.w = @grow();
elem.layout.h = @exact(style.size);
elem.bounds.y -= style.margin.y + style.margin.h + style.border.y + style.border.h;
}
elem.layout.content_offset = style.margin + style.border + style.padding;
elem.layout.absolute = true;
update_parent_size(elem, parent);
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);
elem.events = ctx.get_elem_events(elem);
short o = vertical ? content_bounds.y : content_bounds.x;
short m = vertical ? ctx.input.mouse.pos.y : ctx.input.mouse.pos.x;
short s = vertical ? content_bounds.h : content_bounds.w;
short h = (short)((float)s * handle_percent);
if (elem.events.has_focus && ctx.is_mouse_down(BTN_LEFT)) {
*value = calc_value(o, m, s, h);
elem.events.update = true;
}
short handle_pos = calc_slider(o, s-h, *value);
elem.slider.handle = {
.x = vertical ? content_bounds.x : handle_pos,
.y = vertical ? handle_pos : content_bounds.y,
.w = vertical ? content_bounds.w : h,
.h = vertical ? h : content_bounds.h,
};
Rect bg_bounds = elem.bounds.pad(style.margin);
ctx.push_rect(bg_bounds, parent.div.z_index, style)!;
ctx.push_rect(elem.slider.handle, parent.div.z_index, &&(Style){.bg = style.primary, .radius = style.radius})!;
}
macro short calc_slider(short off, short dim, float value) => (short)off + (short)(dim * value); macro short calc_slider(short off, short dim, float value) => (short)off + (short)(dim * value);
macro float calc_value(short off, short mouse, short dim, short slider) macro float calc_value(short off, short mouse, short dim, short slider)
=> math::clamp((float)(mouse-off-slider/2)/(float)(dim-slider), 0.0f, 1.0f); => math::clamp((float)(mouse-off-slider/2)/(float)(dim-slider), 0.0f, 1.0f);

View File

@ -1,4 +1,4 @@
default { div {
bg: #282828ff; bg: #282828ff;
fg: #fbf1c7ff; fg: #fbf1c7ff;
primary: #cc241dff; primary: #cc241dff;
@ -63,6 +63,17 @@ slider {
accent: #fabd2fff; accent: #fabd2fff;
} }
scrollbar {
padding: 2;
size: 8;
bg: #45858842;
fg: #fbf1c7ff;
primary: #cc241dff;
secondary: #458588ff;
accent: #fabd2fff;
}
text-box { text-box {
bg: #4a4543ff; bg: #4a4543ff;
fg: #fbf1c7ff; fg: #fbf1c7ff;

View File

@ -152,6 +152,8 @@ fn int main(String[] args)
mod.rctrl = e.key.key == K_RCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.rctrl; mod.rctrl = e.key.key == K_RCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.rctrl;
mod.lctrl = e.key.key == K_LCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.lctrl; mod.lctrl = e.key.key == K_LCTRL ? !!(e.type == EVENT_KEY_DOWN) : mod.lctrl;
mod.rshift = e.key.key == K_RSHIFT ? !!(e.type == EVENT_KEY_DOWN) : mod.rshift;
mod.lshift = e.key.key == K_LSHIFT ? !!(e.type == EVENT_KEY_DOWN) : mod.lshift;
mod.bkspc = e.key.key == K_BACKSPACE ? !!(e.type == EVENT_KEY_DOWN) : mod.bkspc; mod.bkspc = e.key.key == K_BACKSPACE ? !!(e.type == EVENT_KEY_DOWN) : mod.bkspc;
mod.del = e.key.key == K_DELETE ? !!(e.type == EVENT_KEY_DOWN) : mod.del; mod.del = e.key.key == K_DELETE ? !!(e.type == EVENT_KEY_DOWN) : mod.del;
mod.up = e.key.key == K_UP ? !!(e.type == EVENT_KEY_DOWN) : mod.up; mod.up = e.key.key == K_UP ? !!(e.type == EVENT_KEY_DOWN) : mod.up;
@ -207,7 +209,7 @@ fn int main(String[] args)
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true; if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true;
const String APPLICATION = "calculator"; const String APPLICATION = "debug";
$switch APPLICATION: $switch APPLICATION:
$case "debug": $case "debug":
debug_app(&ui); debug_app(&ui);
@ -219,7 +221,7 @@ $endswitch
TimeStats dts = draw_times.get_stats(); TimeStats dts = draw_times.get_stats();
TimeStats uts = ui_times.get_stats(); TimeStats uts = ui_times.get_stats();
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT, true) { ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT, absolute: true, off: {10, 10}) {
ui.text(string::tformat("frame %d, fps = %.2f", frame, fps))!!; ui.text(string::tformat("frame %d, fps = %.2f", frame, fps))!!;
ui.text(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!; ui.text(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
}!!; }!!;
@ -251,6 +253,25 @@ $endswitch
return 0; return 0;
} }
fn void debug_app(ugui::Ctx* ui)
{
static LayoutDirection d = ROW;
ui.@div(ugui::@grow(), ugui::@grow(), anchor: CENTER) {
ui.@div(ugui::@exact(300), ugui::@exact(300), d, scroll_x: true, scroll_y: true) {
if (ui.button("one")!!.mouse_release) d = COLUMN;
if (ui.button("two")!!.mouse_release) d = ROW;
ui.button("three")!!;
ui.button("four")!!;
ui.button("five")!!;
ui.button("six")!!;
ui.button("seven")!!;
ui.button("eight")!!;
ui.button("nine")!!;
ui.button("ten")!!;
}!!;
}!!;
}
/* /*
fn void debug_app(ugui::Ctx* ui) fn void debug_app(ugui::Ctx* ui)
{ {
@ -410,7 +431,6 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
} }
}!!; }!!;
}!!; }!!;
ui.@div(ugui::@grow(), ugui::@fit(), anchor: CENTER) { ui.@div(ugui::@grow(), ugui::@fit(), anchor: CENTER) {
static bool state; static bool state;
ui.checkbox("boolean", &state, "tick")!!; ui.checkbox("boolean", &state, "tick")!!;
@ -422,9 +442,8 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
ui.slider_hor(ugui::@exact(100), ugui::@exact(20), &f)!!; ui.slider_hor(ugui::@exact(100), ugui::@exact(20), &f)!!;
ui.slider_ver(ugui::@exact(20), ugui::@exact(100), &f)!!; ui.slider_ver(ugui::@exact(20), ugui::@exact(100), &f)!!;
}!!; }!!;
ui.@div(ugui::@grow(), ugui::@fit(), anchor: CENTER, scroll_y: true) { ui.@div(ugui::@grow(), ugui::@fit(), anchor: CENTER) {
ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!; ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!;
}!!; }!!;
}!!; }!!; }!!; }!!;
} }