--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/detachable_pages.ysl2 Thu Sep 02 21:36:29 2021 +0200
@@ -0,0 +1,225 @@
+// detachable_pages.ysl2
+//
+// compute what elements are required by pages
+// and decide where to cut when removing/attaching
+// pages elements on page switch
+
+const "hmi_pages_descs", "$parsed_widgets/widget[@type = 'Page']";
+const "hmi_pages", "$hmi_elements[@id = $hmi_pages_descs/@id]";
+
+const "default_page" choose {
+ when "count($hmi_pages) > 1" {
+ choose {
+ when "$hmi_pages_descs/arg[1]/@value = 'Home'" > Home
+ otherwise {
+ error > No Home page defined!
+ }
+ }
+ }
+ when "count($hmi_pages) = 0" {
+ error > No page defined!
+ }
+ otherwise > «func:widget($hmi_pages/@id)/arg[1]/@value»
+}
+
+emit "preamble:default-page" {
+ |
+ | var default_page = "«$default_page»";
+}
+
+const "keypads_descs", "$parsed_widgets/widget[@type = 'Keypad']";
+const "keypads", "$hmi_elements[@id = $keypads_descs/@id]";
+
+// returns all directly or indirectly refered elements
+def "func:refered_elements" {
+ param "elems";
+ const "descend", "$elems/descendant-or-self::svg:*";
+ const "clones", "$descend[self::svg:use]";
+ const "originals", "//svg:*[concat('#',@id) = $clones/@xlink:href]";
+ choose {
+ when "$originals"
+ result "$descend | func:refered_elements($originals)";
+ otherwise
+ result "$descend";
+ }
+}
+
+// variable "overlapping_geometry" was added for optimization.
+// It avoids calling func:overlapping_geometry 3 times for each page
+// (apparently libxml doesn't cache exslt function results)
+// in order to optimize further, func:overlapping_geometry
+// should be implemented in python or even C,
+// as this is still the main bottleneck here
+const "_overlapping_geometry" {
+ foreach "$hmi_pages | $keypads" {
+ const "k", "concat('overlapping:', @id)";
+ value "ns:ProgressStart($k, concat('collecting membership of ', @inkscape:label))";
+ elt {
+ attrib "id" > «@id»
+ copy "func:overlapping_geometry(.)";
+ }
+ value "ns:ProgressEnd($k)";
+ }
+}
+
+const "overlapping_geometry", "exsl:node-set($_overlapping_geometry)";
+
+def "func:all_related_elements" {
+ param "page";
+ const "page_overlapping_geometry", "$overlapping_geometry/elt[@id = $page/@id]/*";
+ const "page_overlapping_elements", "//svg:*[@id = $page_overlapping_geometry/@Id]";
+ const "page_sub_elements", "func:refered_elements($page | $page_overlapping_elements)";
+ result "$page_sub_elements";
+}
+
+
+def "func:required_elements" {
+ param "pages";
+ choose{
+ when "$pages"{
+ result """func:all_related_elements($pages[1])
+ | func:required_elements($pages[position()!=1])""";
+ }otherwise{
+ result "/..";
+ }
+ }
+}
+
+const "required_page_elements",
+ "func:required_elements($hmi_pages | $keypads)/ancestor-or-self::svg:*";
+
+const "hmi_lists_descs", "$parsed_widgets/widget[@type = 'List']";
+const "hmi_lists", "$hmi_elements[@id = $hmi_lists_descs/@id]";
+
+const "required_list_elements", "func:refered_elements($hmi_lists[@id = $required_page_elements/@id])";
+
+const "required_elements", "$defs | $required_list_elements | $required_page_elements";
+
+const "discardable_elements", "//svg:*[not(@id = $required_elements/@id)]";
+
+def "func:sumarized_elements" {
+ param "elements";
+ const "short_list", "$elements[not(ancestor::*/@id = $elements/@id)]";
+ const "filled_groups", """$short_list/parent::*[
+ not(child::*[
+ not(@id = $discardable_elements/@id) and
+ not(@id = $short_list/@id)
+ ])]""";
+ const "groups_to_add", "$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]";
+ result "$groups_to_add | $short_list[not(ancestor::*/@id = $filled_groups/@id)]";
+}
+
+def "func:detachable_elements" {
+ param "pages";
+ choose{
+ when "$pages"{
+ result """func:sumarized_elements(func:all_related_elements($pages[1]))
+ | func:detachable_elements($pages[position()!=1])""";
+ }otherwise{
+ result "/..";
+ }
+ }
+}
+
+// Avoid nested detachables
+const "_detachable_elements", "func:detachable_elements($hmi_pages | $keypads)";
+const "detachable_elements", "$_detachable_elements[not(ancestor::*/@id = $_detachable_elements/@id)]";
+
+emit "declarations:detachable-elements" {
+ |
+ | var detachable_elements = {
+ foreach "$detachable_elements"{
+ | "«@id»":[id("«@id»"), id("«../@id»")]`if "position()!=last()" > ,`
+ }
+ | }
+}
+
+const "forEach_widgets_ids", "$parsed_widgets/widget[@type = 'ForEach']/@id";
+const "forEach_widgets", "$hmi_widgets[@id = $forEach_widgets_ids]";
+const "in_forEach_widget_ids", "func:refered_elements($forEach_widgets)[not(@id = $forEach_widgets_ids)]/@id";
+
+template "svg:*", mode="page_desc" {
+ if "ancestor::*[@id = $hmi_pages/@id]" error > HMI:Page «@id» is nested in another HMI:Page
+
+
+ const "desc", "func:widget(@id)";
+ const "pagename", "$desc/arg[1]/@value";
+ const "msg", "concat('generating page description ', $pagename)";
+ value "ns:ProgressStart($pagename, $msg)";
+ const "page", ".";
+ const "p", "$geometry[@Id = $page/@id]";
+
+ const "page_all_elements", "func:all_related_elements($page)";
+
+ const "all_page_widgets","$hmi_widgets[@id = $page_all_elements/@id and @id != $page/@id]";
+ const "page_managed_widgets","$all_page_widgets[not(@id=$in_forEach_widget_ids)]";
+ const "page_relative_widgets",
+ "$page_managed_widgets[func:is_descendant_path(func:widget(@id)/path/@value, $desc/path/@value)]";
+
+ // Take closest ancestor in detachable_elements
+ // since nested detachable elements are filtered out
+ const "sumarized_page",
+ """func:sumarized_elements($page_all_elements)""";
+
+ const "required_detachables",
+ """$sumarized_page/
+ ancestor-or-self::*[@id = $detachable_elements/@id]""";
+
+ | "«$pagename»": {
+ //| widget: hmi_widgets["«@id»"],
+ | bbox: [«$p/@x», «$p/@y», «$p/@w», «$p/@h»],
+ if "$desc/path/@value" {
+ if "count($desc/path/@index)=0"
+ warning > Page id="«$page/@id»" : No match for path "«$desc/path/@value»" in HMI tree
+ | page_index: «$desc/path/@index»,
+ }
+ | widgets: [
+ foreach "$page_managed_widgets" {
+ const "widget_paths_relativeness"
+ foreach "func:widget(@id)/path" {
+ value "func:is_descendant_path(@value, $desc/path/@value)";
+ if "position()!=last()" > ,
+ }
+ | [hmi_widgets["«@id»"], [«$widget_paths_relativeness»]]`if "position()!=last()" > ,`
+ }
+ | ],
+ | jumps: [
+ foreach "$parsed_widgets/widget[@id = $all_page_widgets/@id and @type='Jump']" {
+ | hmi_widgets["«@id»"]`if "position()!=last()" > ,`
+ }
+ | ],
+ | required_detachables: {
+ foreach "$required_detachables" {
+ | "«@id»": detachable_elements["«@id»"]`if "position()!=last()" > ,`
+ }
+ | }
+ apply "$parsed_widgets/widget[@id = $all_page_widgets/@id]", mode="widget_page"{
+ with "page_desc", "$desc";
+ }
+ | }`if "position()!=last()" > ,`
+ value "ns:ProgressEnd($pagename)";
+}
+
+emit "definitions:page-desc" {
+ |
+ | var page_desc = {
+ apply "$hmi_pages", mode="page_desc";
+ | }
+}
+
+template "*", mode="widget_page";
+
+
+emit "debug:detachable-pages" {
+ |
+ | DETACHABLES:
+ foreach "$detachable_elements"{
+ | «@id»
+ }
+ | In Foreach:
+ foreach "$in_forEach_widget_ids"{
+ | «.»
+ }
+ | Overlapping
+ apply "$overlapping_geometry", mode="testtree";
+}