From the user's play: clicking an item in the pack opened nothing, and the wardrobe's picks did not respond - both are a component whose button says `emit click` (ItemCell, LookCell, ListRow), answered by its user's on-click. An on-* attribute's name is kept as a browser would have it, so on-click is on-press (tpl_event), and the emit looked for "click" and found nothing. An emitted name now goes through the same tpl_event. And a settings cycler's left arrow did the right one's job: any press let go on a select stepped it forward. ct_activate_at steps back when it is let go over the select's .ui-prev; Enter and the rest of the select still step forward. Goldens ui_emit_click (the mouse and a press both reach on-click; with the toolchain before this, neither does) and ui_select_arrows (1 -> 0 on <, then 2 on > >; before this, 1 -> 2 on <). The 33 ui examples pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.3 KiB
Text
84 lines
3.3 KiB
Text
# controls_parts.ludic - what a control shows besides its own parts: its label, with a `note` (a line
|
|
# of help under it) in .ui-labels > .ui-label + .ui-note, and a range's or a number's value as text:
|
|
# `decimals="1"` places, `format="percent"` its place between min and max, and a `unit` after it
|
|
function ct_label(ui_st: mut UiState, n: UiNode) -> void {
|
|
let label = ui_attr_text(n, "label")
|
|
let note = ui_attr_text(n, "note")
|
|
if note == "" {
|
|
if label != "" { ui_kept(n.children, ct_part(ui_st, n, "ui-label", ui_tr(ui_st, label))) }
|
|
return
|
|
}
|
|
let both = ct_part(ui_st, n, "ui-labels", "")
|
|
if label != "" { ui_kept(both.children, ct_part(ui_st, n, "ui-label", ui_tr(ui_st, label))) }
|
|
ui_kept(both.children, ct_part(ui_st, n, "ui-note", ui_tr(ui_st, note)))
|
|
ui_kept(n.children, both)
|
|
}
|
|
@alloc_ok("a memo miss: once per value a range shows (memo_more.ludic)")
|
|
function ct_value_text(n: UiNode) -> string {
|
|
let v = ui_attr(n, "value")
|
|
if Value.kind(v) == 0 { return "" }
|
|
var f = Value.as_float(v)
|
|
let fmt = ui_attr_text(n, "format")
|
|
if fmt == "percent" {
|
|
let lo = Value.as_float(ui_attr(n, "min"))
|
|
var hi = Value.as_float(ui_attr(n, "max"))
|
|
if hi <= lo { hi = lo + 1.0 }
|
|
f = (f - lo) / (hi - lo) * 100.0
|
|
}
|
|
var s = ""
|
|
if Value.kind(ui_attr(n, "decimals")) != 0 { s = ct_fixed(f, Value.as_int(ui_attr(n, "decimals"))) } else if fmt == "percent" { s = string(int(Math.round(f))) } else { s = vl_text(v) }
|
|
if fmt == "percent" { s = s + "%" }
|
|
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 }
|
|
let neg = f < 0.0
|
|
let whole = int(Math.round(Math.abs(f) * float(k)))
|
|
var s = string(whole / k)
|
|
if places > 0 {
|
|
var frac = string(whole % k)
|
|
while len(frac) < places { frac = "0" + frac }
|
|
s = s + "." + frac
|
|
}
|
|
if neg and whole != 0 { s = "-" + s }
|
|
return s
|
|
}
|
|
# Enter (or A) on a text field: on-submit with what it holds; the focus stays, and the text too
|
|
# unless it says clear-on-submit
|
|
function ct_submit(ui_st: mut UiState, n: UiNode) -> void {
|
|
ui_fire(ui_st, n, "submit", ui_attr(n, "value"))
|
|
if ui_attr_on(n, "clear-on-submit") { ui_fire(ui_st, n, "change", Value.str("")) }
|
|
}
|
|
# the text is exactly i's digits, compared without writing i out
|
|
function ct_is_int(s: string, i: int) -> bool {
|
|
if i < 0 or len(s) == 0 { return false }
|
|
var n = i
|
|
var at = len(s) - 1
|
|
while at >= 0 {
|
|
if s[at] != 48 + n % 10 { return false }
|
|
n = n / 10
|
|
at -= 1
|
|
if n == 0 { return at < 0 }
|
|
}
|
|
return false
|
|
}
|
|
|
|
# a press let go over a control: on a select's < it steps back, anywhere else on it forward (the <
|
|
# stepped forward too, so a cycler's left arrow did the right one's job)
|
|
function ct_activate_at(ui_st: mut UiState, n: UiNode, x: float, y: float) -> void {
|
|
if ct_type(n) == "select" and ct_over_part(n, "ui-prev", x, y) {
|
|
ct_step(ui_st, n, -1)
|
|
return
|
|
}
|
|
ct_activate(ui_st, n)
|
|
}
|
|
function ct_over_part(n: UiNode, cls: string, x: float, y: float) -> bool {
|
|
for i in 0 .. len(n.children) {
|
|
let c = n.children[i]
|
|
if sel_has(c.classes, cls) and x >= c.x and y >= c.y and x < c.x + c.cw and y < c.y + c.ch { return true }
|
|
}
|
|
return false
|
|
}
|