# units.ludic - lengths in px (the renderer's scale), em (the font size here), rem (the root's), # vw and vh (the screen's), and custom properties: --name set on an element, read by var(--name) # in it and everything inside it # the px of one em for the element being styled (a font size's em is its parent's) function el_em(ui_st: UiState) -> float { if ui_st.el_cur == null { return el_px(ui_st, 16.0) } if ui_st.el_key == "size" and ui_st.el_cur.parent != null { return cs_fs(ui_st, ui_st.el_cur.parent) } return cs_fs(ui_st, ui_st.el_cur) } function el_unit(ui_st: UiState, s: string) -> float { let f = el_num(s) let n = len(s) if n > 3 and sat(s, n - 3, "rem") { return f * el_px(ui_st, 16.0) } if n > 2 and sat(s, n - 2, "em") { if ui_st.el_cur == null { return f * el_px(ui_st, 16.0) } if ui_st.el_key == "size" and ui_st.el_cur.parent != null { return f * cs_fs(ui_st, ui_st.el_cur.parent) } return f * cs_fs(ui_st, ui_st.el_cur) } if n > 2 and sat(s, n - 2, "vw") { return f * ui_st.ly_vp_w / 100.0 } if n > 2 and sat(s, n - 2, "vh") { return f * ui_st.ly_vp_h / 100.0 } return el_px(ui_st, f) } # the font size an element has so far in its cascade (its own, else its parent's) function cs_fs(ui_st: UiState, n: UiNode) -> float { if n.size > 0.0 { return n.size } if n.parent != null { return cs_fs(ui_st, n.parent) * zm_own(n) } return ui_px(ui_st, 16.0) * zm_own(n) } function cs_var_set(n: UiNode, name: string, value: string) -> void { if n.var_names == null { @alloc_ok("once per pooled node: its lists are kept and cleared") n.var_names = new []string @alloc_ok("once per pooled node: as above") n.var_vals = new []string } for i in 0 .. len(n.var_names) { if n.var_names[i] == name { n.var_vals[i] = value return } } ui_kept(n.var_names, name) ui_kept(n.var_vals, value) } function cs_var(n: UiNode, name: string) -> string { var at = n while at != null { if at.var_names != null { for i in 0 .. len(at.var_names) { if at.var_names[i] == name { return at.var_vals[i] } } } at = at.parent } return null } # a value with var(--name) or var(--name, fallback) in it, with each replaced by what it names. What # it becomes is found by its pieces - stretches of the value and each var's text - compared in place, # so a result seen before (every frame's, nearly always) is its kept Value, and nothing is built function cs_vars_in(ui_st: mut UiState, n: UiNode, v: Val) -> Val { if Value.kind(v) != 4 { return v } let s = Value.as_str(v) if not cv_pieces(ui_st, n, s) { return v } let m = mm_of(ui_st) var at = cv_hash(ui_st) & (MM_CAP - 1) while m.slots[at] != 0 { let i = m.slots[at] - 1 if m.tags[i] == MM_VARS and cv_same(ui_st, m.keys[i]) { return m.vals[i] } at = (at + 1) & (MM_CAP - 1) } let raw = cv_join(ui_st) let r = Value.str(tpl_words(raw)) let j = mm_add(m, MM_VARS, raw) if j >= 0 { m.vals[j] = r } return r } function cs_var_ref(ui_st: mut UiState, n: UiNode, inner: string) -> string { let parts = mm_split(ui_st, inner, 44) let v = cs_var(n, tw(ui_st, parts[0])) if v != null { return v } if len(parts) > 1 { return tw(ui_st, inner[len(parts[0]) + 1..len(inner)]) } @alloc_ok("only when a template is wrong: a broken screen, never a working one") ui_err(ui_st, `var({tw(ui_st, parts[0])}): no element here or above sets it`) return "" }