ludic.ui: t(key, holes...) in a template - the key marked (byte 1, a marked key kept as it is) and each hole's value after a byte 31, the text the translator decodes at draw time; joined through the text memo, so nothing is made while it reads the same (phase 26)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
82c652ca31
commit
b5b3edc6ec
8 changed files with 72 additions and 1 deletions
6
changes/ui-t-key.md
Normal file
6
changes/ui-t-key.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
bump: minor
|
||||||
|
type: feature
|
||||||
|
**`t()` in a template: a key and its holes for the translator.** `{t('pause.resume')}` and `{t('hud.day', day)}` make
|
||||||
|
the text the game's translator decodes at draw time: the key marked (byte 1; a key already marked, such as
|
||||||
|
a plural's byte 2, keeps its mark), then each hole's value after a byte 31. The text goes through the memo,
|
||||||
|
so a key and holes that read the same as last frame make nothing. Phase 26 (i18n keys).
|
||||||
26
examples/library/ui_tkey.ludic
Normal file
26
examples/library/ui_tkey.ludic
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# ui_tkey.ludic - t() in a template: the key marked (byte 1) with its holes after it, each after a byte
|
||||||
|
# 31, as the text the translator gets at draw time; a key and holes that read the same twice are one
|
||||||
|
# text. Printed with the bytes made visible: `#pause.resume #hud.day|12 #hud.at|lake|3 same`.
|
||||||
|
import "ludic.ui"
|
||||||
|
program UiTkey {
|
||||||
|
numbers float
|
||||||
|
const BODY: string = "<screen name='k'><p>{t('pause.resume')}</p><p>{t('hud.day', 12)}</p><p>{t('hud.at', 'lake', 3)}</p></screen>"
|
||||||
|
function shown(s: string) -> string {
|
||||||
|
var out = ""
|
||||||
|
for i in 0 .. len(s) {
|
||||||
|
if s[i] == 1 { out = out + "#" } else if s[i] == 31 { out = out + "|" } else { out = out + s[i .. i + 1] }
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
entry (ui_st: mut UiState) {
|
||||||
|
ui_backend(ui_st, new UiBackend)
|
||||||
|
ui_load_text(ui_st, "<ui>" + BODY + "</ui>", "k.xml")
|
||||||
|
let r: UiNode = ui_nodes(ui_st, "k", null)
|
||||||
|
ui_place(ui_st, r, 0.0, 0.0, 400.0, 300.0)
|
||||||
|
let a = r.children[0].text
|
||||||
|
let r2: UiNode = ui_nodes(ui_st, "k", null)
|
||||||
|
var same = "same"
|
||||||
|
if not (r2.children[1].text == r.children[1].text) { same = "differ" }
|
||||||
|
print(`{shown(a)} {shown(r.children[1].text)} {shown(r.children[2].text)} {same}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,8 @@ function ui_default_backend() -> UiBackend {
|
||||||
return UiRenderers[0].make()
|
return UiRenderers[0].make()
|
||||||
}
|
}
|
||||||
export state UiState {
|
export state UiState {
|
||||||
|
tk_mark: string = tk_byte(1) # tkey.ludic: a key's mark, and the byte between its holes
|
||||||
|
tk_sep: string = tk_byte(31)
|
||||||
@max(64) ov_path: []string = new []string # override.ludic: paths a studio's text stands in for ...
|
@max(64) ov_path: []string = new []string # override.ludic: paths a studio's text stands in for ...
|
||||||
@max(64) ov_text: []string = new []string # ... and the texts
|
@max(64) ov_text: []string = new []string # ... and the texts
|
||||||
@max(256) ph_keys: []string = new []string # held.ludic: elements a preview holds a pseudo-class on for ...
|
@max(256) ph_keys: []string = new []string # held.ludic: elements a preview holds a pseudo-class on for ...
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ function ui_eval(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||||
if x.op == X_TEXT { return ev_text(ui_st, x, e) }
|
if x.op == X_TEXT { return ev_text(ui_st, x, e) }
|
||||||
return vl_binary(ui_st, x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
|
return vl_binary(ui_st, x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
|
||||||
}
|
}
|
||||||
# a call: len() and range() are the template's own, anything else is the view's
|
# a call: len(), range() and t() are the template's own, anything else is the view's
|
||||||
function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||||
let depth = ui_st.ev_depth
|
let depth = ui_st.ev_depth
|
||||||
@alloc_ok("once per depth of nesting, then kept")
|
@alloc_ok("once per depth of nesting, then kept")
|
||||||
|
|
@ -45,6 +45,7 @@ function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||||
# the call itself, its arguments evaluated; a callee reads them and keeps none
|
# the call itself, its arguments evaluated; a callee reads them and keeps none
|
||||||
function ev_call_with(ui_st: mut UiState, x: UiExpr, e: UiEnv, args: Val) -> Val {
|
function ev_call_with(ui_st: mut UiState, x: UiExpr, e: UiEnv, args: Val) -> Val {
|
||||||
if x.s == "len" and len(x.args) == 1 { return uv_int(ui_st, Value.count(Value.at(args, 0))) }
|
if x.s == "len" and len(x.args) == 1 { return uv_int(ui_st, Value.count(Value.at(args, 0))) }
|
||||||
|
if x.s == "t" and len(x.args) >= 1 { return ev_t(ui_st, args) } # tkey.ludic: a key, and its holes
|
||||||
if x.s == "range" and len(x.args) == 1 {
|
if x.s == "range" and len(x.args) == 1 {
|
||||||
let out = uv_list(ui_st)
|
let out = uv_list(ui_st)
|
||||||
for i in 0 .. Value.as_int(Value.at(args, 0)) { Value.add(out, uv_int(ui_st, i)) }
|
for i in 0 .. Value.as_int(Value.at(args, 0)) { Value.add(out, uv_int(ui_st, i)) }
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ function ev_text(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||||
List.clear(ps)
|
List.clear(ps)
|
||||||
for i in 0 .. len(x.args) { tx_piece(ui_st, ps, ui_eval(ui_st, x.args[i], e)) }
|
for i in 0 .. len(x.args) { tx_piece(ui_st, ps, ui_eval(ui_st, x.args[i], e)) }
|
||||||
ui_st.tx_depth = depth
|
ui_st.tx_depth = depth
|
||||||
|
return tx_memo(ui_st, ps)
|
||||||
|
}
|
||||||
|
# the text these pieces join into, from the memo when it read this way before, else joined and kept
|
||||||
|
function tx_memo(ui_st: mut UiState, ps: []string) -> Val {
|
||||||
let m = mm_of(ui_st)
|
let m = mm_of(ui_st)
|
||||||
var h = 216613626 ^ (MM_TEXT & 255)
|
var h = 216613626 ^ (MM_TEXT & 255)
|
||||||
for p in 0 .. len(ps) {
|
for p in 0 .. len(ps) {
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,4 @@ import "held.ludic"
|
||||||
import "override.ludic"
|
import "override.ludic"
|
||||||
import "override_paths.ludic"
|
import "override_paths.ludic"
|
||||||
import "mounted.ludic"
|
import "mounted.ludic"
|
||||||
|
import "tkey.ludic"
|
||||||
|
|
|
||||||
30
packages/ludic.ui/tkey.ludic
Normal file
30
packages/ludic.ui/tkey.ludic
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# tkey.ludic - t('pause.resume') and t('hud.day', day) in a template: the key as text, marked, its holes'
|
||||||
|
# values after it ("\x01hud.day\x1f12"), handed to the translator at draw time like any text (the game's
|
||||||
|
# ludic.i18n decodes it). A key already marked (a Key held in the model) keeps its mark, a plural's too.
|
||||||
|
# Joined through the memo, so a key and holes that read the same as last frame make nothing.
|
||||||
|
function ev_t(ui_st: mut UiState, args: Val) -> Val {
|
||||||
|
let depth = ui_st.tx_depth
|
||||||
|
@alloc_ok("once per depth of nesting, then kept")
|
||||||
|
while len(ui_st.tx_pieces) <= depth { push(ui_st.tx_pieces, new []string) }
|
||||||
|
let ps = ui_st.tx_pieces[depth]
|
||||||
|
ui_st.tx_depth = depth + 1
|
||||||
|
List.clear(ps)
|
||||||
|
let key = vl_text_u(ui_st, Value.at(args, 0))
|
||||||
|
if len(key) == 0 or (key[0] != 1 and key[0] != 2) { ui_kept(ps, ui_st.tk_mark) }
|
||||||
|
ui_kept(ps, key)
|
||||||
|
for i in 1 .. Value.count(args) {
|
||||||
|
ui_kept(ps, ui_st.tk_sep)
|
||||||
|
tx_piece(ui_st, ps, Value.at(args, i))
|
||||||
|
}
|
||||||
|
ui_st.tx_depth = depth
|
||||||
|
return tx_memo(ui_st, ps)
|
||||||
|
}
|
||||||
|
# one byte as a string, made once for the state's two markers
|
||||||
|
@alloc_ok("once: a marker the state keeps")
|
||||||
|
function tk_byte(b: int) -> string {
|
||||||
|
let out = bytes(2)
|
||||||
|
out[0] = b
|
||||||
|
out[1] = 0
|
||||||
|
let s: string = out
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
@ -1311,6 +1311,7 @@ function cmd_dev_test() -> int {
|
||||||
controller_case("library/ui_popover_align", "", "centred, flipped in the panel 90,40 | end below 110,70 | above b in the list 0 inside 1", "ui_popover_align.ludic (L11: anchored popovers aligned along their side, flipped and kept within a named container or their scroll box)")
|
controller_case("library/ui_popover_align", "", "centred, flipped in the panel 90,40 | end below 110,70 | above b in the list 0 inside 1", "ui_popover_align.ludic (L11: anchored popovers aligned along their side, flipped and kept within a named container or their scroll box)")
|
||||||
controller_case("library/ui_text", "", "Photograph the Be...@100 y0 | Short@160 y20 | Medium words@133 y40 | lines 5 160 one two@160 y66 three@160 y98 | tall 30 | em 20 24 | tie #00ff00", "ui_text.ludic (L11: text-fit shrinks then cuts, line-height, em against the final font size, a sheet over its imports on a tie)")
|
controller_case("library/ui_text", "", "Photograph the Be...@100 y0 | Short@160 y20 | Medium words@133 y40 | lines 5 160 one two@160 y66 three@160 y98 | tall 30 | em 20 24 | tie #00ff00", "ui_text.ludic (L11: text-fit shrinks then cuts, line-height, em against the final font size, a sheet over its imports on a tie)")
|
||||||
controller_case("library/ui_calc", "", "min 200 max 82 clamp 50 21 | p 150,60 q 255,80 r 30", "ui_calc.ludic (L11: min(), max() and clamp(), and insets as percentages and calc() of the containing block)")
|
controller_case("library/ui_calc", "", "min 200 max 82 clamp 50 21 | p 150,60 q 255,80 r 30", "ui_calc.ludic (L11: min(), max() and clamp(), and insets as percentages and calc() of the containing block)")
|
||||||
|
controller_case("library/ui_tkey", "", "#pause.resume #hud.day|12 #hud.at|lake|3 same", "ui_tkey.ludic (t() in a template: the key marked, its holes after it, for the translator)")
|
||||||
controller_case("library/ui_nine", "", "cap 0 12 18 30/ 0 8 8 16 | wide 0 12 88 100/ 0 12 28 40 | odd 10 21 21 31/ 4 8 8 13", "ui_nine.ludic (L11: a nine-slice's corners clamped to half the box each way, on whole pixels)")
|
controller_case("library/ui_nine", "", "cap 0 12 18 30/ 0 8 8 16 | wide 0 12 88 100/ 0 12 28 40 | odd 10 21 21 31/ 4 8 8 13", "ui_nine.ludic (L11: a nine-slice's corners clamped to half the box each way, on whole pixels)")
|
||||||
controller_case("library/ui_scroll_hold", "", "120 50 0 top 120 | 80 50 0 top 80 | 80 50 0 top 80 | 80 50 70 top 80 | 80 50 30 top 80", "ui_scroll_hold.ludic (L11: scroll-top holds a box at an offset, on-scroll follows the wheel, ui_scroll_set moves one once)")
|
controller_case("library/ui_scroll_hold", "", "120 50 0 top 120 | 80 50 0 top 80 | 80 50 0 top 80 | 80 50 70 top 80 | 80 50 30 top 80", "ui_scroll_hold.ludic (L11: scroll-top holds a box at an offset, on-scroll follows the wheel, ui_scroll_set moves one once)")
|
||||||
controller_case("library/ui_fill", "", "0,0 2x20 #030303 .. 98,0 2x20 #fcfcfc | 0,20 50x2 #f2000d .. 0,38 50x2 #0d00f2 .. 0,58 50x2 #0000ff of 70 | 0,85 100x50 | -50,160 200x100 clipped 1 | ratio 90 knob 60", "ui_fill.ludic (L11: linear-gradient backgrounds, object-fit contain and cover, aspect-ratio for boxes and natives)")
|
controller_case("library/ui_fill", "", "0,0 2x20 #030303 .. 98,0 2x20 #fcfcfc | 0,20 50x2 #f2000d .. 0,38 50x2 #0d00f2 .. 0,58 50x2 #0000ff of 70 | 0,85 100x50 | -50,160 200x100 clipped 1 | ratio 90 knob 60", "ui_fill.ludic (L11: linear-gradient backgrounds, object-fit contain and cover, aspect-ratio for boxes and natives)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue