in layout use a combined version of margin, border and padding

This commit is contained in:
Alessandro Mauri 2025-09-05 18:53:47 +02:00
parent 0f7d5a6506
commit 335624fcbe
3 changed files with 33 additions and 66 deletions

View File

@ -49,9 +49,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
.max = (short)max(min_size, left_pad + icon_size.w + text_size.height.max + right_pad), .max = (short)max(min_size, left_pad + icon_size.w + text_size.height.max + right_pad),
}; };
// add style border and padding // add style border and padding
elem.layout.margin = style.margin; elem.layout.content_offset = style.margin.add(style.border).add(style.padding);
elem.layout.border = style.border;
elem.layout.padding = style.padding;
elem.layout.children.w = elem.layout.w; elem.layout.children.w = elem.layout.w;
elem.layout.children.h = elem.layout.h; elem.layout.children.h = elem.layout.h;
@ -60,7 +58,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
update_parent_size(elem, parent); update_parent_size(elem, parent);
elem.events = ctx.get_elem_events(elem); elem.events = ctx.get_elem_events(elem);
Rect content_bounds = elem.content_bounds(style); Rect content_bounds = elem.content_bounds();
Rect text_bounds = { Rect text_bounds = {
.x = content_bounds.x + icon_size.w + left_pad + inner_pad, .x = content_bounds.x + icon_size.w + left_pad + inner_pad,
@ -85,7 +83,8 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
s.bg = s.accent; s.bg = s.accent;
} }
ctx.push_rect(elem.bounds, parent.div.z_index, &s)!; Rect border_bounds = elem.bounds.pad(style.margin);
ctx.push_rect(border_bounds, parent.div.z_index, &s)!;
if (icon != "") { if (icon != "") {
ctx.push_sprite(icon_bounds, sprite.uv(), ctx.sprite_atlas.id, parent.div.z_index, type: sprite.type)!; ctx.push_sprite(icon_bounds, sprite.uv(), ctx.sprite_atlas.id, parent.div.z_index, type: sprite.type)!;
} }

View File

@ -78,15 +78,13 @@ fn void? Ctx.div_begin_id(&ctx,
.h = height, .h = height,
.dir = dir, .dir = dir,
.anchor = anchor, .anchor = anchor,
.margin = style.margin, .content_offset = style.margin.add(style.border).add(style.padding),
.border = style.border,
.padding = style.padding,
}; };
// update parent grow children // update parent grow children
update_parent_grow(elem, parent); update_parent_grow(elem, parent);
ctx.push_rect(elem.bounds, elem.div.z_index, style)!; ctx.push_rect(elem.bounds.pad(style.margin), elem.div.z_index, style)!;
elem.events = ctx.get_elem_events(elem); elem.events = ctx.get_elem_events(elem);

View File

