ludic.ui: ui_override(path, text), ui_override_clear(path | "") and ui_overridden(path) - a template's or a stylesheet's text given from outside, its component shown again with its state kept and its own text brought back when let go; lib_text answers a copy of the override, and the dev poll leaves an overridden component alone; examples/library/ui_override
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9f07fc8a62
commit
aad3f8ca27
8 changed files with 144 additions and 0 deletions
|
|
@ -34,6 +34,8 @@ function ui_default_backend() -> UiBackend {
|
|||
return UiRenderers[0].make()
|
||||
}
|
||||
export state UiState {
|
||||
@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(256) ph_keys: []string = new []string # held.ludic: elements a preview holds a pseudo-class on for ...
|
||||
@max(256) ph_bits: []int = new []int # ... and which (hover 1, active 2, focus 4, focus-visible 8, checked 16, disabled 32)
|
||||
@max(512) pl_pools: []UiPool = new []UiPool # pool.ludic: each screen's nodes and envs, used again
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ export property UiClass {
|
|||
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
|
||||
ov_xml: string = null # override.ludic: its own template and styles, kept while a studio's text stands in
|
||||
ov_lss: string = null
|
||||
roots: []UiTpl = null # build_class.ludic: [root], for bd_kids
|
||||
none: Val = null # and the empty model it is shown with
|
||||
}
|
||||
|
|
@ -62,6 +64,7 @@ function uc_reload(ui_st: mut UiState) -> bool {
|
|||
var changed = false
|
||||
for i in 0 .. len(ui_st.uc_list) {
|
||||
let k = ui_st.uc_list[i]
|
||||
if ov_find(ui_st, k.xml_path) >= 0 or ov_find(ui_st, k.lss_path) >= 0 { continue } # a studio's text stands
|
||||
if k.xml_path != "" and Fs.exists(k.xml_path) {
|
||||
let x = lib_text(ui_st, k.xml_path)
|
||||
var l = ""
|
||||
|
|
|
|||
|
|
@ -110,3 +110,4 @@ import "draw_gradient.ludic"
|
|||
import "dump.ludic"
|
||||
import "inspect.ludic"
|
||||
import "held.ludic"
|
||||
import "override.ludic"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ function lib_blank(path: string) -> UiFile {
|
|||
return f
|
||||
}
|
||||
function lib_text(ui_st: UiState, path: string) -> string {
|
||||
let o = ov_find(ui_st, path)
|
||||
if o >= 0 { return `{ui_st.ov_text[o]}` } # a copy: a caller may give back what it read (rl_let_go)
|
||||
var src = ""
|
||||
if ui_st.ui_be != null and ui_st.ui_be.read != null { src = ui_st.ui_be.read(path) } else { src = Fs.read_text(path) }
|
||||
if src == null { return "" }
|
||||
|
|
|
|||
91
packages/ludic.ui/override.ludic
Normal file
91
packages/ludic.ui/override.ludic
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# override.ludic - a template's or a stylesheet's text given from outside, in place of its file: a
|
||||
# studio editing a running game's interface (devlink's ui_override). Every read of that path answers
|
||||
# the given text, a component whose file it is swaps its own and is shown again with its state kept,
|
||||
# and letting go puts the file back. A game overrides nothing; a read asks an empty list first.
|
||||
|
||||
# from now on `path` reads as `text`; its component (or every importer) is reloaded, state kept
|
||||
@alloc_ok("a studio's edit: the text kept until it is let go, never a frame")
|
||||
export function ui_override(ui_st: mut UiState, path: string, text: string) -> void {
|
||||
let t = keep(text)
|
||||
var found = false
|
||||
for i in 0 .. len(ui_st.ov_path) {
|
||||
if ui_st.ov_path[i] == path {
|
||||
ui_st.ov_text[i] = t
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if not found {
|
||||
push(ui_st.ov_path, intern(path))
|
||||
push(ui_st.ov_text, t)
|
||||
}
|
||||
ov_classes(ui_st, path, t)
|
||||
ui_file_forget(ui_st, path) # a file an import read is read again; every class re-shown
|
||||
}
|
||||
# let go of `path`'s override ("" every override): the file reads from disk again, a component's
|
||||
# own text comes back
|
||||
@alloc_ok("a studio's edit, never a frame")
|
||||
export function ui_override_clear(ui_st: mut UiState, path: string) -> void {
|
||||
var keep_p = new []string
|
||||
var keep_t = new []string
|
||||
for i in 0 .. len(ui_st.ov_path) {
|
||||
let p = ui_st.ov_path[i]
|
||||
if path == "" or p == path {
|
||||
ov_restore(ui_st, p)
|
||||
ui_file_forget(ui_st, p)
|
||||
} else {
|
||||
push(keep_p, p)
|
||||
push(keep_t, ui_st.ov_text[i])
|
||||
}
|
||||
}
|
||||
List.clear(ui_st.ov_path)
|
||||
List.clear(ui_st.ov_text)
|
||||
for i in 0 .. len(keep_p) {
|
||||
push(ui_st.ov_path, keep_p[i])
|
||||
push(ui_st.ov_text, keep_t[i])
|
||||
}
|
||||
}
|
||||
# is `path` overridden, and its text (null when it is not)
|
||||
export function ui_overridden(ui_st: UiState, path: string) -> bool { return ov_find(ui_st, path) >= 0 }
|
||||
function ov_find(ui_st: UiState, path: string) -> int {
|
||||
for i in 0 .. len(ui_st.ov_path) {
|
||||
if ui_st.ov_path[i] == path { return i }
|
||||
}
|
||||
return -1
|
||||
}
|
||||
# a component whose template or stylesheet is `path` takes `text`, its own kept to come back to
|
||||
function ov_classes(ui_st: mut UiState, path: string, text: string) -> void {
|
||||
if ui_st.uc_list == null { return }
|
||||
for i in 0 .. len(ui_st.uc_list) {
|
||||
let k = ui_st.uc_list[i]
|
||||
if k.xml_path == path or k.lss_path == path {
|
||||
if k.ov_xml == null {
|
||||
k.ov_xml = k.xml
|
||||
k.ov_lss = k.lss
|
||||
}
|
||||
if k.xml_path == path { k.xml = text } else { k.lss = text }
|
||||
k.root = null
|
||||
}
|
||||
}
|
||||
}
|
||||
function ov_restore(ui_st: mut UiState, path: string) -> void {
|
||||
if ui_st.uc_list == null { return }
|
||||
for i in 0 .. len(ui_st.uc_list) {
|
||||
let k = ui_st.uc_list[i]
|
||||
if k.ov_xml != null and (k.xml_path == path or k.lss_path == path) {
|
||||
if k.xml_path == path { k.xml = k.ov_xml } else { k.lss = k.ov_lss }
|
||||
if not ov_class_held(ui_st, k, path) {
|
||||
k.ov_xml = null
|
||||
k.ov_lss = null
|
||||
}
|
||||
k.root = null
|
||||
}
|
||||
}
|
||||
}
|
||||
# does the class's other file still have an override (so its own texts are still needed later)
|
||||
function ov_class_held(ui_st: UiState, k: UiClass, going: string) -> bool {
|
||||
for i in 0 .. len(ui_st.ov_path) {
|
||||
let p = ui_st.ov_path[i]
|
||||
if p != going and (p == k.xml_path or p == k.lss_path) { return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue