ludic.ui, ludic.i18n: frame_keeps and birth_leaks to 0 - :nth-child's a and b read once per argument, a border-image's miss its own function; typing, the dev dump, parsing, the pointer ring's making, Ln's counted line and the plural header declared
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
23d204b9fc
commit
f8825dc55a
16 changed files with 67 additions and 23 deletions
|
|
@ -5,6 +5,7 @@ export const PL_ABOVE_ONE: int = 1 # French, Portuguese (0 and 1 are singular)
|
|||
export const PL_ONE_FORM: int = 2 # Japanese, Chinese
|
||||
export const PL_SLAVIC: int = 3 # Russian, Ukrainian: one, few, many
|
||||
|
||||
@alloc_ok("on a language change: its plural rule read from the header")
|
||||
function plural_rule_of(h: string) -> int {
|
||||
let k = i18n_find(h, len(h), 0, "plural=", 7)
|
||||
if k < 0 { return PL_NOT_ONE }
|
||||
|
|
@ -56,6 +57,7 @@ export function Ln(i18n_st: I18nState, one: string, many: string, n: int) -> str
|
|||
return count_in(t, n)
|
||||
}
|
||||
|
||||
@alloc_ok("a counted line (Ln) made when the game asks for one, outside a frame; its caller keeps it")
|
||||
function count_in(t: string, n: int) -> string {
|
||||
let m = len(t)
|
||||
var r = ""
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ function an_now(ui_st: UiState) -> float {
|
|||
return float(ui_st.an_frame) / 60.0
|
||||
}
|
||||
# @keyframes NAME { from {...} 50% {...} to {...} }
|
||||
@alloc_ok("a stylesheet being read: once per component class (uc_load)")
|
||||
function an_read(ui_st: mut UiState, name: string, body: string, err: []string) -> void {
|
||||
let k = new UiKeyframes
|
||||
k.name = tpl_words(name)
|
||||
|
|
|
|||
|
|
@ -26,14 +26,8 @@ function el_url(s: string) -> string {
|
|||
function el_border_image(ui_st: mut UiState, n: UiNode, s: string) -> void {
|
||||
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)
|
||||
}
|
||||
if at < 0 { at = el_bimg_read(m, s) }
|
||||
if at < 0 { return }
|
||||
n.bi_src = m.texts[at]
|
||||
let rest = m.lists[at]
|
||||
n.bi_slice = el_num(tw(ui_st, rest[0]))
|
||||
|
|
@ -95,3 +89,14 @@ function el_secs(ui_st: mut UiState, s: string) -> float {
|
|||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
@alloc_ok("a memo miss: once per border-image value, bounded by MM_CAP")
|
||||
function el_bimg_read(m: UiMemo, s: string) -> int {
|
||||
var close = 0
|
||||
while close < len(s) and s[close] != 41 { close += 1 }
|
||||
let at = mm_add(m, MM_BIMG, s)
|
||||
if at < 0 { return -1 }
|
||||
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)
|
||||
return at
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ export state UiState {
|
|||
pe_evs: []UiPointer = pe_evs__new() # pointer_events.ludic: an event's record and its Value, a ring
|
||||
pe_vals: []Val = pe_vals__new()
|
||||
pe_at: int = 0
|
||||
nth_a: int = 0 # select_nth.ludic: the a and b just read
|
||||
nth_b: int = 0
|
||||
tt_src: string = null # tooltip.ludic: the title tt_text was translated from
|
||||
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
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ function ct_drag(ui_st: mut UiState, root: UiNode, key: string, x: float, y: flo
|
|||
if n != null { ct_press(ui_st, n, x, y) }
|
||||
}
|
||||
# typing into a focused text field, or the key a key field is waiting for
|
||||
@alloc_ok("on a key typed into a field, not a frame")
|
||||
function ct_typed(ui_st: mut UiState, n: UiNode) -> void {
|
||||
let t = ct_type(n)
|
||||
if t == "key" { ct_key_typed(ui_st, n) }
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ function ct_num_step(ui_st: mut UiState, n: UiNode, d: int) -> void {
|
|||
ct_num_set(ui_st, n, base + ct_num_step_of(n) * float(d))
|
||||
}
|
||||
# what was typed this frame, the characters a number can hold; Backspace takes one back
|
||||
@alloc_ok("on a key typed into a number field, not a frame")
|
||||
function ct_num_typed(ui_st: mut UiState, n: UiNode) -> void {
|
||||
if ui_st.fc_key != n.key or (ui_st.in_now.typed == "" and not ui_st.in_now.backspace) { return }
|
||||
if ui_st.ct_num_key != n.key {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ function ct_value_text(n: UiNode) -> string {
|
|||
return s + ui_attr_text(n, "unit")
|
||||
}
|
||||
# a number with `places` digits after the point, rounded
|
||||
@alloc_ok("a memo miss (ct_value_shown): once per value a range shows")
|
||||
function ct_fixed(f: float, places: int) -> string {
|
||||
var k = 1
|
||||
for i in 0 .. Math.max(places, 0) { k = k * 10 }
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export function ui_dump(n: UiNode, depth: int) -> string {
|
|||
return out
|
||||
}
|
||||
# tag#id.class.class, or the kind of a node the template did not name
|
||||
@alloc_ok("developer builds only, once, under LUDIC_UI_DUMP")
|
||||
function dp_name(n: UiNode) -> string {
|
||||
var s = n.tag
|
||||
if s == "" { s = `({n.kind})` }
|
||||
|
|
@ -21,6 +22,7 @@ function dp_name(n: UiNode) -> string {
|
|||
return s
|
||||
}
|
||||
# the computed styles that differ from an element's defaults
|
||||
@alloc_ok("developer builds only, once, under LUDIC_UI_DUMP")
|
||||
function dp_style(n: UiNode) -> string {
|
||||
var s = ""
|
||||
if n.size > 0.0 { s = s + ` font {int(n.size)}` }
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ function fb_keep(ui_st: mut UiState, screen: string, root: UiNode) -> void {
|
|||
ui_kept(ui_st.fb_roots, root)
|
||||
}
|
||||
# the box of the element with that id (or key) on any screen shown, in screen pixels; null if none
|
||||
@alloc_ok("a question the game asks now and then (an anchor, a screen's box), never per frame")
|
||||
export function ui_box(ui_st: UiState, id: string) -> UiBox {
|
||||
for i in 0 .. len(ui_st.fb_roots) {
|
||||
let n = fb_find(ui_st.fb_roots[i], id)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ function lk_in(words: string, w: string) -> bool {
|
|||
return false
|
||||
}
|
||||
# `any` finds an unexported one too, to say why it cannot be used
|
||||
@alloc_ok("a template being linked: once per component class (uc_load)")
|
||||
function lk_find(f: UiFile, tag: string, any: bool) -> UiTpl {
|
||||
var colon = -1
|
||||
for i in 0 .. len(tag) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
# pointer_ring.ludic - a pointer event's record and the Value its handler is given, made once
|
||||
# events are made once and taken in turn: more than a frame fires before its handlers have run
|
||||
const PE_RING: int = 16
|
||||
@alloc_ok("once: made with the state")
|
||||
function pe_evs__new() -> []UiPointer {
|
||||
let xs = new []UiPointer
|
||||
for i in 0 .. PE_RING { push(xs, new UiPointer) }
|
||||
return xs
|
||||
}
|
||||
@alloc_ok("once: made with the state")
|
||||
function pe_vals__new() -> []Val {
|
||||
let xs = new []Val
|
||||
for i in 0 .. PE_RING {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ function sel_pseudo_ok(ui_st: mut UiState, p: string, arg: string, n: UiNode) ->
|
|||
if p == "checked" { return ui_attr_on(n, "checked") }
|
||||
if p == "active" { return fr_active(ui_st, n) }
|
||||
if p == "capturing" { return ui_st.ct_capture != "" and n.key == ui_st.ct_capture }
|
||||
if p == "nth-child" { return sel_nth(arg, n.index + 1) }
|
||||
if p == "nth-child" { return sel_nth(ui_st, arg, n.index + 1) }
|
||||
@alloc_ok("only when a template is wrong: a broken screen, never a working one")
|
||||
ui_err(ui_st, `:{p}: there is no such pseudo-class here`)
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -1,21 +1,43 @@
|
|||
# select_nth.ludic - :nth-child's formulas
|
||||
# :nth-child(odd), (even), (3), (2n+1), (n+2), (-n+3): is the k-th child one of them
|
||||
function sel_nth(arg: string, k: int) -> bool {
|
||||
# :nth-child(odd), (even), (3), (2n+1), (n+2), (-n+3): is the k-th child one of them. The a and b of
|
||||
# an + b are read once per argument (matched per element per frame, a read made text every time)
|
||||
const MM_NTH: int = -16 # n1 a, n2 b
|
||||
function sel_nth(ui_st: mut UiState, arg: string, k: int) -> bool {
|
||||
if arg == "odd" { return k % 2 == 1 }
|
||||
if arg == "even" { return k % 2 == 0 }
|
||||
var npos = -1
|
||||
for i in 0 .. len(arg) {
|
||||
if arg[i] == 110 { npos = i }
|
||||
}
|
||||
if npos < 0 { return string(k) == arg }
|
||||
var a = 1
|
||||
let head: string = arg[0..npos]
|
||||
if head == "-" { a = -1 } else if len(head) > 0 { a = int(el_num(head)) }
|
||||
var b = 0
|
||||
var tail = tpl_words(arg[npos + 1..len(arg)])
|
||||
if len(tail) > 0 and tail[0] == 43 { tail = tpl_words(tail[1..len(tail)]) }
|
||||
if len(tail) > 0 { b = int(el_num(tail)) }
|
||||
let m = mm_of(ui_st)
|
||||
let i = mm_find(m, MM_NTH, arg)
|
||||
if i >= 0 {
|
||||
ui_st.nth_a = int(m.n1[i])
|
||||
ui_st.nth_b = int(m.n2[i])
|
||||
} else { sel_nth_read(ui_st, m, arg) }
|
||||
let a = ui_st.nth_a
|
||||
let b = ui_st.nth_b
|
||||
if a == 0 { return k == b }
|
||||
let d = k - b
|
||||
return d % a == 0 and d / a >= 0
|
||||
}
|
||||
@alloc_ok("a memo miss: once per :nth-child argument, bounded by MM_CAP")
|
||||
function sel_nth_read(ui_st: mut UiState, m: UiMemo, arg: string) -> void {
|
||||
var npos = -1
|
||||
for i in 0 .. len(arg) {
|
||||
if arg[i] == 110 { npos = i }
|
||||
}
|
||||
var a = 0
|
||||
var b = 0
|
||||
if npos < 0 { b = int(el_num(arg)) } else {
|
||||
a = 1
|
||||
let head: string = arg[0..npos]
|
||||
if head == "-" { a = -1 } else if len(head) > 0 { a = int(el_num(head)) }
|
||||
var tail = tpl_words(arg[npos + 1..len(arg)])
|
||||
if len(tail) > 0 and tail[0] == 43 { tail = tpl_words(tail[1..len(tail)]) }
|
||||
if len(tail) > 0 { b = int(el_num(tail)) }
|
||||
}
|
||||
ui_st.nth_a = a
|
||||
ui_st.nth_b = b
|
||||
let j = mm_add(m, MM_NTH, arg)
|
||||
if j >= 0 {
|
||||
m.n1[j] = float(a)
|
||||
m.n2[j] = float(b)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ function sel_attr(r: UiRd, s: UiSel) -> void {
|
|||
}
|
||||
if not rd_op(r, "]") { rd_fail(r, "']'") }
|
||||
}
|
||||
@alloc_ok("a stylesheet being read: once per component class (uc_load)")
|
||||
function sel_pseudo(r: UiRd, s: UiSel) -> void {
|
||||
r.i += 1
|
||||
let name = sel_name(r)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ function lss_file(ui_st: mut UiState, src: string, path: string) -> UiFile {
|
|||
f.sheet = sheet_of(f)
|
||||
return f
|
||||
}
|
||||
@alloc_ok("a stylesheet being read: once per component class (uc_load)")
|
||||
function lss_text(ui_st: mut UiState, f: UiFile, src: string) -> void {
|
||||
let err = new []string
|
||||
lss_block(ui_st, f, lss_strip(src), null, err)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export property UiTpl {
|
|||
raw: string = "" # a <style>'s text, which is not a template's
|
||||
where: string = "" # file:line, for an error
|
||||
}
|
||||
@alloc_ok("a template being read: once per component class (uc_load)")
|
||||
function tpl_read(ui_st: mut UiState, x: Xml, file: string) -> UiTpl {
|
||||
let t = new UiTpl
|
||||
t.tag = Xml.tag(x)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue