SVGHMI: Make Back widget activable. (#52)
authorDino Kosic <44305363+kraskrom@users.noreply.github.com>
Wed, 11 Dec 2024 09:31:35 +0100 (3 months ago)
changeset 4070 fabc5d6c5a8c
parent 4069 e4f648e0595a
child 4072 87b8dd704b86
SVGHMI: Make Back widget activable. (#52)
exemples/svghmi_jumps/svghmi_0@svghmi/svghmi.svg
svghmi/analyse_widget.xslt
svghmi/gen_index_xhtml.xslt
svghmi/widget_back.ysl2
svghmi/widget_jump.ysl2
--- a/exemples/svghmi_jumps/svghmi_0@svghmi/svghmi.svg	Wed Dec 11 09:30:07 2024 +0100
+++ b/exemples/svghmi_jumps/svghmi_0@svghmi/svghmi.svg	Wed Dec 11 09:31:35 2024 +0100
@@ -2,21 +2,21 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
+   inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
    sodipodi:docname="svghmi.svg"
    id="hmi0"
    version="1.1"
    viewBox="0 0 1280 720"
    height="720"
-   width="1280">
+   width="1280"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
   <metadata
      id="metadata8">
     <rdf:RDF>
@@ -40,24 +40,27 @@
      guidetolerance="10"
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
-     inkscape:window-width="1850"
-     inkscape:window-height="1036"
+     inkscape:window-width="1854"
+     inkscape:window-height="1011"
      id="namedview4"
      showgrid="false"
-     inkscape:zoom="0.46354778"
-     inkscape:cx="-544.27948"
-     inkscape:cy="655.56978"
+     inkscape:zoom="1.04375"
+     inkscape:cx="626.58683"
+     inkscape:cy="1233.5329"
      inkscape:window-x="0"
-     inkscape:window-y="27"
+     inkscape:window-y="0"
      inkscape:window-maximized="1"
-     inkscape:current-layer="g2496"
+     inkscape:current-layer="g845"
      showguides="true"
      inkscape:guide-bbox="true"
      borderlayer="true"
      fit-margin-top="0"
      fit-margin-left="0"
      fit-margin-right="0"
-     fit-margin-bottom="0" />
+     fit-margin-bottom="0"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1" />
   <use
      sodipodi:insensitive="true"
      x="0"
@@ -133,14 +136,25 @@
      id="g845"
      transform="translate(0,660.00004)">
     <rect
-       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77952766;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       id="rect839"
-       width="244.72002"
-       height="70.089722"
-       x="517.64001"
-       y="324.95514"
-       ry="21.355932"
-       rx="21.355932" />
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:5.07063;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect851-6-36-7"
+       width="214.92937"
+       height="74.929367"
+       x="532.53534"
+       y="320.53528"
+       ry="12.800982"
+       rx="18.525251"
+       inkscape:label="active" />
+    <rect
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.84883;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect851-6-36"
+       width="220"
+       height="80"
+       x="530"
+       y="317.99997"
+       ry="13.667253"
+       rx="18.962297"
+       inkscape:label="inactive" />
     <text
        xml:space="preserve"
        style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
--- a/svghmi/analyse_widget.xslt	Wed Dec 11 09:30:07 2024 +0100
+++ b/svghmi/analyse_widget.xslt	Wed Dec 11 09:31:35 2024 +0100
@@ -288,6 +288,12 @@
     <longdesc>
       <xsl:text>Back widget brings focus back to previous page in history when clicked.
 </xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>"active" + "inactive" labeled elements can be provided and reflect whether
+</xsl:text>
+      <xsl:text>widget is pressed or not.
+</xsl:text>
     </longdesc>
     <shortdesc>
       <xsl:text>Jump to previous page</xsl:text>
@@ -545,6 +551,15 @@
     <path name="root" accepts="HMI_NODE">
       <xsl:text> where to find HMI_NODEs whose HMI_CLASS is class_name</xsl:text>
     </path>
+    <path name="position" accepts="HMI_INT">
+      <xsl:text>position of HMI_NODE mapped to first item, among similar siblings</xsl:text>
+    </path>
+    <path name="range" accepts="HMI_INT" count="optional">
+      <xsl:text> count of HMI_NODE siblings</xsl:text>
+    </path>
+    <path name="size" accepts="HMI_INT" count="optional">
+      <xsl:text> count of visible items</xsl:text>
+    </path>
   </xsl:template>
   <xsl:template match="widget[@type='Image']" mode="widget_desc">
     <type>
@@ -627,7 +642,7 @@
 </xsl:text>
       <xsl:text>
 </xsl:text>
-      <xsl:text>If first path is pointint to HMI_NODE variable is used as new reference
+      <xsl:text>If first path is pointing to HMI_NODE variable is used as new reference
 </xsl:text>
       <xsl:text>when jumping to a relative page.
 </xsl:text>
--- a/svghmi/gen_index_xhtml.xslt	Wed Dec 11 09:30:07 2024 +0100
+++ b/svghmi/gen_index_xhtml.xslt	Wed Dec 11 09:31:35 2024 +0100
@@ -2740,6 +2740,12 @@
     <longdesc>
       <xsl:text>Back widget brings focus back to previous page in history when clicked.
 </xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>"active" + "inactive" labeled elements can be provided and reflect whether
+</xsl:text>
+      <xsl:text>widget is pressed or not.
+</xsl:text>
     </longdesc>
     <shortdesc>
       <xsl:text>Jump to previous page</xsl:text>
@@ -2750,37 +2756,96 @@
     <xsl:text>BackWidget</xsl:text>
     <xsl:text> extends Widget{
 </xsl:text>
-    <xsl:text>    on_click(evt) {
-</xsl:text>
-    <xsl:text>        if(jump_history.length &gt; 1){
-</xsl:text>
-    <xsl:text>           let page_name, index;
+    <xsl:text>    onmouseup(evt) {
+</xsl:text>
+    <xsl:text>        svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
+</xsl:text>
+    <xsl:text>        this.activity_state = false;
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>        let page_name, index;
+</xsl:text>
+    <xsl:text>        if (jump_history.length &gt; 1) {
 </xsl:text>
     <xsl:text>           do {
 </xsl:text>
     <xsl:text>               jump_history.pop(); // forget current page
 </xsl:text>
-    <xsl:text>               if(jump_history.length == 0) return;
+    <xsl:text>               if (jump_history.length == 0) return;
 </xsl:text>
     <xsl:text>               [page_name, index] = jump_history[jump_history.length-1];
 </xsl:text>
-    <xsl:text>           } while(page_name == "ScreenSaver") // never go back to ScreenSaver
-</xsl:text>
-    <xsl:text>           switch_page(page_name, index);
+    <xsl:text>           } while (page_name == "ScreenSaver") // never go back to ScreenSaver
+</xsl:text>
+    <xsl:text>           fading_page_switch(page_name, index);
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    onmousedown(){
+</xsl:text>
+    <xsl:text>        svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
+</xsl:text>
+    <xsl:text>        this.activity_state = true;
+</xsl:text>
+    <xsl:text>        this.request_animate();
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>    init() {
 </xsl:text>
-    <xsl:text>        this.element.onclick = this.on_click.bind(this);
+    <xsl:text>        this.bound_onmouseup = this.onmouseup.bind(this);
+</xsl:text>
+    <xsl:text>        this.activity_state = false;
+</xsl:text>
+    <xsl:text>        this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>}
 </xsl:text>
   </xsl:template>
+  <xsl:template match="widget[@type='Back']" mode="widget_defs">
+    <xsl:param name="hmi_element"/>
+    <xsl:variable name="disability">
+      <xsl:call-template name="defs_by_labels">
+        <xsl:with-param name="hmi_element" select="$hmi_element"/>
+        <xsl:with-param name="labels">
+          <xsl:text>/disabled</xsl:text>
+        </xsl:with-param>
+        <xsl:with-param name="mandatory" select="'no'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:value-of select="$disability"/>
+    <xsl:variable name="has_disability" select="string-length($disability)&gt;0"/>
+    <xsl:text>    activable_sub:{
+</xsl:text>
+    <xsl:variable name="activity">
+      <xsl:call-template name="defs_by_labels">
+        <xsl:with-param name="hmi_element" select="$hmi_element"/>
+        <xsl:with-param name="labels">
+          <xsl:text>/active /inactive</xsl:text>
+        </xsl:with-param>
+        <xsl:with-param name="mandatory">
+          <xsl:text>warn</xsl:text>
+        </xsl:with-param>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:value-of select="$activity"/>
+    <xsl:variable name="has_activity" select="string-length($activity)&gt;0"/>
+    <xsl:text>    },
+</xsl:text>
+    <xsl:text>    has_activity: </xsl:text>
+    <xsl:value-of select="$has_activity"/>
+    <xsl:text>,
+</xsl:text>
+  </xsl:template>
   <xsl:template match="widget[@type='Button']" mode="widget_desc">
     <type>
       <xsl:value-of select="@type"/>
@@ -4454,6 +4519,15 @@
     <path name="root" accepts="HMI_NODE">
       <xsl:text> where to find HMI_NODEs whose HMI_CLASS is class_name</xsl:text>
     </path>
+    <path name="position" accepts="HMI_INT">
+      <xsl:text>position of HMI_NODE mapped to first item, among similar siblings</xsl:text>
+    </path>
+    <path name="range" accepts="HMI_INT" count="optional">
+      <xsl:text> count of HMI_NODE siblings</xsl:text>
+    </path>
+    <path name="size" accepts="HMI_INT" count="optional">
+      <xsl:text> count of visible items</xsl:text>
+    </path>
   </xsl:template>
   <xsl:template match="widget[@type='ForEach']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
@@ -5606,7 +5680,7 @@
 </xsl:text>
       <xsl:text>
 </xsl:text>
-      <xsl:text>If first path is pointint to HMI_NODE variable is used as new reference
+      <xsl:text>If first path is pointing to HMI_NODE variable is used as new reference
 </xsl:text>
       <xsl:text>when jumping to a relative page.
 </xsl:text>
@@ -5694,7 +5768,7 @@
 </xsl:text>
     <xsl:text>        target_page_is_current_page = false;
 </xsl:text>
-    <xsl:text>        button_beeing_pressed = false;
+    <xsl:text>        button_being_pressed = false;
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -5710,9 +5784,9 @@
 </xsl:text>
     <xsl:text>                    this.indexes[0] + this.offset : undefined;
 </xsl:text>
-    <xsl:text>                this.button_beeing_pressed = false;
-</xsl:text>
-    <xsl:text>                this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed;
+    <xsl:text>                this.button_being_pressed = false;
+</xsl:text>
+    <xsl:text>                this.activity_state = this.target_page_is_current_page || this.button_being_pressed;
 </xsl:text>
     <xsl:text>                fading_page_switch(this.args[0], index);
 </xsl:text>
@@ -5730,7 +5804,7 @@
 </xsl:text>
     <xsl:text>                svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
 </xsl:text>
-    <xsl:text>                this.button_beeing_pressed = true;
+    <xsl:text>                this.button_being_pressed = true;
 </xsl:text>
     <xsl:text>                this.activity_state = true;
 </xsl:text>
@@ -5754,7 +5828,7 @@
 </xsl:text>
     <xsl:text>                this.target_page_is_current_page = ((ref_name == undefined || ref_name == page_name) &amp;&amp; index == ref_index);
 </xsl:text>
-    <xsl:text>                this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed;
+    <xsl:text>                this.activity_state = this.target_page_is_current_page || this.button_being_pressed;
 </xsl:text>
     <xsl:text>                // Since called from animate, update activity directly
 </xsl:text>
--- a/svghmi/widget_back.ysl2	Wed Dec 11 09:30:07 2024 +0100
+++ b/svghmi/widget_back.ysl2	Wed Dec 11 09:31:35 2024 +0100
@@ -4,25 +4,45 @@
     longdesc
     ||
     Back widget brings focus back to previous page in history when clicked.
+
+    "active" + "inactive" labeled elements can be provided and reflect whether
+    widget is pressed or not.
     ||
 
     shortdesc > Jump to previous page
 }
 
-widget_class("Back")
-    ||
-        on_click(evt) {
-            if(jump_history.length > 1){
-               let page_name, index;
-               do {
-                   jump_history.pop(); // forget current page
-                   if(jump_history.length == 0) return;
-                   [page_name, index] = jump_history[jump_history.length-1];
-               } while(page_name == "ScreenSaver") // never go back to ScreenSaver
-               switch_page(page_name, index);
-            }
+widget_class("Back") {
+||
+    onmouseup(evt) {
+        svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
+        this.activity_state = false;
+        this.request_animate();
+        let page_name, index;
+        if (jump_history.length > 1) {
+           do {
+               jump_history.pop(); // forget current page
+               if (jump_history.length == 0) return;
+               [page_name, index] = jump_history[jump_history.length-1];
+           } while (page_name == "ScreenSaver") // never go back to ScreenSaver
+           fading_page_switch(page_name, index);
         }
-        init() {
-            this.element.onclick = this.on_click.bind(this);
-        }
-    ||
+    }
+
+    onmousedown(){
+        svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
+        this.activity_state = true;
+        this.request_animate();
+    }
+
+    init() {
+        this.bound_onmouseup = this.onmouseup.bind(this);
+        this.activity_state = false;
+        this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
+    }
+||
+}
+
+widget_defs("Back") {
+    activable();
+}
--- a/svghmi/widget_jump.ysl2	Wed Dec 11 09:30:07 2024 +0100
+++ b/svghmi/widget_jump.ysl2	Wed Dec 11 09:31:35 2024 +0100
@@ -6,7 +6,7 @@
     Jump widget brings focus to a different page. Mandatory first argument
     gives name of the page.
 
-    If first path is pointint to HMI_NODE variable is used as new reference
+    If first path is pointing to HMI_NODE variable is used as new reference
     when jumping to a relative page.
 
     Additional arguments are unordered options:
@@ -53,7 +53,7 @@
         activable = false;
         frequency = 2;
         target_page_is_current_page = false;
-        button_beeing_pressed = false;
+        button_being_pressed = false;
 
         onmouseup(evt) {
             svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
@@ -61,8 +61,8 @@
                 const index =
                     (this.is_relative && this.indexes.length > 0) ?
                     this.indexes[0] + this.offset : undefined;
-                this.button_beeing_pressed = false;
-                this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed;
+                this.button_being_pressed = false;
+                this.activity_state = this.target_page_is_current_page || this.button_being_pressed;
                 fading_page_switch(this.args[0], index);
                 this.notify();
             }
@@ -71,7 +71,7 @@
         onmousedown(){
             if(this.enable_state) {
                 svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
-                this.button_beeing_pressed = true;
+                this.button_being_pressed = true;
                 this.activity_state = true;
                 this.request_animate();
             }
@@ -83,7 +83,7 @@
                 const ref_index = this.indexes.length > 0 ? this.indexes[0] + this.offset : undefined;
                 const ref_name = this.args[0];
                 this.target_page_is_current_page = ((ref_name == undefined || ref_name == page_name) && index == ref_index);
-                this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed;
+                this.activity_state = this.target_page_is_current_page || this.button_being_pressed;
                 // Since called from animate, update activity directly
                 if(this.enable_displayed_state && this.has_activity) {
                     this.animate_activity();