# attr.ludic - what a property sets, by its CSS name or the short one (width or w, background or # bg, font-size or size, ...). An attribute on an element is the same property, applied last. const AT_UNDRAWN: string = " font-weight font-family font-style box-shadow cursor transition animation letter-spacing text-decoration " # a stylesheet's property: one that means nothing is said function el_attr(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> void { if el_apply(ui_st, n, key, v) { return } @alloc_ok("only when a template is wrong: a broken screen, never a working one") if lk_in(AT_UNDRAWN, at_alias(key)) { ui_err(ui_st, `{key}: this renderer does not draw it`) } else { ui_err(ui_st, `{key}: there is no such property`) } } # an element's attribute: a property when it names one, otherwise only there for [selectors] function el_apply(ui_st: mut UiState, n: UiNode, key: string, v0: Val) -> bool { let k = at_alias(key) ui_st.el_cur = n ui_st.el_key = k if len(k) > 2 and sat(k, 0, "--") { cs_var_set(n, k, vl_text_u(ui_st, v0)) return true } let v = cs_vars_in(ui_st, n, v0) return el_size(ui_st, n, k, v) or el_box(ui_st, n, k, v) or el_flex(ui_st, n, k, v) or el_look(ui_st, n, k, v) or el_place_prop(ui_st, n, k, v) or el_edge(ui_st, n, k, v) or el_more(ui_st, n, k, v) } function at_alias(key: string) -> string { # the CSS name to the short one; literals, so asking allocates nothing (asked per rule per element) if key == "width" { return "w" } if key == "height" { return "h" } if key == "min-width" { return "min-w" } if key == "max-width" { return "max-w" } if key == "min-height" { return "min-h" } if key == "max-height" { return "max-h" } if key == "padding" { return "pad" } if key == "background" { return "bg" } if key == "background-color" { return "bg" } if key == "font-size" { return "size" } if key == "opacity" { return "alpha" } if key == "flex-grow" { return "grow" } if key == "flex-shrink" { return "shrink" } if key == "flex-direction" { return "dir" } if key == "flex-wrap" { return "wrap" } if key == "justify-content" { return "justify" } if key == "align-items" { return "align" } if key == "align-self" { return "self" } if key == "row-gap" { return "gap" } if key == "column-gap" { return "gap" } if key == "text-align" { return "talign" } return key } function el_size(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> bool { if key == "w" or key == "h" { el_dim(ui_st, n, key == "w", v) } else if key == "min-w" { n.min_w = el_len(ui_st, v) } else if key == "max-w" { n.max_w = el_len(ui_st, v) } else if key == "min-h" { n.min_h = el_len(ui_st, v) } else if key == "max-h" { n.max_h = el_len(ui_st, v) } else if key == "gap" { n.gap = el_len(ui_st, v) } else { return false } return true } const EL_KEYS_FLEX: string = " align dir display flex grow justify self talign wrap " function el_flex(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> bool { if not lk_in(EL_KEYS_FLEX, key) { return false } # not one of its keys: no text made for it if key == "grow" { n.grow = el_numv(ui_st, v) return true } let s = vl_text_u(ui_st, v) if key == "dir" { n.dir = UI_COL if s == "row" { n.dir = UI_ROW } } else if key == "grow" { n.grow = el_num(s) } else if key == "flex" { el_flex_short(ui_st, n, s) } else if key == "wrap" { n.wrap = s == "wrap" or (vl_truthy(v) and s != "false" and s != "nowrap") } else if key == "align" { n.align = el_place(v) } else if key == "self" { n.self = el_place(v) } else if key == "justify" { n.justify = el_place(v) } else if key == "talign" { n.talign = el_place(v) } else if key == "display" { n.hidden = s == "none" } else { return false } return true } const EL_KEYS_LOOK: string = " alpha bg border border-bottom border-color border-left border-right border-top border-width color enabled size " function el_look(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> bool { if not lk_in(EL_KEYS_LOOK, key) { return false } # not one of its keys: no text made for it if key == "alpha" { n.alpha = el_numv(ui_st, v) return true } let s = vl_text_u(ui_st, v) if key == "size" { n.size = el_len(ui_st, v) } else if key == "color" { n.fg = ui_color(ui_st, s) n.fg_a = ui_st.cl_alpha } else if key == "bg" { if gr_is(tw(ui_st, s)) { nx(n).grad = gr_parse(ui_st, s) } else if len(s) > 4 and sat(s, 0, "url(") { n.bg_img = el_url_u(ui_st, s) } else if tw(ui_st, s) == "transparent" { n.bg = 0 n.bg_a = 0.0 } else { n.bg = ui_color(ui_st, s) n.bg_a = ui_st.cl_alpha } } else if key == "alpha" { n.alpha = el_num(s) } else if key == "enabled" { n.enabled = vl_truthy(v) and s != "false" } else if key == "border-color" { n.border_c = ui_color(ui_st, s) n.border_a = ui_st.cl_alpha } else if key == "border-width" { el_sides_border(n, el_len(ui_st, v)) } else if key == "border" { el_border(ui_st, n, s) } else if key == "border-top" or key == "border-right" or key == "border-bottom" or key == "border-left" { el_border_side(ui_st, n, key, s) } else { return false } return true } # border: 1 #ffcc00, or 1px solid accent function el_border(ui_st: mut UiState, n: UiNode, s: string) -> void { let parts = mm_split(ui_st, s, 32) for i in 0 .. len(parts) { let p = parts[i] if len(p) > 0 and ((p[0] >= 48 and p[0] <= 57) or p[0] == 46) { el_sides_border(n, el_unit(ui_st, p)) } else if p == "none" { el_sides_border(n, 0.0) } else if len(p) > 0 and p != "solid" { n.border_c = ui_color(ui_st, p) n.border_a = ui_st.cl_alpha } } if n.border_c == UI_NONE { n.border_c = n.rfg } }