A prebuilt tool, bin/ludic-ui-preview (built by ludic-dev build, shipped beside ludic-lsp, run as `ludic ui-preview [--font DIR]`): ludic.ui with a backend that records every draw call as a line, and mock component classes the studio describes (load / model / calls). frame t runs ui_show at interface time t; text is measured on the CPU with the game's font.json metrics; locale goes through ludic.i18n; tree / box / rules inspect the frame. No game code, no GPU, no network. The wire protocol is frozen as tools/ui-preview/protocol-v1.md; smoke.txt is a transcript to run. ludic.ui gains, all additive: UiClass.make_of, UiBackend.said / emitted, UiRule.text / at (with comment line breaks kept so rule lines count true, and a class's styles read under its .lss path), and ui_root, ui_find, ui_rules_of, ui_rule_value, ui_building, ui_class, ui_class_load, ui_file_forget, ui_errors_clear; ui_nine_cuts_into exported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
3.1 KiB
Text
71 lines
3.1 KiB
Text
# inspect.ludic - what an inspector asks of the last frame shown (ludic ui-preview, a developer's
|
|
# tools): a screen's root, an element by id, the rules that style it, and whether a build is running
|
|
# the root of the screen `screen` as it was last shown, or null
|
|
export function ui_root(ui_st: UiState, screen: string) -> UiNode {
|
|
for i in 0 .. len(ui_st.fb_roots) {
|
|
if ui_st.fb_roots[i].key == screen { return ui_st.fb_roots[i] }
|
|
}
|
|
return null
|
|
}
|
|
# the element with that id (or key) on any screen shown, or null
|
|
export function ui_find(ui_st: UiState, id: string) -> UiNode {
|
|
for i in 0 .. len(ui_st.fb_roots) {
|
|
let n = fb_find(ui_st.fb_roots[i], id)
|
|
if n != null { return n }
|
|
}
|
|
return null
|
|
}
|
|
# true while a frame's tree is being built (a model read), false while an action runs (a press)
|
|
export function ui_building(ui_st: UiState) -> bool { return ui_st.uv_on }
|
|
# the rules that match the element now, in the order the cascade applies them: the default sheet's,
|
|
# then its file's (merged with each user's sheet for a component's root); its style="..." is not one
|
|
@alloc_ok("an inspector's question, never a frame")
|
|
export function ui_rules_of(ui_st: mut UiState, n: UiNode, out: []UiRule) -> void {
|
|
List.clear(out)
|
|
ir_sheet(ui_st, n, ua_sheet(ui_st), out)
|
|
if n.ups == null {
|
|
if n.sheet != null { ir_sheet(ui_st, n, n.sheet, out) }
|
|
return
|
|
}
|
|
let at = len(out)
|
|
if n.sheet != null { ir_sheet(ui_st, n, n.sheet, out) }
|
|
for i in 0 .. len(n.ups) {
|
|
if n.ups[i].sheet != null and n.ups[i].sheet != n.sheet { ir_sheet(ui_st, n, n.ups[i].sheet, out) }
|
|
}
|
|
for i in at + 1 .. len(out) { # one order by specificity, stable, as cs_merged applies them
|
|
let r = out[i]
|
|
var j = i
|
|
while j > at and out[j - 1].spec > r.spec {
|
|
out[j] = out[j - 1]
|
|
j -= 1
|
|
}
|
|
out[j] = r
|
|
}
|
|
}
|
|
@alloc_ok("an inspector's question, never a frame")
|
|
function ir_sheet(ui_st: mut UiState, n: UiNode, sh: UiSheet, out: []UiRule) -> void {
|
|
for i in 0 .. len(sh.rules) {
|
|
let r = sh.rules[i]
|
|
if md_ok(ui_st, r) and sel_match(ui_st, r.sel, n) { push(out, r) }
|
|
}
|
|
}
|
|
# a declaration's value as written, when it is plain text (else "" - an expression, read per element)
|
|
export function ui_rule_value(r: UiRule, k: int) -> string {
|
|
let x = r.vals[k]
|
|
if x.op == X_LIT { return vl_text(x.v) }
|
|
return ""
|
|
}
|
|
# a class's template and styles read now (else at its first show), so a host hears their errors at once
|
|
export function ui_class_load(ui_st: mut UiState, k: UiClass) -> void { uc_load(ui_st, k) }
|
|
# a file read again the next time anything imports it (a host changed its text): every class too
|
|
@alloc_ok("an inspector's edit, never a frame")
|
|
export function ui_file_forget(ui_st: mut UiState, path: string) -> void {
|
|
let keep = new []UiFile
|
|
for i in 0 .. len(ui_st.lib_files) {
|
|
if ui_st.lib_files[i].path != path { push(keep, ui_st.lib_files[i]) }
|
|
}
|
|
ui_st.lib_files = keep
|
|
uc_invalidate(ui_st)
|
|
}
|
|
# every error forgotten, so each is said again the next time it is made (a host that reloads a class)
|
|
export function ui_errors_clear(ui_st: mut UiState) -> void { List.clear(ui_st.ui_errs) }
|