# ui_draw.ludic — layout arrange, drawing, focus/activation and the game-facing # accessors. Split out of ui.ludic (the storage/build/measure half). # ---- arrange: rects, parents before children ------------------------------ fn ui_arrange(i: int, x: int, y: int) -> void { poke32(ui_rx, i, x) poke32(ui_ry, i, y) let t = peek32(ui_type, i) if ui_is_container(t) == false { return } let dir = ui_dir(t) let pad = peek32(ui_pad, i) let gap = peek32(ui_gap, i) let innerw = peek32(ui_rw, i) - 2 * pad let innerh = peek32(ui_rh, i) - 2 * pad var total = 0 var ng = 0 var grows = 0 for c in 0 .. ui_n { if peek32(ui_parent, c) == i { if peek32(ui_visible, c) == 1 { if dir == 1 { total = total + peek32(ui_rh, c) } else { total = total + peek32(ui_rw, c) } ng = ng + 1 grows = grows + peek32(ui_grow, c) } } } if ng > 1 { total = total + gap * (ng - 1) } var avail = innerw if dir == 1 { avail = innerh } let extra = max(avail - total, 0) var cx = x + pad var cy = y + pad for c in 0 .. ui_n { if peek32(ui_parent, c) == i { if peek32(ui_visible, c) == 1 { var kw = peek32(ui_rw, c) var kh = peek32(ui_rh, c) if peek32(ui_grow, c) != 0 { if grows > 0 { if dir == 1 { kh = kh + extra / grows } else { kw = kw + extra / grows } } } let al = ui_eff_align(c, i) poke32(ui_rw, c, kw) poke32(ui_rh, c, kh) if dir == 1 { var ax = x + pad if al == 1 { ax = x + pad + (innerw - kw) / 2 } if al == 2 { ax = x + pad + (innerw - kw) } ui_arrange(c, ax, cy) cy = cy + kh + gap } else { var ay = y + pad if al == 1 { ay = y + pad + (innerh - kh) / 2 } if al == 2 { ay = y + pad + (innerh - kh) } ui_arrange(c, cx, ay) cx = cx + kw + gap } } } } } fn ui_layout() -> void { if ui_active < 0 { return } ui_measure() var px = (rt_fbw - peek32(ui_rw, ui_active)) / 2 var py = (rt_fbh - peek32(ui_rh, ui_active)) / 2 if peek32(ui_haspos, ui_active) == 1 { px = peek32(ui_x, ui_active) py = peek32(ui_y, ui_active) } ui_arrange(ui_active, px, py) } # ---- drawing -------------------------------------------------------------- fn ui_lighten(c: int) -> int { var r = ((c >> 16) & 255) var g = ((c >> 8) & 255) var b = (c & 255) r = r + (255 - r) * 4 / 10 g = g + (255 - g) * 4 / 10 b = b + (255 - b) * 4 / 10 return (((r << 16) | (g << 8)) | b) } fn ui_draw_node(i: int) -> void { if peek32(ui_visible, i) == 0 { return } let t = peek32(ui_type, i) let rx = peek32(ui_rx, i) let ry = peek32(ui_ry, i) let rw = peek32(ui_rw, i) let rh = peek32(ui_rh, i) let skin = peek32(ui_skin, i) var focused = 0 if i == ui_focus { focused = 1 } if t == WT_PANEL { if skin >= 0 { rt_draw_9slice(skin, rx, ry, rw, rh, peek32(ui_inset, i)) } else { if peek32(ui_bg, i) >= 0 { rt_fill_rect(rx, ry, rw, rh, peek32(ui_bg, i)) } if peek32(ui_border, i) >= 0 { rt_frame_rect(rx, ry, rw, rh, peek32(ui_border, i)) } } } if t == WT_BUTTON { var bg = peek32(ui_bg, i) if bg < 0 { bg = 0x2a2a3a } if focused == 1 { bg = ui_lighten(bg) } if skin >= 0 { rt_draw_9slice(skin, rx, ry, rw, rh, peek32(ui_inset, i)) } else { rt_fill_rect(rx, ry, rw, rh, bg) } var bc = peek32(ui_border, i) if bc < 0 { bc = 0x555577 } if focused == 1 { bc = 0xffff00 } rt_frame_rect(rx, ry, rw, rh, bc) let s = ui_str(i) var fg = peek32(ui_fg, i) if fg < 0 { fg = 0xffffff } let font = peek32(ui_font, i) let size = peek32(ui_size, i) ui_draw_text(font, rx + (rw - ui_tw(font, s, size)) / 2, ry + (rh - ui_th(font, size)) / 2, s, fg, size) } if t == WT_LABEL { let s = ui_str(i) var fg = peek32(ui_fg, i) if fg < 0 { fg = 0xffffff } let font = peek32(ui_font, i) let size = peek32(ui_size, i) let pad = peek32(ui_pad, i) let tw = ui_tw(font, s, size) var tx = rx + pad let al = peek32(ui_align, i) if al == 1 { tx = rx + (rw - tw) / 2 } if al == 2 { tx = rx + rw - pad - tw } ui_draw_text(font, tx, ry + pad, s, fg, size) } if t == WT_IMAGE { if peek32(ui_img, i) >= 0 { rt_draw_image_scaled(peek32(ui_img, i), rx, ry, rw, rh) } } for c in 0 .. ui_n { if peek32(ui_parent, c) == i { ui_draw_node(c) } } } # ---- focus and activation ------------------------------------------------- var ui_fl: ptr = ptr_null() fn ui_focusables() -> int { if ptr_is_null(ui_fl) { ui_fl = mem_alloc(UI_MAX * 4) } var n = 0 for i in 0 .. ui_n { if peek32(ui_focusable, i) == 1 { if peek32(ui_visible, i) == 1 { if ui_under(i, ui_active) { poke32(ui_fl, n, i) n = n + 1 } } } } return n } fn rt_ui_open(root: int) -> void { ui_active = root let n = ui_focusables() ui_focus = 0 - 1 if n > 0 { ui_focus = peek32(ui_fl, 0) } } fn rt_ui_tick(k: int) -> void { if ui_active < 0 { return } for i in 0 .. ui_n { poke32(ui_fired, i, 0) } ui_layout() let n = ui_focusables() if n == 0 { ui_focus = 0 - 1 return } var cur = 0 - 1 for i in 0 .. n { if peek32(ui_fl, i) == ui_focus { cur = i } } if cur < 0 { cur = 0 ui_focus = peek32(ui_fl, 0) } if k == 119 { # 'w' cur = (cur - 1 + n) % n ui_focus = peek32(ui_fl, cur) } if k == 115 { # 's' cur = (cur + 1) % n ui_focus = peek32(ui_fl, cur) } if k == 32 { poke32(ui_fired, ui_focus, 1) } if k == 10 { poke32(ui_fired, ui_focus, 1) } if k == 13 { poke32(ui_fired, ui_focus, 1) } } fn rt_ui_render() -> void { if ui_active < 0 { return } ui_layout() ui_draw_node(ui_active) } # ---- game-facing accessors ------------------------------------------------ fn rt_ui_clicked(id: int) -> bool { if id < 0 { return false } if id >= ui_n { return false } return peek32(ui_fired, id) == 1 } fn rt_ui_set_text(id: int, s: str) -> void { if id < 0 { return } if id >= ui_n { return } let base = id * 96 var i = 0 var ch = peek8(s, 0) while ch != 0 { if i >= 95 { ch = 0 } if ch != 0 { poke8(ui_dyn, base + i, ch) i = i + 1 ch = peek8(s, i) } } poke8(ui_dyn, base + i, 0) poke32(ui_hasdyn, id, 1) } fn rt_ui_set_int(id: int, n: int) -> void { if id < 0 { return } if id >= ui_n { return } let base = id * 96 var at = 0 var v = n if v < 0 { poke8(ui_dyn, base, 45) at = 1 v = 0 - v } if v == 0 { poke8(ui_dyn, base + at, 48) at = at + 1 } else { var digits = 0 var t = v while t > 0 { digits = digits + 1 t = t / 10 } var p = digits while p > 0 { var div = 1 for k in 1 .. p { div = div * 10 } poke8(ui_dyn, base + at, 48 + (v / div) % 10) at = at + 1 p = p - 1 } } poke8(ui_dyn, base + at, 0) poke32(ui_hasdyn, id, 1) } fn rt_ui_focus(id: int) -> void { if id < 0 { return } if id >= ui_n { return } ui_focus = id } fn rt_ui_focused() -> int { return ui_focus } fn rt_ui_visible(id: int, v: int) -> void { if id < 0 { return } if id >= ui_n { return } poke32(ui_visible, id, v) }