# 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 } }