SVGHMI: preliminary implementation of a general enable bit for all widgets, using "#" at the end of widget declaration, and a "disabled" element that becomes visible instead of widget when disabled.
authorEdouard Tisserant
Fri, 19 Aug 2022 10:22:16 +0200
changeset 3593 122b1094b8e6
parent 3592 b2bdb8b433e0
child 3594 30f7eade322f
SVGHMI: preliminary implementation of a general enable bit for all widgets, using "#" at the end of widget declaration, and a "disabled" element that becomes visible instead of widget when disabled.
svghmi/parse_labels.ysl2
svghmi/widgets_common.ysl2
tests/projects/svghmi_scrollbar/svghmi_0@svghmi/svghmi.svg
--- a/svghmi/parse_labels.ysl2	Fri Aug 12 09:34:24 2022 +0200
+++ b/svghmi/parse_labels.ysl2	Fri Aug 19 10:22:16 2022 +0200
@@ -2,7 +2,7 @@
 
 
 //  Parses:
-//  "HMI:WidgetType|freq:param1:param2@path1,path1min,path1max@path2"
+//  "HMI:WidgetType|freq:param1:param2@path1,path1min,path1max@path2#"
 //
 //  Into:
 //  widget type="WidgetType" id="blah456" {
@@ -75,7 +75,18 @@
                 attrib "value" > «.»
             }
         }
-        const "paths", "substring-after($declaration,'@')";
+        const "tail", "substring-after($declaration,'@')";
+        const "taillen","string-length($tail)";
+        const "has_enable", "substring($tail,$taillen,1)='#'";
+        const "paths" choose{
+            when "$has_enable" {
+               value "substring($tail,1,$taillen - 1)";
+            }
+            otherwise value "$tail";
+        }
+        if "$has_enable" {
+            attrib "has_enable" > yes
+        }
         foreach "str:split($paths, '@')" {
             if "string-length(.) > 0" path {
                 // 1 : global match
--- a/svghmi/widgets_common.ysl2	Fri Aug 12 09:34:24 2022 +0200
+++ b/svghmi/widgets_common.ysl2	Fri Aug 19 10:22:16 2022 +0200
@@ -104,7 +104,14 @@
             > undefined
     }
 
-    |   "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$indexes»],[«$minmaxes»],{
+    const "has_enable" choose {
+        when "$widget/@has_enable = 'yes'"
+            > true
+        otherwise
+            > false
+    }
+
+    |   "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$indexes»],[«$minmaxes»],«$has_enable»,{
     apply "$widget", mode="widget_defs" with "hmi_element",".";
     |   })`if "position()!=last()" > ,`
 }
@@ -218,12 +225,14 @@
         unsubscribable = false;
         pending_animate = false;
 
-        constructor(elt_id, freq, args, indexes, minmaxes, members){
+        constructor(elt_id, freq, args, indexes, minmaxes, has_enable, members){
             this.element_id = elt_id;
             this.element = id(elt_id);
             this.args = args;
             this.indexes = indexes;
+            this.indexes_length = indexes.length;
             this.minmaxes = minmaxes;
+            this.has_enable = has_enable;
             Object.keys(members).forEach(prop => this[prop]=members[prop]);
             this.lastapply = indexes.map(() => undefined);
             this.inhibit = indexes.map(() => undefined);
@@ -267,12 +276,27 @@
                     console.log(err);
                 }
             }
+
+            if(this.has_enable){
+                this.disabled_elt = null;
+                this.enabled_elts = [];
+                this.enable_state = false;
+                this.enable_displayed_state = false;
+                for(let child of Array.from(this.element.children)){
+                    if(child.getAttribute("inkscape:label")=="disabled"){
+                        this.disabled_elt = child;
+                    }else{
+                        this.enabled_elts.push(child);
+                        this.element.removeChild(child);
+                    }
+                }
+            }
         }
 
         unsub(){
             /* remove subsribers */
             if(!this.unsubscribable)
-                for(let i = 0; i < this.indexes.length; i++) {
+                for(let i = 0; i < this.indexes_length; i++) {
                     /* flush updates pending because of inhibition */
                     let inhibition = this.inhibit[i];
                     if(inhibition != undefined){
@@ -301,7 +325,7 @@
             this.container_id = container_id ;
             /* add this's subsribers */
             if(!this.unsubscribable)
-                for(let i = 0; i < this.indexes.length; i++) {
+                for(let i = 0; i < this.indexes_length; i++) {
                     let index = this.get_variable_index(i);
                     if(index == undefined) continue;
                     subscribers(index).add(this);
@@ -402,7 +426,7 @@
 
         new_hmi_value(index, value, oldval) {
             // TODO avoid searching, store index at sub()
-            for(let i = 0; i < this.indexes.length; i++) {
+            for(let i = 0; i < this.indexes_length; i++) {
                 let refindex = this.get_variable_index(i);
                 if(refindex == undefined) continue;
 
@@ -420,20 +444,63 @@
             this.dispatch(new_val, old_val, index);
         }
 
+        enable(enabled){
+            if(this.enable_state != enabled){
+                this.enable_state = enabled;
+                this.request_animate();
+            }
+        }
+
+        animate_enable(){
+            if(this.enable_state && !this.enable_displayed_state){
+                //show widget
+                for(let child of this.enabled_elts){
+                    this.element.appendChild(child);
+                }
+
+                //hide disabled content
+                if(this.disabled_elt && this.disabled_elt.parentNode != null)
+                    this.element.removeChild(this.disabled_elt);
+
+                this.enable_displayed_state = true;
+
+            }else if(!this.enable_state && this.enable_displayed_state){
+
+                //hide widget
+                for(let child of this.enabled_elts){
+                    if(child.parentNode != null)
+                        this.element.removeChild(child);
+                }
+
+                //show disabled content
+                if(this.disabled_elt)
+                    this.element.appendChild(this.disabled_elt);
+
+                this.enable_displayed_state = false;
+            }
+        }
+
         _dispatch(value, oldval, varnum) {
             let dispatch = this.dispatch;
-            if(dispatch != undefined){
+            let has_dispatch = dispatch != undefined;
+            let is_enable_var = this.has_enable && (varnum == (this.indexes_length - 1));
+            if(has_dispatch || is_enable_var){
                 if(this.deafen[varnum] == undefined){
                     let now = Date.now();
                     let min_interval = 1000/this.frequency;
                     let lastdispatch = this.lastdispatch[varnum];
                     if(lastdispatch == undefined || now > lastdispatch + min_interval){
                         this.lastdispatch[varnum] = now;
-                        try {
+                        if(has_dispatch) try {
                             dispatch.call(this, value, oldval, varnum);
                         } catch(err) {
                             console.log(err);
                         }
+                        if(is_enable_var) try {
+                            this.enable(Boolean(value));
+                        } catch(err) {
+                            console.log(err);
+                        }
                     }
                     else {
                         let elapsed = now - lastdispatch;
@@ -448,7 +515,10 @@
         }
 
         _animate(){
-            this.animate();
+            if(this.has_enable)
+                this.animate_enable();
+            if(this.animate != undefined && (!this.has_enable || this.enable_state))
+                this.animate();
             this.pending_animate = false;
         }
 
--- a/tests/projects/svghmi_scrollbar/svghmi_0@svghmi/svghmi.svg	Fri Aug 12 09:34:24 2022 +0200
+++ b/tests/projects/svghmi_scrollbar/svghmi_0@svghmi/svghmi.svg	Fri Aug 19 10:22:16 2022 +0200
@@ -59,9 +59,9 @@
      inkscape:current-layer="hmi0"
      showgrid="false"
      units="px"
-     inkscape:zoom="0.64"
-     inkscape:cx="476.03774"
-     inkscape:cy="444.53549"
+     inkscape:zoom="0.90509668"
+     inkscape:cx="474.80696"
+     inkscape:cy="335.41469"
      inkscape:window-width="1600"
      inkscape:window-height="836"
      inkscape:window-x="0"
@@ -751,8 +751,15 @@
        id="desc150">@.range
 @.position
 @.size
+#
 
 my tailor is rich</desc>
+    <path
+       inkscape:connector-curvature="0"
+       d="m 972,603 h 75 l -37.5,58 z m 37.5,-579.999998 37.5,58 H 972 Z M 969,236 h 81 V 407 H 969 Z M 960,84.000002 h 100 V 600 H 960 Z"
+       style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8, 8;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+       id="path159"
+       inkscape:label="disabled" />
     <rect
        y="84"
        x="960"