svghmi/detachable_pages.ysl2
branchsvghmi
changeset 2877 682bce953795
parent 2876 d2adbc273125
child 2885 f398896b7ebf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/detachable_pages.ysl2	Tue Mar 17 11:24:07 2020 +0100
@@ -0,0 +1,150 @@
+// detachable_elements.ysl2
+//
+// compute what elements are required by pages
+// and decide where to cut when removing/attaching 
+// pages elements on page switch
+
+const "hmi_pages", "$hmi_elements[func:parselabel(@inkscape:label)/widget/@type = 'Page']";
+
+const "default_page" choose {
+    when "count($hmi_pages) > 1" {
+        const "Home_page", 
+            "$hmi_pages[func:parselabel(@inkscape:label)/widget/arg[1]/@value = 'Home']";
+        choose {
+            when "$Home_page" > Home
+            otherwise {
+                error "No Home page defined!";
+            }
+        }
+    }
+    when "count($hmi_pages) = 0" {
+        error "No page defined!";
+    }
+    otherwise > «func:parselabel($hmi_pages/@inkscape:label)/widget/arg[1]/@value»
+}
+
+// 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";
+    }
+}
+
+def "func:all_related_elements" {
+    param "page";
+    const "page_overlapping_geometry", "func:overlapping_geometry($page)";
+    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_elements",
+    """//svg:defs/descendant-or-self::svg:*
+       | func:required_elements($hmi_pages)/ancestor-or-self::svg:*""";
+
+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::svg:*[
+        not(descendant::*[
+            not(self::svg:g) and
+            not(@id = $discardable_elements/@id) and
+            not(@id = $short_list/descendant-or-self::*[not(self::svg:g)]/@id)
+        ])]""";
+    const "groups_to_add", "$filled_groups[not(ancestor::*/@id = $filled_groups/@id)]";
+    result "$groups_to_add | $short_list[not(ancestor::svg:g/@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)";
+const "detachable_elements", "$_detachable_elements[not(ancestor::*/@id = $_detachable_elements/@id)]";
+
+def "func:is_descendant_path" {
+    param "descend";
+    param "ancest";
+    result "string-length($ancest) > 0 and starts-with($descend,$ancest)";
+}
+
+template "svg:*", mode="page_desc" {
+    const "desc", "func:parselabel(@inkscape:label)/widget";
+    const "page", ".";
+    const "p", "$geometry[@Id = $page/@id]";
+
+    const "page_all_elements", "func:all_related_elements($page)";
+
+    const "all_page_widgets","$hmi_elements[@id = $page_all_elements/@id and @id != $page/@id]";
+
+    const "page_relative_widgets",
+        "$all_page_widgets[func:is_descendant_path(func:parselabel(@inkscape:label)/widget/path/@value, $desc/path/@value)]";
+
+    // Take closest ancestor in detachable_elements
+    // since nested detachable elements are filtered out
+    const "required_detachables", 
+        """func:sumarized_elements($page_all_elements)/
+           ancestor-or-self::*[@id = $detachable_elements/@id]""";
+
+    |   "«$desc/arg[1]/@value»": {
+    |     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»,
+    }
+    |     relative_widgets: [
+    foreach "$page_relative_widgets" {
+    |         hmi_widgets["«@id»"]`if "position()!=last()" > ,`
+    }
+    |     ],
+    |     absolute_widgets: [
+    foreach "$all_page_widgets[not(@id = $page_relative_widgets/@id)]" {
+    |         hmi_widgets["«@id»"]`if "position()!=last()" > ,`
+    }
+    |     ],
+    |     required_detachables: {
+    foreach "$required_detachables" {
+    |         "«@id»": detachable_elements["«@id»"]`if "position()!=last()" > ,`
+    }
+    |     }
+    |   }`if "position()!=last()" > ,`
+}
+
+function "debug_detachables" {
+    foreach "$detachable_elements"{
+        |  «@id»
+    }
+}
+!debug_output_calls.append("debug_detachables")