@ -21,8 +21,8 @@ enum Anchor {
} }
struct Layout { struct Layout {
Size w, h; Size w, h; // size of the CONTENT, does not include margin, border and padding
struct children { struct children { // the children size includes the children's margin/border/pading
Size w, h; Size w, h;
} }
ushort grow_children; ushort grow_children;
@ -32,24 +32,22 @@ struct Layout {
} }
LayoutDirection dir; LayoutDirection dir;
Anchor anchor; Anchor anchor;
// FIXME: all this cruft with margin border and padding could be resolved by simply providing Rect content_offset; // combined effect of margin, border and padding
// a "Rect content_offset" that represents the combined effect of all three above
Rect margin, border, padding;
} }
// Total width of a layout element, complete with margin, border and padding // Total width of a layout element, complete with margin, border and padding
macro Size Layout.total_width(&el) macro Size Layout.total_width(&el)
{ {
Rect min = (Rect){.w = el.w.min, .h = el.h.min}.expand(el.margin).expand(el.border).expand(el.padding); Rect min = (Rect){.w = el.w.min, .h = el.h.min}.expand(el.content_offset);
Rect max = (Rect){.w = el.w.max, .h = el.h.max}.expand(el.margin).expand(el.border).expand(el.padding); Rect max = (Rect){.w = el.w.max, .h = el.h.max}.expand(el.content_offset);
return {.min = min.w, .max = max.w}; return {.min = min.w, .max = max.w};
} }
// Total height of a layout element, complete with margin, border and padding // Total height of a layout element, complete with margin, border and padding
macro Size Layout.total_height(&el) macro Size Layout.total_height(&el)
{ {
Rect min = (Rect){.w = el.w.min, .h = el.h.min}.expand(el.margin).expand(el.border).expand(el.padding); Rect min = (Rect){.w = el.w.min, .h = el.h.min}.expand(el.content_offset);
Rect max = (Rect){.w = el.w.max, .h = el.h.max}.expand(el.margin).expand(el.border).expand(el.padding); Rect max = (Rect){.w = el.w.max, .h = el.h.max}.expand(el.content_offset);
return {.min = min.h, .max = max.h}; return {.min = min.h, .max = max.h};
} }
@ -57,8 +55,8 @@ macro Size Layout.total_height(&el)
macro Point Elem.content_space(&e) macro Point Elem.content_space(&e)
{ {
return { return {
.x = e.bounds.w - e.layout.border.x - e.layout.border.w - e.layout.padding.x - e.layout.padding.w, .x = e.bounds.w - e.layout.content_offset.x - e.layout.content_offset.w,
.y = e.bounds.h - e.layout.border.y - e.layout.border.h - e.layout.padding.y - e.layout.padding.h, .y = e.bounds.h - e.layout.content_offset.y - e.layout.content_offset.h,
}; };
} }
@ -94,7 +92,7 @@ fn void update_children_bounds(Elem* child, Elem* parent)
parent.children_bounds = containing_rect(child.bounds, parent.bounds); parent.children_bounds = containing_rect(child.bounds, parent.bounds);
} }
macro Rect Elem.content_bounds(&elem, style) => elem.bounds.pad(style.border).pad(style.padding); macro Rect Elem.content_bounds(&elem) => elem.bounds.pad(elem.layout.content_offset);
/* /*
macro Rect Elem.get_view(&elem) macro Rect Elem.get_view(&elem)
@ -120,16 +118,11 @@ macro Point Elem.get_view_off(&elem)
// 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)
{ {
// The element's border and paddign total width and height
Point m_tot = {.x = e.layout.margin.x + e.layout.margin.w, .y = e.layout.margin.y + e.layout.margin.h};
Point b_tot = {.x = e.layout.border.x + e.layout.border.w, .y = e.layout.border.y + e.layout.border.h};
Point p_tot = {.x = e.layout.padding.x + e.layout.padding.w, .y = e.layout.padding.y + e.layout.padding.h};
// ASSIGN WIDTH // ASSIGN WIDTH
switch { switch {
case e.layout.w.@is_exact(): case e.layout.w.@is_exact():
// if width is exact then assign it to the bounds and add border and paddign // if width is exact then assign it to the bounds
e.bounds.w = e.layout.w.min + b_tot.x + p_tot.x; e.bounds.w = e.layout.total_width().min;
case e.layout.w.@is_grow(): case e.layout.w.@is_grow():
// done in another pass // done in another pass
break; break;
@ -140,20 +133,10 @@ fn void resolve_dimensions(Elem* e, Elem* p)
if (combined.min <= combined.max) { // OK! if (combined.min <= combined.max) { // OK!
// the final element width is the content width plus padding and border // the final element width is the content width plus padding and border
e.bounds.w = min(elem_width.max, children_width.max) + b_tot.x + p_tot.x; e.bounds.w = min(elem_width.max, children_width.max) + e.layout.content_offset.x + e.layout.content_offset.w;
} else { } else {
unreachable("Cannot fit children width: min:%d max:%d", combined.min, combined.max); unreachable("Cannot fit children width: min:%d max:%d", combined.min, combined.max);
} }
/*
Size w = e.needed_width().combine(e.layout.children.w);
if (w.max >= w.min) { // OK!
e.bounds.w = w.min;
} else {
println(e.needed_width(), " ", e.needed_height());
println(e.layout.children.w, " ", e.layout.children.h);
unreachable("Cannot fit children width: min:%d max:%d", w.min, w.max);
}
*/
default: unreachable("width is not exact, grow or fit"); default: unreachable("width is not exact, grow or fit");
} }
@ -161,7 +144,7 @@ fn void resolve_dimensions(Elem* e, Elem* p)
switch { switch {
case e.layout.h.@is_exact(): case e.layout.h.@is_exact():
// if width is exact then assign it to the bounds and add border and paddign // if width is exact then assign it to the bounds and add border and paddign
e.bounds.h = e.layout.h.min + b_tot.y + p_tot.y; e.bounds.h = e.layout.total_height().min;
case e.layout.h.@is_grow(): case e.layout.h.@is_grow():
break; break;
// done in another pass // done in another pass
@ -172,47 +155,34 @@ fn void resolve_dimensions(Elem* e, Elem* p)
if (combined.min <= combined.max) { // OK! if (combined.min <= combined.max) { // OK!
// the final element width is the content width plus padding and border // the final element width is the content width plus padding and border
e.bounds.h = min(elem_height.max, children_height.max) + b_tot.y + p_tot.y; e.bounds.h = min(elem_height.max, children_height.max) + e.layout.content_offset.y + e.layout.content_offset.h;
} else { } else {
unreachable("Cannot fit children height: min:%d max:%d", combined.min, combined.max); unreachable("Cannot fit children height: min:%d max:%d", combined.min, combined.max);
} }
/*
Size h = e.needed_height().combine(e.layout.children.h);
if (h.max >= h.min) { // OK!
e.bounds.h = h.max;
} else {
unreachable("Cannot fit children height: min:%d max:%d", h.min, h.max);
}
*/
default: unreachable("width is not exact, grow or fit"); default: unreachable("width is not exact, grow or fit");
} }
// the occupied space must account for the element's margin as well
switch (p.layout.dir) { switch (p.layout.dir) {
case ROW: case ROW:
if (!e.layout.w.@is_grow()) p.layout.occupied += e.bounds.w + m_tot.x; if (!e.layout.w.@is_grow()) p.layout.occupied += e.bounds.w;
case COLUMN: case COLUMN:
if (!e.layout.h.@is_grow()) p.layout.occupied += e.bounds.h + m_tot.y; if (!e.layout.h.@is_grow()) p.layout.occupied += e.bounds.h;
} }
} }
fn void resolve_grow_elements(Elem* e, Elem* p) fn void resolve_grow_elements(Elem* e, Elem* p)
{ {
Point m_tot = {.x = e.layout.margin.x + e.layout.margin.w, .y = e.layout.margin.y + e.layout.margin.h};
Point b_tot = {.x = e.layout.border.x + e.layout.border.w, .y = e.layout.border.y + e.layout.border.h};
Point p_tot = {.x = e.layout.padding.x + e.layout.padding.w, .y = e.layout.padding.y + e.layout.padding.h};
// 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 (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);
// the space slot accounts for the total size, while the element bounds does not account // the space slot accounts for the total size, while the element bounds does not account
// for the margin // for the margin
e.bounds.w = slot - m_tot.x; e.bounds.w = slot;
p.layout.grow_children--; p.layout.grow_children--;
p.layout.occupied += slot; p.layout.occupied += slot;
} else if (p.layout.dir == COLUMN) { // grow across the layout axis, inherit width of the parent } else if (p.layout.dir == COLUMN) { // grow across the layout axis, inherit width of the parent
e.bounds.w = p.content_space().x - m_tot.x; e.bounds.w = p.content_space().x;
} }
} }
@ -220,11 +190,11 @@ fn void resolve_grow_elements(Elem* e, Elem* p)
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 (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 - m_tot.y; e.bounds.h = slot;
p.layout.grow_children--; p.layout.grow_children--;
p.layout.occupied += slot; p.layout.occupied += slot;
} else if (p.layout.dir == ROW) { // grow across the layout axis, inherit width of the parent } else if (p.layout.dir == ROW) { // grow across the layout axis, inherit width of the parent
e.bounds.h = p.content_space().y - m_tot.y; e.bounds.h = p.content_space().y;
} }
} }
} }
@ -235,8 +205,8 @@ fn void resolve_placement(Elem* c, Elem* p)
Layout* cl = &c.layout; Layout* cl = &c.layout;
Point off = { Point off = {
.x = p.bounds.x + pl.origin.x + cl.margin.x, .x = p.bounds.x + pl.origin.x,
.y = p.bounds.y + pl.origin.y + cl.margin.x, .y = p.bounds.y + pl.origin.y,
}; };
switch (pl.anchor) { switch (pl.anchor) {
@ -320,9 +290,9 @@ fn void resolve_placement(Elem* c, Elem* p)
switch (pl.dir) { switch (pl.dir) {
case ROW: case ROW:
pl.origin.x += c.bounds.w + cl.margin.x + cl.margin.w; pl.origin.x += c.bounds.w;
case COLUMN: case COLUMN:
pl.origin.y += c.bounds.h + cl.margin.x + cl.margin.w; pl.origin.y += c.bounds.h;
default: unreachable("unknown layout direction"); default: unreachable("unknown layout direction");
} }
@ -336,8 +306,8 @@ fn void Ctx.layout_element_tree(&ctx)
for (; current >= 0; current = ctx.tree.level_order_it(0, &cursor)!!) { for (; current >= 0; current = ctx.tree.level_order_it(0, &cursor)!!) {
Elem* p = ctx.find_elem(ctx.tree.get(current))!!; Elem* p = ctx.find_elem(ctx.tree.get(current))!!;
// offset the origin by the margin // offset the origin by the margin
p.layout.origin.x = p.layout.border.x + p.layout.padding.x; p.layout.origin.x = p.layout.content_offset.x;
p.layout.origin.y = p.layout.border.y + p.layout.padding.y; p.layout.origin.y = p.layout.content_offset.y;
// RESOLVE KNOWN DIMENSIONS // RESOLVE KNOWN DIMENSIONS
isz ch_cur = 0; isz ch_cur = 0;