module ugui; enum Layout { LAYOUT_ROW, LAYOUT_COLUMN, LAYOUT_FLOATING, LAYOUT_ABSOLUTE, } 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 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 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 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) { return UNEXPECTED_ELEMENT?; } parent.div.origin_r = { .x = parent.bounds.x, .y = parent.div.children_bounds.bottom_right().y, }; 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) { return UNEXPECTED_ELEMENT?; } parent.div.origin_c = { .x = parent.div.children_bounds.bottom_right().x, .y = parent.bounds.y, }; parent.div.origin_r = parent.div.origin_c; } 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 = -SCROLLBAR_DIM; } 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 = -SCROLLBAR_DIM; } return elem.bounds.add(off); } macro Point Elem.get_view_off(&elem) { return elem.get_view().sub(elem.bounds).position(); } // position the rectangle inside the parent according to the layout // parent: parent div // rect: the requested size // style: apply style <* @require ctx != null @require parent.type == ETYPE_DIV *> fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false) { ElemDiv* div = &parent.div; Rect parent_bounds, parent_view; Rect child_placement, child_occupied; // 1. Select the right origin Point origin; switch (div.layout) { case LAYOUT_ROW: origin = div.origin_r; case LAYOUT_COLUMN: origin = div.origin_c; 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 {}; } // 2. Compute the parent's view parent_bounds = parent.bounds; parent_view = parent.get_view(); // 3. Compute the placement and occupied area // grow rect (wanted size) when widht or height are less than zero bool adapt_x = rect.w <= 0; bool adapt_y = rect.h <= 0; if (adapt_x) rect.w = parent_bounds.w - parent_bounds.x - origin.x; if (adapt_y) rect.h = parent_bounds.h - parent_bounds.y - origin.y; // offset placement and area child_placement = child_placement.off(origin.add(rect.position())); child_occupied = child_occupied.off(origin.add(rect.position())); if (style) { Rect margin = ctx.style.margin; Rect border = ctx.style.border; Rect padding = ctx.style.padding; // padding, grows both the placement and occupied area child_placement = child_placement.grow(padding.position().add(padding.size())); child_occupied = child_occupied.grow(padding.position().add(padding.size())); // border, grows both the placement and occupied area child_placement = child_placement.grow(border.position().add(border.size())); child_occupied = child_occupied.grow(border.position().add(border.size())); // margin, offsets the placement and grows the occupied area child_placement = child_placement.off(margin.position()); child_occupied = child_occupied.grow(margin.position().add(margin.size())); // oh yeah also adjust the rect if i was to grow if (adapt_x) rect.w -= padding.x+padding.w + border.x+border.w + margin.x+margin.w; if (adapt_y) rect.h -= padding.y+padding.h + border.y+border.h + margin.y+margin.h; } // set the size child_placement = child_placement.grow(rect.size()); child_occupied = child_occupied.grow(rect.size()); // 4. Update the parent's origin div.origin_r = { .x = child_occupied.bottom_right().x, .y = origin.y, }; div.origin_c = { .x = origin.x, .y = child_occupied.bottom_right().y, }; // 5. Update the parent's children bounds if (!child_occupied.bottom_right().in_rect(div.children_bounds)) { // right overflow if (child_occupied.bottom_right().x > div.children_bounds.bottom_right().x) { div.children_bounds.w += child_occupied.bottom_right().x - div.children_bounds.bottom_right().x; } // bottom overflow if (child_occupied.bottom_right().y > div.children_bounds.bottom_right().y) { div.children_bounds.h += child_occupied.bottom_right().y - div.children_bounds.bottom_right().y; } } // 99. return the placement if (child_placement.collides(parent_view)) { return child_placement.off(parent.get_view_off().neg()); } else { return {}; } }