From 763e9ba8d61b6a59ae2ac499d219eb0b0b9b8513 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Thu, 14 Nov 2024 23:42:20 +0100 Subject: [PATCH] correct placement and box model --- src/main.c3 | 6 +-- src/ugui_data.c3 | 11 ++++++ src/ugui_div.c3 | 7 ++++ src/ugui_impl.c3 | 6 +++ src/ugui_layout.c3 | 93 ++++++++++++++++++++++++++++------------------ 5 files changed, 83 insertions(+), 40 deletions(-) diff --git a/src/main.c3 b/src/main.c3 index 93dfbb8..b64da0b 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -53,15 +53,15 @@ fn int main(String[] args) // main div, fill the whole window ctx.div_begin("main", ugui::Rect{.w=ctx.width/2})!!; {| - ctx.layout_set_column()!!; + ctx.layout_set_row()!!; if (ctx.button("button0", ugui::Rect{0,0,30,30})!!.mouse_press) { io::printn("press button0"); } - ctx.layout_next_column()!!; + //ctx.layout_next_column()!!; if (ctx.button("button1", ugui::Rect{0,0,30,30})!!.mouse_press) { io::printn("press button1"); } - ctx.layout_next_column()!!; + //ctx.layout_next_column()!!; if (ctx.button("button2", ugui::Rect{0,0,30,30})!!.mouse_release) { io::printn("release button2"); } diff --git a/src/ugui_data.c3 b/src/ugui_data.c3 index 834eeff..34bbb9a 100644 --- a/src/ugui_data.c3 +++ b/src/ugui_data.c3 @@ -52,6 +52,11 @@ enum DivLayout { // div element struct Div { DivLayout layout; + bool can_scroll_x; + bool can_scroll_y; + isz vertical_scroll_bar; + isz horizontal_scroll_bar; + Rect children_bounds; Point origin_r, origin_c; Color color_bg; } @@ -208,3 +213,9 @@ macro rect_intersection(Rect a, Rect b) .h = (short)min(a.y+a.h, b.y+b.h) - (short)max(a.y, b.y), }; } + +// rect intersection not null +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); +} diff --git a/src/ugui_div.c3 b/src/ugui_div.c3 index 6980a47..1ecb386 100644 --- a/src/ugui_div.c3 +++ b/src/ugui_div.c3 @@ -17,6 +17,9 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size) if (c_elem.flags.is_new || parent.flags.updated) { // 2. layout the element c_elem.bounds = ctx.position_element(parent, size); + if (c_elem.flags.is_new) { + c_elem.div.children_bounds = c_elem.bounds; + } // 3. Mark the element as updated c_elem.flags.updated = true; @@ -28,6 +31,10 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size) }; c_elem.div.color_bg = uint_to_rgba(0xff0000ff); c_elem.div.origin_r = c_elem.div.origin_c; + c_elem.div.can_scroll_x = false; + c_elem.div.can_scroll_y = false; + c_elem.div.vertical_scroll_bar = -1; + c_elem.div.horizontal_scroll_bar = -1; } // Add the background to the draw stack diff --git a/src/ugui_impl.c3 b/src/ugui_impl.c3 index 5a84e42..c916b40 100644 --- a/src/ugui_impl.c3 +++ b/src/ugui_impl.c3 @@ -32,6 +32,12 @@ macro Ctx.get_elem_by_label(&ctx, String label) return ctx.cache.search(id); } +macro Ctx.get_elem_by_tree_idx(&ctx, isz idx) @private +{ + Id id = ctx.tree.get(ctx.active_div)!; + return ctx.cache.search(id); +} + fn void! Ctx.init(&ctx) { ctx.tree.init(MAX_ELEMENTS)!; diff --git a/src/ugui_layout.c3 b/src/ugui_layout.c3 index c3416f6..a8e47e5 100644 --- a/src/ugui_layout.c3 +++ b/src/ugui_layout.c3 @@ -75,9 +75,12 @@ fn void! Ctx.layout_next_column(&ctx) } // position the rectangle inside the parent according to the layout +// parent: parent div +// rect: the requested size +// style: apply style fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) { - Rect bounds; + Rect placement; Point origin; // 1. Select the right origin @@ -91,49 +94,65 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) // Error } - // 2. Position the rect - bounds.x = (short)max(origin.x + rect.x, 0); - bounds.y = (short)max(origin.y + rect.y, 0); + // the bottom-right border of the element box + Point pl_corner; - // 3. Calculate width & height - // FIXME: account for origin offset!! - bounds.w = rect.w > 0 ? rect.w : parent.bounds.w; - bounds.h = rect.h > 0 ? rect.h : parent.bounds.h; + // 2. Calculate the placement + placement.x = (short)max(origin.x + rect.x, 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.h = rect.h > 0 ? rect.h : parent.bounds.h - (placement.y - parent.bounds.y); - // 4. Update the origins of the parent + pl_corner.x = placement.x + placement.w; + pl_corner.y = placement.y + placement.h; + + // 2.1 apply style, css box model + if (style) { + Rect margin = ctx.style.margin; + Rect border = ctx.style.border; + Rect padding = ctx.style.padding; + + placement.x += margin.x; + placement.y += margin.y; + if (rect.w != 0) { placement.w += margin.x+margin.w + padding.x+padding.w; } + if (rect.h != 0) { placement.h += margin.y+margin.h + padding.y+padding.h; } + + pl_corner.x = placement.x + placement.w + margin.w; + pl_corner.y = placement.y + placement.h + margin.h; + } + + // 3. Update the origins of the parent parent.div.origin_r = Point{ - .x = bounds.x + bounds.w, - .y = bounds.y, + .x = pl_corner.x, + .y = origin.y, }; parent.div.origin_c = Point{ - .x = bounds.x, - .y = bounds.y + bounds.h, + .x = origin.x, + .y = pl_corner.y, }; - // if using the style then apply margins - // FIXME: this does not work - if (style && parent.div.layout != LAYOUT_FLOATING) { - bounds.x += ctx.style.margin.x; - bounds.y += ctx.style.margin.y; - - // total keep-out borders - Rect margin_tot = { - .x = ctx.style.padding.x + ctx.style.border.x + - ctx.style.margin.x, - .y = ctx.style.padding.y + ctx.style.border.y + - ctx.style.margin.y, - .w = ctx.style.padding.w + ctx.style.border.x + - ctx.style.margin.w, - .h = ctx.style.padding.h + ctx.style.border.x + - ctx.style.margin.h, - }; - - parent.div.origin_r.x += margin_tot.x + margin_tot.w; - // parent.div.origin_r.y += margin_tot.h; - - // parent.div.origin_c.x += margin_tot.w; - parent.div.origin_c.y += margin_tot.y + margin_tot.h; + // 4. Calculate the "scrolled" view + Point off; + if (parent.div.can_scroll_x && parent.div.horizontal_scroll_bar != -1) { + Elem*! sx = ctx.get_elem_by_tree_idx(parent.div.horizontal_scroll_bar); + if (catch sx) { return Rect{}; } + // 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) { + Elem*! sy = ctx.get_elem_by_tree_idx(parent.div.vertical_scroll_bar); + 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 = { + .x = parent.bounds.x + off.x, + .y = parent.bounds.y + off.y, + .w = parent.bounds.w, + .h = parent.bounds.h, + }; + + // TODO: 5. check if the placement is inside the view - return bounds; + return placement; }