2024-10-29 22:45:47 +01:00
|
|
|
module ugui;
|
|
|
|
|
|
2024-12-19 00:29:30 +01:00
|
|
|
enum Layout {
|
|
|
|
|
LAYOUT_ROW,
|
|
|
|
|
LAYOUT_COLUMN,
|
|
|
|
|
LAYOUT_FLOATING,
|
|
|
|
|
LAYOUT_ABSOLUTE,
|
|
|
|
|
}
|
2024-10-29 22:45:47 +01:00
|
|
|
|
|
|
|
|
fn void! Ctx.layout_set_row(&ctx)
|
|
|
|
|
{
|
|
|
|
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
|
|
|
|
Elem *parent = ctx.cache.search(parent_id)!;
|
|
|
|
|
|
|
|
|
|
if (parent.type != ETYPE_DIV) {
|
|
|
|
|
// what?
|
|
|
|
|
return UgError.UNEXPECTED_ELEMENT?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.div.layout = LAYOUT_ROW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn void! Ctx.layout_set_column(&ctx)
|
|
|
|
|
{
|
|
|
|
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
|
|
|
|
Elem *parent = ctx.cache.search(parent_id)!;
|
|
|
|
|
|
|
|
|
|
if (parent.type != ETYPE_DIV) {
|
|
|
|
|
// what?
|
|
|
|
|
return UgError.UNEXPECTED_ELEMENT?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.div.layout = LAYOUT_COLUMN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn void! Ctx.layout_set_floating(&ctx)
|
|
|
|
|
{
|
|
|
|
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
|
|
|
|
Elem *parent = ctx.cache.search(parent_id)!;
|
|
|
|
|
|
|
|
|
|
if (parent.type != ETYPE_DIV) {
|
|
|
|
|
// what?
|
|
|
|
|
return UgError.UNEXPECTED_ELEMENT?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.div.layout = LAYOUT_FLOATING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn void! Ctx.layout_next_row(&ctx)
|
|
|
|
|
{
|
|
|
|
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
|
|
|
|
Elem *parent = ctx.cache.search(parent_id)!;
|
|
|
|
|
|
|
|
|
|
if (parent.type != ETYPE_DIV) {
|
|
|
|
|
// what?
|
|
|
|
|
return UgError.UNEXPECTED_ELEMENT?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.div.origin_r = Point{
|
2024-12-14 13:29:45 +01:00
|
|
|
.x = parent.bounds.x,
|
|
|
|
|
.y = parent.div.origin_c.y,
|
2024-10-29 22:45:47 +01:00
|
|
|
};
|
|
|
|
|
parent.div.origin_c = parent.div.origin_r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn void! Ctx.layout_next_column(&ctx)
|
|
|
|
|
{
|
|
|
|
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
|
|
|
|
Elem *parent = ctx.cache.search(parent_id)!;
|
|
|
|
|
|
|
|
|
|
if (parent.type != ETYPE_DIV) {
|
|
|
|
|
// what?
|
|
|
|
|
return UgError.UNEXPECTED_ELEMENT?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.div.origin_c = Point{
|
2024-12-14 13:29:45 +01:00
|
|
|
.x = parent.div.origin_r.x,
|
|
|
|
|
.y = parent.bounds.y,
|
2024-10-29 22:45:47 +01:00
|
|
|
};
|
|
|
|
|
parent.div.origin_r = parent.div.origin_c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// position the rectangle inside the parent according to the layout
|
2024-11-14 23:42:20 +01:00
|
|
|
// parent: parent div
|
|
|
|
|
// rect: the requested size
|
|
|
|
|
// style: apply style
|
2024-11-17 21:36:46 +01:00
|
|
|
<*
|
|
|
|
|
@require ctx != null
|
|
|
|
|
@require parent.type == ETYPE_DIV
|
|
|
|
|
*>
|
2024-10-29 22:45:47 +01:00
|
|
|
fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
|
|
|
|
|
{
|
2024-12-14 13:29:45 +01:00
|
|
|
Rect placement;
|
2024-10-29 22:45:47 +01:00
|
|
|
Point origin;
|
2024-12-15 22:29:07 +01:00
|
|
|
ElemDiv* div = &parent.div;
|
2024-10-29 22:45:47 +01:00
|
|
|
|
|
|
|
|
// 1. Select the right origin
|
2024-11-17 21:36:46 +01:00
|
|
|
switch (div.layout) {
|
2024-10-29 22:45:47 +01:00
|
|
|
case LAYOUT_ROW:
|
2024-11-17 21:36:46 +01:00
|
|
|
origin = div.origin_r;
|
2024-10-29 22:45:47 +01:00
|
|
|
case LAYOUT_COLUMN:
|
2024-11-17 21:36:46 +01:00
|
|
|
origin = div.origin_c;
|
2024-12-18 20:04:23 +01:00
|
|
|
case LAYOUT_FLOATING: // none, relative to zero zero
|
|
|
|
|
case LAYOUT_ABSOLUTE: // absolute position, this is a no-op, return the rect
|
|
|
|
|
return rect;
|
|
|
|
|
default: // error
|
|
|
|
|
return Rect{};
|
2024-10-29 22:45:47 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-14 23:42:20 +01:00
|
|
|
// the bottom-right border of the element box
|
|
|
|
|
Point pl_corner;
|
2024-10-29 22:45:47 +01:00
|
|
|
|
2024-11-14 23:42:20 +01:00
|
|
|
// 2. Calculate the placement
|
|
|
|
|
placement.x = (short)max(origin.x + rect.x, 0);
|
|
|
|
|
placement.y = (short)max(origin.y + rect.y, 0);
|
2024-11-17 21:36:46 +01:00
|
|
|
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 : (short)max(parent.bounds.h - (placement.y - parent.bounds.y), 0);
|
2024-10-29 22:45:47 +01:00
|
|
|
|
2024-11-14 23:42:20 +01:00
|
|
|
pl_corner.x = placement.x + placement.w;
|
|
|
|
|
pl_corner.y = placement.y + placement.h;
|
|
|
|
|
|
|
|
|
|
// 2.1 apply style, css box model
|
|
|
|
|
if (style) {
|
2024-12-12 15:46:24 +01:00
|
|
|
Rect margin = ctx.style.margin;
|
|
|
|
|
Rect border = ctx.style.border;
|
2024-11-14 23:42:20 +01:00
|
|
|
Rect padding = ctx.style.padding;
|
|
|
|
|
|
|
|
|
|
placement.x += margin.x;
|
|
|
|
|
placement.y += margin.y;
|
2024-12-12 15:46:24 +01:00
|
|
|
if (rect.w != 0) { placement.w += border.x+border.w + padding.x+padding.w; }
|
|
|
|
|
if (rect.h != 0) { placement.h += border.y+border.h + padding.y+padding.h; }
|
2024-11-14 23:42:20 +01:00
|
|
|
|
|
|
|
|
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
|
2024-11-17 21:36:46 +01:00
|
|
|
div.origin_r = Point{
|
2024-12-14 13:29:45 +01:00
|
|
|
.x = pl_corner.x,
|
|
|
|
|
.y = origin.y,
|
2024-10-29 22:45:47 +01:00
|
|
|
};
|
2024-11-17 21:36:46 +01:00
|
|
|
div.origin_c = Point{
|
2024-12-14 13:29:45 +01:00
|
|
|
.x = origin.x,
|
|
|
|
|
.y = pl_corner.y,
|
2024-10-29 22:45:47 +01:00
|
|
|
};
|
|
|
|
|
|
2024-11-14 23:42:20 +01:00
|
|
|
// 4. Calculate the "scrolled" view
|
2024-12-19 00:29:30 +01:00
|
|
|
Rect off;
|
2024-11-17 21:36:46 +01:00
|
|
|
Rect* cb = &div.children_bounds;
|
2024-12-19 00:29:30 +01:00
|
|
|
if (div.scroll_x.enabled && div.scroll_x.on) {
|
|
|
|
|
off.x = (short)((float)(div.pcb.w - parent.bounds.w) * div.scroll_x.value);
|
|
|
|
|
off.w = SCROLLBAR_DIM;
|
2024-11-14 23:42:20 +01:00
|
|
|
}
|
2024-12-19 00:29:30 +01:00
|
|
|
if (div.scroll_y.enabled && div.scroll_y.on) {
|
|
|
|
|
off.y = (short)((float)(div.pcb.h - parent.bounds.h) * div.scroll_y.value);
|
|
|
|
|
off.h = SCROLLBAR_DIM;
|
2024-10-29 22:45:47 +01:00
|
|
|
}
|
2024-11-14 23:42:20 +01:00
|
|
|
Rect view = {
|
|
|
|
|
.x = parent.bounds.x + off.x,
|
|
|
|
|
.y = parent.bounds.y + off.y,
|
2024-12-19 00:29:30 +01:00
|
|
|
.w = parent.bounds.w - off.w,
|
|
|
|
|
.h = parent.bounds.h - off.h,
|
2024-11-14 23:42:20 +01:00
|
|
|
};
|
|
|
|
|
|
2024-11-17 21:36:46 +01:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-29 22:45:47 +01:00
|
|
|
|
2024-11-17 21:36:46 +01:00
|
|
|
// 6. check if the placement is inside the view
|
2024-12-17 11:26:59 +01:00
|
|
|
if (placement.collides(view)) {
|
2024-11-17 21:36:46 +01:00
|
|
|
return Rect{
|
|
|
|
|
.x = placement.x - off.x,
|
|
|
|
|
.y = placement.y - off.y,
|
|
|
|
|
.w = placement.w,
|
|
|
|
|
.h = placement.h,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
return Rect{};
|
|
|
|
|
}
|
2024-10-29 22:45:47 +01:00
|
|
|
}
|
2024-11-17 21:36:46 +01:00
|
|
|
|