# layout_run.ludic - a box's children given their places: along its direction by size, grow and # justify, across it by align (or each child's own `self`), a wrapping row one line at a time function ly_place(ui_st: mut UiState, n: UiNode, x: float, y: float, w: float, h: float) -> void { n.x = x n.y = y n.cw = w n.ch = h if (n.kind != UI_BOX and n.kind != UI_SCROLL) or n.children == null { return } let inner_w = w - ly_hin(n) var inner_h = h - ly_vin(n) if n.kind == UI_SCROLL { inner_h = Math.max(n.content_h, h) - ly_vin(n) } let top = n.y + n.pt + n.bt if n.dir == UI_ROW and n.wrap { ly_place_wrap(ui_st, n, top, inner_w) } else if n.dir == UI_ROW { ly_place_line(ui_st, n, 0, len(n.children), top, inner_w, inner_h) } else { ly_place_line(ui_st, n, 0, len(n.children), top, inner_h, inner_w) } for i in 0 .. len(n.children) { if n.children[i].pos >= 2 { ly_place_abs(ui_st, n, n.children[i]) } } } function ly_fills(c: UiNode, dir: int) -> bool { if dir == UI_ROW { return c.w == UI_FILL } return c.h == UI_FILL } function ly_grow(c: UiNode, dir: int) -> float { if ly_fills(c, dir) { return Math.max(c.grow, 1.0) } return c.grow } # where a child starts along the run: `fill` from nothing, a percentage from the room it is in function ly_basis(c: UiNode, dir: int, room: float) -> float { if ly_fills(c, dir) { return 0.0 } if dir == UI_ROW and c.w_pct > 0.0 { return ly_pw(c, room) + c.ml + c.mr } if dir == UI_COL and c.h_pct > 0.0 { return ly_ph(c, room) + c.mt + c.mb } return ly_main(c, dir) } function ly_stretches(n: UiNode, c: UiNode) -> bool { if ly_fills(c, 1 - n.dir) { return true } var a = n.align if c.self >= 0 { a = c.self } return a == UI_STRETCH } # children from..to along one line starting at `top`: sizes, the spare room, then each place function ly_place_line(ui_st: mut UiState, n: UiNode, from: int, to: int, top: float, main_room: float, cross_room: float) -> void { var used = 0.0 var grows = 0.0 var count = 0 let depth = ui_st.ly_depth ui_st.ly_depth += 1 let sizes = ly_scratch(ui_st, depth) for i in from .. to { ui_kept(sizes, ly_basis(n.children[i], n.dir, main_room)) if n.children[i].pos < 2 { used = used + sizes[i - from] grows = grows + ly_grow(n.children[i], n.dir) count += 1 } } used = used + n.gap * float(Math.max(count - 1, 0)) let free = main_room - used if free < 0.0 { ly_shrink_line(ui_st, depth, n, from, sizes, 0.0 - free) } ly_justify(ui_st, n, free, count, grows) let extra = ui_st.ly_extra var at = ui_st.ly_lead for i in from .. to { let c = n.children[i] if c.pos >= 2 { continue } var m = sizes[i - from] if grows > 0.0 and free > 0.0 { m = m + free * ly_grow(c, n.dir) / grows } ly_place_child(ui_st, n, c, at, top, m, cross_room) if c.pos == 1 { ly_nudge(n, c) } at = at + m + n.gap + extra } ui_st.ly_depth = depth } # a line's sizes, and its shrink's frozen flags, in lists kept one a depth (a line's children lay out # their own lines while it is still placing them), emptied rather than made anew function ly_scratch(ui_st: mut UiState, depth: int) -> []float { while len(ui_st.ly_sizes) <= depth { @alloc_ok("once per depth of nesting, then kept") push(ui_st.ly_sizes, new []float) @alloc_ok("once per depth of nesting, then kept") push(ui_st.ly_frozen, new []bool) } List.clear(ui_st.ly_sizes[depth]) return ui_st.ly_sizes[depth] } # one child: `m` along the run and its own size (or the whole room, stretched) across, less margins function ly_place_child(ui_st: mut UiState, n: UiNode, c: UiNode, at: float, top: float, m: float, cross_room: float) -> void { var cr = ly_cross(c, n.dir) if n.dir == UI_ROW and c.h_pct > 0.0 { cr = ly_ph(c, cross_room) + c.mt + c.mb } if n.dir == UI_COL and c.w_pct > 0.0 { cr = ly_pw(c, cross_room) + c.ml + c.mr } if ly_stretches(n, c) { cr = cross_room } var a = n.align if c.self >= 0 { a = c.self } var off = 0.0 if a == UI_CENTER { off = (cross_room - cr) / 2.0 } if a == UI_END { off = cross_room - cr } let left = n.x + n.pl + n.bl if n.dir == UI_ROW { let cw = ly_clamp(m - c.ml - c.mr, c.min_w, c.max_w) ly_place(ui_st, c, left + at + c.ml, top + off + c.mt, cw, ly_clamp(cr - c.mt - c.mb, c.min_h, c.max_h)) } else { let chh = ly_clamp(m - c.mt - c.mb, c.min_h, c.max_h) ly_place(ui_st, c, left + off + c.ml, top + at + c.mt, ly_clamp(cr - c.ml - c.mr, c.min_w, c.max_w), chh) } }