ludic.ui, compiler: a component's model is filled in place into an object its instance keeps (value_set_*); colours, border-images and a class screen's stub made once; object-fit into a kept list - benchmark 20 -> 10 KB a frame
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
08ac2f1125
commit
429eeb7754
15 changed files with 44939 additions and 44681 deletions
|
|
@ -21,10 +21,18 @@ function el_url(s: string) -> string {
|
|||
}
|
||||
# border-image: url(chip.png) 14 / 12px - the source's corner size, then how wide they are drawn
|
||||
function el_border_image(ui_st: mut UiState, n: UiNode, s: string) -> void {
|
||||
var close = 0
|
||||
while close < len(s) and s[close] != 41 { close += 1 }
|
||||
n.bi_src = el_url_u(ui_st, s[0..Math.min(close + 1, len(s))])
|
||||
let rest = mm_split(ui_st, s[Math.min(close + 1, len(s))..len(s)], 47)
|
||||
let m = mm_of(ui_st)
|
||||
var at = mm_find(m, MM_BIMG, s)
|
||||
if at < 0 {
|
||||
var close = 0
|
||||
while close < len(s) and s[close] != 41 { close += 1 }
|
||||
at = mm_add(m, MM_BIMG, s)
|
||||
if at < 0 { return }
|
||||
m.texts[at] = el_url(s[0..Math.min(close + 1, len(s))])
|
||||
m.lists[at] = lss_split(s[Math.min(close + 1, len(s))..len(s)], 47)
|
||||
}
|
||||
n.bi_src = m.texts[at]
|
||||
let rest = m.lists[at]
|
||||
n.bi_slice = el_num(tw(ui_st, rest[0]))
|
||||
n.bi_width = el_px(ui_st, n.bi_slice)
|
||||
if len(rest) > 1 { n.bi_width = el_unit(ui_st, tw(ui_st, rest[1])) }
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export state UiState {
|
|||
ev_depth: int = 0 # eval.ludic: a call's arguments, in lists kept one a depth
|
||||
ev_args: []Val = new []Val
|
||||
cv_texts: []string = new []string # units_pieces.ludic: a value's var() pieces, kept
|
||||
fit_out: []float = new []float # draw_content.ludic: an image's box, kept
|
||||
r3n_cut: []float = new []float # render3d_nine.ludic: a nine-slice's edges, kept
|
||||
r3n_xs: []float = new []float
|
||||
r3n_ys: []float = new []float
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ function bd_class(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui
|
|||
}
|
||||
cls.props(it.ptr, props)
|
||||
let ce = env_new(ui_st, null)
|
||||
ce.model = cls.model(it.ptr)
|
||||
if it.model == null { it.model = Value.object() }
|
||||
ce.model = cls.model(it.ptr, it.model)
|
||||
ce.inst = it
|
||||
ce.ctx = e.ctx
|
||||
ce.slot = k
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ export property UiClass {
|
|||
lss: string = ""
|
||||
make: fn() -> pointer = null
|
||||
props: fn(pointer, Val) -> void = null
|
||||
model: fn(pointer) -> Val = null
|
||||
model: fn(pointer, Val) -> Val = null # fills the object it is given (the instance keeps one)
|
||||
call: fn(pointer, string, Val) -> Val = null
|
||||
root: UiTpl = null # the template, once read
|
||||
sheet: UiSheet = null
|
||||
seen_xml: string = "" # the files as a developer's build last found them on disk
|
||||
seen_lss: string = ""
|
||||
use: UiTpl = null # screen_class.ludic: the element that shows it as a screen, made once
|
||||
none: Val = null # and the empty model it is shown with
|
||||
}
|
||||
export function ui_register(ui_st: mut UiState, k: UiClass) -> int {
|
||||
push(ui_st.uc_list, k)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,23 @@
|
|||
# color.ludic - CSS colours: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(r, g, b), rgba(r, g, b, a), the
|
||||
# basic names, transparent and none. A colour is 0xRRGGBB; its alpha is left in cl_alpha.
|
||||
const CL_NAMES: string = " black=000000 white=ffffff red=ff0000 green=008000 lime=00ff00 blue=0000ff yellow=ffff00 cyan=00ffff aqua=00ffff magenta=ff00ff fuchsia=ff00ff gray=808080 grey=808080 silver=c0c0c0 maroon=800000 olive=808000 navy=000080 purple=800080 teal=008080 orange=ffa500 "
|
||||
# a colour is worked out once per text and kept with its alpha
|
||||
function ui_color(ui_st: mut UiState, name0: string) -> int {
|
||||
let m = mm_of(ui_st)
|
||||
let i = mm_find(m, MM_COLOR, name0)
|
||||
if i >= 0 {
|
||||
ui_st.cl_alpha = m.n2[i]
|
||||
return int(m.n1[i])
|
||||
}
|
||||
let c = ui_color_new(ui_st, name0)
|
||||
let j = mm_add(m, MM_COLOR, name0)
|
||||
if j >= 0 {
|
||||
m.n1[j] = float(c)
|
||||
m.n2[j] = ui_st.cl_alpha
|
||||
}
|
||||
return c
|
||||
}
|
||||
function ui_color_new(ui_st: mut UiState, name0: string) -> int {
|
||||
let name = tw(ui_st, name0)
|
||||
ui_st.cl_alpha = 1.0
|
||||
if name == "none" or name == "" or name == "transparent" { return UI_NONE }
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ function fr_image(ui_st: mut UiState, n: UiNode) -> void {
|
|||
nat_w = ui_st.ui_be.image_w(n.src)
|
||||
nat_h = ui_st.ui_be.image_h(n.src)
|
||||
}
|
||||
let r = ui_object_fit(n, nat_w, nat_h)
|
||||
let r = ui_object_fit_into(ui_st.fit_out, n, nat_w, nat_h)
|
||||
let clipped = ui_st.ui_be.clip != null and (r[2] > n.cw - ly_hin(n) or r[3] > n.ch - ly_vin(n))
|
||||
if clipped { fr_clip_push(ui_st, n.x + n.pl + n.bl, n.y + n.pt + n.bt, n.cw - ly_hin(n), n.ch - ly_vin(n)) }
|
||||
ui_st.ui_be.image(n.src, r[0], r[1], r[2], r[3], c, a)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ property UiInst {
|
|||
ptr: pointer = null
|
||||
seen: int = 0
|
||||
props: Val = null # its props, filled again each frame into the same object
|
||||
model: Val = null # and its model, filled in place by the class's model()
|
||||
}
|
||||
function in_get(ui_st: mut UiState, key: string, k: UiClass) -> UiInst {
|
||||
for i in 0 .. len(ui_st.in_list) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ function ly_ratio(n: UiNode, fixed_w: float, avail: float) -> void {
|
|||
} else if fixed_w < 0.0 and n.w != UI_FILL and n.h >= 0.0 { n.mw = n.h * r }
|
||||
}
|
||||
# x, y, w, h: a `nat_w` x `nat_h` picture in the node's content box by its object-fit
|
||||
export function ui_object_fit(n: UiNode, nat_w: float, nat_h: float) -> []float {
|
||||
export function ui_object_fit(n: UiNode, nat_w: float, nat_h: float) -> []float { return ui_object_fit_into(new []float, n, nat_w, nat_h) }
|
||||
function ui_object_fit_into(out: []float, n: UiNode, nat_w: float, nat_h: float) -> []float {
|
||||
let bx = n.x + n.pl + n.bl
|
||||
let by = n.y + n.pt + n.bt
|
||||
let bw = n.cw - ly_hin(n)
|
||||
|
|
@ -29,7 +30,7 @@ export function ui_object_fit(n: UiNode, nat_w: float, nat_h: float) -> []float
|
|||
w = nat_w * s
|
||||
h = nat_h * s
|
||||
}
|
||||
let out = new []float
|
||||
List.clear(out)
|
||||
push(out, bx + (bw - w) / 2.0)
|
||||
push(out, by + (bh - h) / 2.0)
|
||||
push(out, w)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ export property UiMemo {
|
|||
texts: []string = null # a text worked out from the key: tw, el_url
|
||||
vals: []Val = null # a Value made from it: cs_vars_in's
|
||||
vtpls: []UiVarTpl = null
|
||||
n1: []float = null # numbers worked out from the key: a colour and its alpha
|
||||
n2: []float = null
|
||||
}
|
||||
const MM_WORDS: int = -1 # el_words: spaces, a bracket's own kept
|
||||
const MM_GRAD: int = -2
|
||||
|
|
@ -20,6 +22,8 @@ const MM_TW: int = -4 # tpl_words
|
|||
const MM_URL: int = -5 # el_url
|
||||
const MM_VARS: int = -6 # cs_vars_in: keys[i] is the text with every var() put in
|
||||
const MM_VTPL: int = -7 # a value's var()s parsed (units_pieces.ludic)
|
||||
const MM_COLOR: int = -8 # ui_color: n1 the colour, n2 its alpha
|
||||
const MM_BIMG: int = -9 # border-image: texts the picture, lists what follows its url()
|
||||
function mm_new() -> UiMemo {
|
||||
let m = new UiMemo
|
||||
m.slots = words(MM_CAP)
|
||||
|
|
@ -30,6 +34,8 @@ function mm_new() -> UiMemo {
|
|||
m.texts = new []string
|
||||
m.vals = new []Val
|
||||
m.vtpls = new []UiVarTpl
|
||||
m.n1 = new []float
|
||||
m.n2 = new []float
|
||||
return m
|
||||
}
|
||||
function mm_of(ui_st: mut UiState) -> UiMemo {
|
||||
|
|
@ -64,6 +70,8 @@ function mm_add(m: UiMemo, tag: int, s: string) -> int {
|
|||
push(m.texts, null)
|
||||
push(m.vals, null)
|
||||
push(m.vtpls, null)
|
||||
push(m.n1, 0.0)
|
||||
push(m.n2, 0.0)
|
||||
m.slots[at] = len(m.keys)
|
||||
return len(m.keys) - 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ function mm_join(ui_st: mut UiState, a: string, sep: string, b: string) -> strin
|
|||
push(m.texts, null)
|
||||
push(m.vals, null)
|
||||
push(m.vtpls, null)
|
||||
push(m.n1, 0.0)
|
||||
push(m.n2, 0.0)
|
||||
m.slots[at] = len(m.keys)
|
||||
return k
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,21 @@ function fr_class_nodes(ui_st: mut UiState, cls: UiClass) -> UiNode {
|
|||
ui_st.ui_view = null
|
||||
uc_load(ui_st, cls)
|
||||
let root = fr_root(ui_st, cls.name, cls.sheet)
|
||||
let use = new UiTpl
|
||||
use.tag = cls.name
|
||||
use.cls = cls
|
||||
use.keys = new []string
|
||||
use.vals = new []UiExpr
|
||||
use.on_keys = new []string
|
||||
use.on_acts = new []UiAct
|
||||
use.kids = new []UiTpl
|
||||
if cls.use == null {
|
||||
let use = new UiTpl
|
||||
use.tag = cls.name
|
||||
use.cls = cls
|
||||
use.keys = new []string
|
||||
use.vals = new []UiExpr
|
||||
use.on_keys = new []string
|
||||
use.on_acts = new []UiAct
|
||||
use.kids = new []UiTpl
|
||||
cls.use = use
|
||||
cls.none = Value.object()
|
||||
}
|
||||
let use = cls.use
|
||||
let e = env_new(ui_st, null)
|
||||
e.model = Value.object()
|
||||
e.model = cls.none
|
||||
root.env = e
|
||||
bd_class(ui_st, use, e, cls.name, root.children)
|
||||
in_sweep(ui_st, cls.name)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,42 @@ function value_put(obj: Val, key: pointer, item: Val) -> Val {
|
|||
# --- accessors --------------------------------------------------------------
|
||||
function value_kind(v: Val) -> int { return v.tag }
|
||||
function value_count(v: Val) -> int { return value_n(v) }
|
||||
# a scalar field of an object set in place: the Value under `key` is changed when it is already that
|
||||
# kind, and made only the first time (a component's model, filled into the same object every frame)
|
||||
function value_slot(o: Val, key: pointer, tag: int) -> Val {
|
||||
value_lists(o)
|
||||
var i = 0
|
||||
while i < len(o.keys) {
|
||||
if o.keys[i] == key {
|
||||
if o.kids[i].tag == tag { return o.kids[i] }
|
||||
let v = value_new(tag)
|
||||
o.kids[i] = v
|
||||
return v
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
let v = value_new(tag)
|
||||
push(o.keys, key)
|
||||
push(o.kids, v)
|
||||
return v
|
||||
}
|
||||
function value_set_int(o: Val, key: pointer, n: int) -> void {
|
||||
let v = value_slot(o, key, 1)
|
||||
v.num = n
|
||||
}
|
||||
function value_set_float(o: Val, key: pointer, x: float) -> void {
|
||||
let v = value_slot(o, key, 7)
|
||||
v.num = float_bits(x)
|
||||
}
|
||||
function value_set_str(o: Val, key: pointer, s: pointer) -> void {
|
||||
let v = value_slot(o, key, 4)
|
||||
v.txt = s
|
||||
}
|
||||
function value_set_bool(o: Val, key: pointer, b: bool) -> void {
|
||||
let v = value_slot(o, key, 3)
|
||||
v.num = 0
|
||||
if b { v.num = 1 }
|
||||
}
|
||||
# a list or an object emptied in place, its room kept - for one filled again every frame
|
||||
function value_clear(v: Val) -> void {
|
||||
if v.kids == null { return }
|
||||
|
|
|
|||
|
|
@ -23,24 +23,31 @@ function cm_from(c: int, ty: pointer, x: pointer) -> pointer {
|
|||
cm_err(c, `a prop or state is an int, a float, a bool, a string or a Val, not a {ty}`)
|
||||
return x
|
||||
}
|
||||
# the model is filled into the object the instance keeps (ludic.ui's): a scalar field updates its own
|
||||
# Value in place, so showing a component allocates nothing for it frame after frame
|
||||
function cm_model_src(c: int, px: pointer, rec: pointer, head: Node) -> pointer {
|
||||
var src = `function {px}model(p: pointer) -> Val {{\n let s: {rec} = p\n let o = value_object()\n`
|
||||
var src = `function {px}model(p: pointer, o: Val) -> Val {{\n let s: {rec} = p\n`
|
||||
var i = 0
|
||||
while i < len(g_cm_keep[c]) {
|
||||
let k = g_cm_keep[c][i]
|
||||
let put = cm_to(c, g_cm_keep_ty[c][i], "s." + k, head)
|
||||
src = src + ` value_put(o, "{k}", {put})\n`
|
||||
src = src + cm_set(c, g_cm_keep_ty[c][i], k, "s." + k, head)
|
||||
i += 1
|
||||
}
|
||||
i = 0
|
||||
while i < len(g_cm_fields[c]) {
|
||||
let g = g_cm_fields[c][i]
|
||||
let put = cm_to(c, g.ty, g.s + "(s)", head)
|
||||
src = src + ` value_put(o, "{g_cm_fnames[c][i]}", {put})\n`
|
||||
src = src + cm_set(c, g.ty, g_cm_fnames[c][i], g.s + "(s)", head)
|
||||
i += 1
|
||||
}
|
||||
return src + " return o\n}\n"
|
||||
}
|
||||
function cm_set(c: int, ty: pointer, k: pointer, x: pointer, head: Node) -> pointer {
|
||||
if (ty == "int") { return ` value_set_int(o, "{k}", {x})\n` }
|
||||
if (ty == "float") { return ` value_set_float(o, "{k}", {x})\n` }
|
||||
if (ty == "string") { return ` value_set_str(o, "{k}", {x})\n` }
|
||||
if (ty == "bool") { return ` value_set_bool(o, "{k}", {x})\n` }
|
||||
return ` value_put(o, "{k}", {cm_to(c, ty, x, head)})\n`
|
||||
}
|
||||
function cm_to(c: int, ty: pointer, x: pointer, head: Node) -> pointer {
|
||||
if (ty == "Val") { return x }
|
||||
let conv = vw_conv(0, ty, x, head)
|
||||
|
|
|
|||
44743
selfhost/ludicc.seed.ll
44743
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue