ludic/packages/ludic.ui/class.ludic

94 lines
4.2 KiB
Text

# class.ludic - components declared in Ludic (`component Name { ... }` beside Name.xml and
# Name.lss): the compiler registers each at start with its template, its styles and its functions,
# and a tag of its name anywhere is an instance of it
export property UiClass {
name: string = ""
xml_path: string = "" # where the template is, for a developer's build to read again
xml: string = ""
lss_path: string = ""
lss: string = ""
make: fn() -> pointer = null
make_of: fn(UiClass) -> pointer = null # instead of make: one constructor a host's classes share, handed the class
renew: fn(pointer) -> void = null # a record `make` made, put back as new: an instance mounted again
props: fn(pointer, Val) -> void = null
model: fn(pointer, Val) -> Val = null # fills the object it is given (the instance keeps one)
call: fn(pointer, string, Val, Val) -> Val = null # the last: where to write the answer, or null
root: UiTpl = null # the template, once read
sheet: UiSheet = null
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
}
export function ui_register(ui_st: mut UiState, k: UiClass) -> int {
push(ui_st.uc_list, k)
return len(ui_st.uc_list)
}
# the class of that name, for a host that changes a class it registered (null: none)
export function ui_class(ui_st: UiState, name: string) -> UiClass { return uc_find(ui_st, name) }
function uc_find(ui_st: UiState, name: string) -> UiClass {
if ui_st.uc_list == null { return null }
for i in 0 .. len(ui_st.uc_list) {
if ui_st.uc_list[i].name == name { return ui_st.uc_list[i] }
}
return null
}
# the class's template and styles, read the first time it is shown
@alloc_ok("once per component class: its template and styles are read the first time it is shown")
function uc_load(ui_st: mut UiState, k: UiClass) -> void {
if k.root != null { return }
let f = lib_blank(k.xml_path)
f.src = k.xml
if k.lss != "" {
if k.lss_path != "" { f.path = k.lss_path } # its rules and errors say the stylesheet they are in
lss_text(ui_st, f, k.lss)
f.path = k.xml_path
}
f.sheet = sheet_of(f)
tp_lines(ui_st, k.xml)
let doc = Xml.parse(k.xml)
let root = tpl_read(ui_st, doc, k.xml_path)
Xml.free(doc) # the template keeps the tree's strings, never its nodes
k.sheet = f.sheet
lib_link(ui_st, f, root)
k.root = root
}
# a developer's build reads a class's files again when they change on disk: the first look is what
# they are, and a later one that differs is taken (an @import in the styles is read from disk too)
@alloc_ok("developer builds only: a changed file read again")
function uc_reload(ui_st: mut UiState) -> bool {
if ui_st.uc_list == null { return false }
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 = ""
let has_l = k.lss_path != "" and Fs.exists(k.lss_path)
if has_l { l = lib_text(ui_st, k.lss_path) }
if k.seen_xml != "" and x == k.seen_xml and l == k.seen_lss {
rl_let_go(x) # read only to compare, and the same: nothing keeps it (a dev build
if has_l { rl_let_go(l) } # asks every second, and each read was kept for good)
continue
}
if k.seen_xml != "" {
k.xml = x
k.lss = l
k.root = null
changed = true
}
k.seen_xml = x
k.seen_lss = l
}
}
return changed
}
# every class reads its template and styles again, as when a stylesheet they import has changed
function uc_invalidate(ui_st: mut UiState) -> void {
if ui_st.uc_list == null { return }
for i in 0 .. len(ui_st.uc_list) { ui_st.uc_list[i].root = null }
}