# emit_ui.ludic — a `ui` block is data: the compiler flattens the widget tree # and emits @ui_build(), which hands every property to the Ludic UI runtime via # rt_ui_set(index, key, value). Mirrors compiler/back/ir_ui.c. Layout/drawing/ # focus all live in runtime/native/ui.ludic. var uiw: []Node # flattened widgets, pre-order var uiw_parent: []int var ui_roots: []int # first-widget index of each ui block function ui_wtype(w: Node) -> int { let s = w.s if (s == "panel") { return 0 }; if (s == "col") { return 1 }; if (s == "row") { return 2 } if (s == "label") { return 3 }; if (s == "button") { return 4 } if (s == "image") { return 5 }; if (s == "spacer") { return 6 } return 0 } function ui_prop(w: Node, key: pointer) -> Node { var i = 0 while i < len(w.b.kids) { if (w.b.kids[i].s == key) { return w.b.kids[i].a }; i += 1 } return null } # a widget's props plus the text props (font, size, fg, align) it inherits from the # nearest ancestor that sets them — so a panel states the menu's look once. function ui_props_with_inherited(i: int) -> []Node { let w = uiw[i] let out = new []Node var p = 0 while p < len(w.b.kids) { push(out, w.b.kids[p]); p += 1 } let keys = new []pointer push(keys, "font"); push(keys, "size"); push(keys, "fg"); push(keys, "align") var k = 0 while k < len(keys) { if ui_prop(w, keys[k]) == null { var a = uiw_parent[i] while a >= 0 { let v = ui_prop(uiw[a], keys[k]) if (v != null) { let fi = node(E_FINIT); fi.s = keys[k]; fi.a = v; push(out, fi); a = -1 } else { a = uiw_parent[a] } } } k += 1 } return out } function ui_flatten(w: Node, parent: int) -> void { let idx = len(uiw) push(uiw, w); push(uiw_parent, parent) var i = 0 while i < len(w.kids) { ui_flatten(w.kids[i], idx); i += 1 } } function ui_flatten_all() -> void { uiw = new []Node; uiw_parent = new []int; ui_roots = new []int var i = 0 while i < len(prog) { if prog[i].kind == N_UI and prog[i].ival == 1 { push(ui_roots, len(uiw)); ui_flatten(prog[i].a, -1) } i += 1 } } function has_ui() -> bool { var i = 0 while i < len(prog) { if prog[i].kind == N_UI and prog[i].ival == 1 { return true }; i += 1 } return false } # index of a UI_: a ui block's root, or a widget's id= function ui_index_of(nm: pointer) -> int { if uiw == null { ui_flatten_all() } # a UI_ id may be read before @ui_build is emitted (a function, a listener) let s = nm[3..len(nm)] # strip "UI_" let u = 0; var bi = 0 var i = 0 while i < len(prog) { if prog[i].kind == N_UI and prog[i].ival == 1 { if (prog[i].s == s) { return ui_roots[bi] }; bi += 1 } i += 1 } i = 0 while i < len(uiw) { let idp = ui_prop(uiw[i], "id") if (idp != null) { if idp.kind == E_ID and (idp.s == s) { return i } } i += 1 } return 0 } function is_ui_ident(nm: pointer) -> bool { return len(nm) > 3 and nm[0] == 'U' and nm[1] == 'I' and nm[2] == '_' # "UI_" } function ll_ui_set(idx: int, key: int, val: pointer) -> void { emit(" call void " + fn_sym("rt_ui_set") + "(i32 "); emit(itoa(idx)); emit(", i32 "); emit(itoa(key)); emit(", i32 "); emit(val); emit(")\n") } function ui_prop_key(k: pointer) -> int { if (k == "w") { return 2 }; if (k == "h") { return 3 }; if (k == "x") { return 4 }; if (k == "y") { return 5 } if (k == "pad") { return 7 }; if (k == "gap") { return 8 }; if (k == "bg") { return 9 }; if (k == "fg") { return 10 } if (k == "border") { return 11 }; if (k == "grow") { return 13 }; if (k == "font") { return 14 }; if (k == "size") { return 15 } if (k == "inset") { return 16 }; if (k == "focus") { return 17 } return -1 } function emit_ui_build() -> void { ui_flatten_all() ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0 let fbody = buf_new() falloc = buf_new() let saved = code code = fbody emit(" call void " + fn_sym("rt_ui_reset") + "(i32 "); emit(itoa(len(uiw))); emit(")\n") var i = 0 while i < len(uiw) { let w = uiw[i] let t = ui_wtype(w) ll_ui_set(i, 0, itoa(t)) ll_ui_set(i, 1, itoa(uiw_parent[i])) if t == 4 { ll_ui_set(i, 17, "1") } let props = ui_props_with_inherited(i) var p = 0 while p < len(props) { let pr = props[p] let k = pr.s let v = pr.a if (k == "id") or (k == "goto") { p += 1; continue } if (k == "text") { let sv = emit_expr(v) emit(" call void " + fn_sym("rt_ui_static_text") + "(i32 "); emit(itoa(i)); emit(", ptr "); emit(sv.code); emit(")\n") } else { if (k == "skin") or (k == "image") { let sv = emit_expr(v) let r = emit_bind(`call i32 {fn_sym("rt_image_load")}(ptr {sv.code})`) if (k == "skin") { ll_ui_set(i, 20, r) } else { ll_ui_set(i, 19, r) } } else { if (k == "align") { if v.kind == E_ID { var a = 0 if (v.s == "center") { a = 1 }; if (v.s == "end") { a = 2 } ll_ui_set(i, 12, itoa(a)) } else { let vv = emit_expr(v); ll_ui_set(i, 12, vv.code) } } else { let key = ui_prop_key(k) if key >= 0 { let vv = emit_expr(v) ll_ui_set(i, key, vv.code) if key == 4 or key == 5 { ll_ui_set(i, 6, "1") } } } } } p += 1 } i += 1 } code = saved emit("define void @ui_build() {\nentry:\n") emit(buf_str(falloc)) emit(buf_str(fbody)) emit(" ret void\n}\n\n") }