Button, ToggleButton and slider updated. Error to warning when building svghmi
authorusveticic
Wed, 16 Sep 2020 09:41:52 +0200
branchsvghmi
changeset 3056 827bf284feec
parent 3045 f6d428330e04
child 3057 696301e869d5
Button, ToggleButton and slider updated. Error to warning when building

Button fixed so it doesn't release until it gets feedback from plc

Toggle button changed so it makes changes instantly. There was too big delay if we waited for feedback.

Slider added new features need some changes for final version.
svghmi/widget_button.ysl2
svghmi/widget_slider.ysl2
svghmi/widget_tooglebutton.ysl2
svghmi/widgets_common.ysl2
--- a/svghmi/widget_button.ysl2	Tue Aug 18 11:42:28 2020 +0200
+++ b/svghmi/widget_button.ysl2	Wed Sep 16 09:41:52 2020 +0200
@@ -5,23 +5,40 @@
     class ButtonWidget extends Widget{
         frequency = 5;
         state = 0;
+        plc_lock = false;
         active_style = undefined;
         inactive_style = undefined;
 
+        dispatch(value) {
+            if(value){
+                this.button_release();
+            }
+        }
+
          on_mouse_down(evt) {
              if (this.active_style && this.inactive_style) {
                  this.active_elt.setAttribute("style", this.active_style);
                  this.inactive_elt.setAttribute("style", "display:none");
              }
              this.apply_hmi_value(0, 1);
+             this.plc_lock = false;
          }
 
          on_mouse_up(evt) {
-             if (this.active_style && this.inactive_style) {
-                 this.active_elt.setAttribute("style", "display:none");
-                 this.inactive_elt.setAttribute("style", this.inactive_style);
-             }
-             this.apply_hmi_value(0, 0);
+             this.button_release();
+         }
+
+         button_release(){
+            if(!this.plc_lock){
+                this.plc_lock = true;
+            }
+            else{
+                if (this.active_style && this.inactive_style) {
+                     this.active_elt.setAttribute("style", "display:none");
+                     this.inactive_elt.setAttribute("style", this.inactive_style);
+                 }
+                 this.apply_hmi_value(0, 0);
+            }
          }
 
          init() {
--- a/svghmi/widget_slider.ysl2	Tue Aug 18 11:42:28 2020 +0200
+++ b/svghmi/widget_slider.ysl2	Wed Sep 16 09:41:52 2020 +0200
@@ -5,27 +5,85 @@
     class SliderWidget extends Widget{
         frequency = 5;
         range = undefined;
+        handle_orig = undefined;
+        scroll_size = 10;
+        min_size = 0.07;
         fi = undefined;
-        svg_dist = undefined;
+        curr_value = 0;
         drag = false;
         enTimer = false;
+        handle_click = undefined;
+        last_drag = false;
 
         dispatch(value) {
+            //save current value inside widget
+            this.curr_value = value;
+
             if(this.value_elt)
                 this.value_elt.textContent = String(value);
 
-            this.update_DOM(value, this.handle_elt);
-
-        }
-
-        last_drag = false;
+            //don't update if draging and setpoint ghost doesn't exist
+            if(!this.drag || (this.setpoint_elt != undefined)){
+                this.update_DOM(value, this.handle_elt);
+            }
+        }
 
         update_DOM(value, elt){
             let [min,max,start,totallength] = this.range;
-            let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
-            let tip = this.range_elt.getPointAtLength(length);
-            elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
-
+            // check if handle is resizeable
+            if (this.scroll_size != undefined){ //size changes
+                //get parameters
+                let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min)));
+                let tip = this.range_elt.getPointAtLength(length);
+                let handle_min = totallength*this.min_size;
+
+                let step = 1;
+                //check if range is bigger than  max displayed and recalculate step
+                if ((totallength/handle_min) < (max-min+1)){
+                    step = (max-min+1)/(totallength/handle_min-1);
+                }
+
+                let kx,ky,offseY,offseX = undefined;
+                //scale on x or y axes
+                if (this.fi > 0.75){
+                    //get scale factor
+                    if(step > 1){
+                        ky = handle_min/this.handle_orig.height;
+                    }
+                    else{
+                        ky = (totallength-handle_min*(max-min))/this.handle_orig.height;
+                    }
+                    kx = 1;
+                    //get 0 offset to stay inside range
+                    offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky;
+                    offseX = 0;
+                    //get distance from value
+                    tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min;
+                }
+                else{
+                    //get scale factor
+                    if(step > 1){
+                        kx = handle_min/this.handle_orig.width;
+                    }
+                    else{
+                        kx = (totallength-handle_min*(max-min))/this.handle_orig.width;
+                    }
+                    ky = 1;
+                    //get 0 offset to stay inside range
+                    offseX = start.x - (this.handle_orig.x * kx);
+                    offseY = 0;
+                    //get distance from value
+                    tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min;
+                }
+                elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")");
+            }
+            else{ //size stays the same
+                let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
+                let tip = this.range_elt.getPointAtLength(length);
+                elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
+            }
+
+            // show or hide ghost if exists
             if(this.setpoint_elt != undefined){
                 if(this.last_drag!= this.drag){
                     if(this.drag){
@@ -39,22 +97,29 @@
         }
 
         on_release(evt) {
+            //unbind events
             window.removeEventListener("touchmove", this.on_bound_drag, true);
             window.removeEventListener("mousemove", this.on_bound_drag, true);
 
             window.removeEventListener("mouseup", this.bound_on_release, true)
             window.removeEventListener("touchend", this.bound_on_release, true);
             window.removeEventListener("touchcancel", this.bound_on_release, true);
+
+            //reset drag flag
             if(this.drag){
                 this.drag = false;
             }
+
+            // get final position
             this.update_position(evt);
-        }
-
+
+        }
 
         on_drag(evt){
+            //ignore drag event for X amount of time and if not selected
             if(this.enTimer && this.drag){
                 this.update_position(evt);
+
                 //reset timer
                 this.enTimer = false;
                 setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
@@ -63,11 +128,12 @@
 
         update_position(evt){
             var html_dist = 0;
+            let [min,max,start,totallength] = this.range;
 
             //calculate size of widget in html
             var range_borders = this.range_elt.getBoundingClientRect();
+            var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
             var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
-            var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
 
             //get range and mouse coordinates
             var mouseX = undefined;
@@ -81,59 +147,123 @@
                 mouseY = evt.pageY;
             }
 
-            //get handle distance from mouse position
-            if (minX > mouseX && minY < mouseY){
-                html_dist = 0;
-            }
-            else if (maxX < mouseX && maxY > mouseY){
-                html_dist = range_length;
+            // calculate position
+            if (this.handle_click){ //if clicked on handle
+                let moveDist = 0, resizeAdd = 0;
+                let range_percent = 1;
+
+                //set paramters for resizeable handle
+                if (this.scroll_size != undefined){
+                    // add one more object to stay inside range
+                    resizeAdd = 1;
+
+                    //chack if range is bigger than display option and
+                    // calculate percent of range with out handle
+                    if(((max/(max*this.min_size)) < (max-min+1))){
+                        range_percent = 1-this.min_size;
+                    }
+                    else{
+                        range_percent = 1-(max-max*this.min_size*(max-min))/max;
+                    }
+                }
+
+                //calculate value difference on x or y axis
+                if(this.fi > 0.7){
+                    moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi));
+                }
+                else{
+                    moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi));
+                }
+
+                this.curr_value = Math.ceil(this.handle_click[2] + moveDist);
+            }
+            else{ //if clicked on widget
+                //get handle distance from mouse position
+                if (minX > mouseX && minY < mouseY){
+                    html_dist = 0;
+                }
+                else if (maxX < mouseX && maxY > mouseY){
+                    html_dist = range_length;
+                }
+                else{
+                    if(this.fi > 0.7){
+                        html_dist = (minY - mouseY)/Math.sin(this.fi);
+                    }
+                    else{
+                        html_dist = (mouseX - minX)/Math.cos(this.fi);
+                    }
+                }
+                //calculate distance
+                this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]);
+            }
+
+            //check if in range
+            if (this.curr_value > max){
+                this.curr_value = max;
+            }
+            else if (this.curr_value < min){
+                this.curr_value = min;
+            }
+
+            this.apply_hmi_value(0, this.curr_value);
+
+            //redraw handle
+            this.request_animate();
+
+        }
+
+        animate(){
+            // redraw handle on screen refresh
+            // check if setpoint(ghost) handle exsist otherwise update main handle
+            if(this.setpoint_elt != undefined){
+                this.update_DOM(this.curr_value, this.setpoint_elt);
             }
             else{
-                // calculate distace
-                if(this.fi > 0.7){
-                    html_dist = (minY - mouseY)/Math.sin(this.fi);
-                }
-                else{
-                    html_dist = (mouseX - minX)/Math.cos(this.fi);
-                }
-
-                //check if in range
-                if (html_dist > range_length){
-                    html_dist = range_length;
-                }
-                else if (html_dist < 0){
-                    html_dist = 0;
-                }
-
-            }
-
-            this.svg_dist=Math.ceil((html_dist/range_length)*this.range[1]);
-
-            this.apply_hmi_value(0, this.svg_dist);
-
-            // update ghost cursor
-            if(this.setpoint_elt != undefined){
-                this.request_animate();
-            }
-        }
-
-        animate(){
-            this.update_DOM(this.svg_dist, this.setpoint_elt);
+                this.update_DOM(this.curr_value, this.handle_elt);
+            }
         }
 
         on_select(evt){
+            //enable drag flag and timer
             this.drag = true;
             this.enTimer = true;
+
+            //bind events
             window.addEventListener("touchmove", this.on_bound_drag, true);
             window.addEventListener("mousemove", this.on_bound_drag, true);
 
             window.addEventListener("mouseup", this.bound_on_release, true)
             window.addEventListener("touchend", this.bound_on_release, true);
             window.addEventListener("touchcancel", this.bound_on_release, true);
-            this.update_position(evt);
+
+            // check if handle was pressed
+            if (evt.currentTarget == this.handle_elt){
+                //get mouse position on the handle
+                let mouseX = undefined;
+                let mouseY = undefined;
+                if (evt.type.startsWith("touch")){
+                    mouseX = Math.ceil(evt.touches[0].clientX);
+                    mouseY = Math.ceil(evt.touches[0].clientY);
+                }
+                else{
+                    mouseX = evt.pageX;
+                    mouseY = evt.pageY;
+                }
+                //save coordinates and orig value
+                this.handle_click = [mouseX,mouseY,this.curr_value];
+            }
+            else{
+                // get new handle position and reset if handle was not pressed
+                this.handle_click = undefined;
+                this.update_position(evt);
+            }
+
+            //prevent next events
+            evt.stopPropagation();
         }
 
         init() {
+            //set min max value if not defined
             let min = this.min_elt ?
                         Number(this.min_elt.textContent) :
                         this.args.length >= 1 ? this.args[0] : 0;
@@ -141,15 +271,20 @@
                         Number(this.max_elt.textContent) :
                         this.args.length >= 2 ? this.args[1] : 100;
 
+            // save initial parameters
+            this.range_elt.style.strokeMiterlimit="0";
             this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
             let start = this.range_elt.getPointAtLength(0);
             let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
             this.fi = Math.atan2(start.y-end.y, end.x-start.x);
-
+            this.handle_orig = this.handle_elt.getBBox();
+
+            //bind functions
             this.bound_on_select = this.on_select.bind(this);
             this.bound_on_release = this.on_release.bind(this);
             this.on_bound_drag = this.on_drag.bind(this);
 
+            this.handle_elt.addEventListener("mousedown", this.bound_on_select);
             this.element.addEventListener("mousedown", this.bound_on_select);
             this.element.addEventListener("touchstart", this.bound_on_select);
 
--- a/svghmi/widget_tooglebutton.ysl2	Tue Aug 18 11:42:28 2020 +0200
+++ b/svghmi/widget_tooglebutton.ysl2	Wed Sep 16 09:41:52 2020 +0200
@@ -10,19 +10,28 @@
         inactive_style = undefined;
 
         dispatch(value) {
-            this.state = value;
-            if (this.state) {
-                this.active_elt.setAttribute("style", this.active_style);
-                this.inactive_elt.setAttribute("style", "display:none");
-                this.state = 0;
-            } else {
-                this.inactive_elt.setAttribute("style", this.inactive_style);
-                this.active_elt.setAttribute("style", "display:none");
-                this.state = 1;
+            if(this.state != value){
+                this.state = value;
+                if (this.state) {
+                    this.active_elt.setAttribute("style", this.active_style);
+                    this.inactive_elt.setAttribute("style", "display:none");
+                } else {
+                    this.inactive_elt.setAttribute("style", this.inactive_style);
+                    this.active_elt.setAttribute("style", "display:none");
+                }
             }
         }
 
         on_click(evt) {
+            if (this.state) {
+                this.inactive_elt.setAttribute("style", this.inactive_style);
+                this.active_elt.setAttribute("style", "display:none");
+                this.state = 0;
+            } else {
+                this.active_elt.setAttribute("style", this.active_style);
+                this.inactive_elt.setAttribute("style", "display:none");
+                this.state = 1;
+            }
             this.apply_hmi_value(0, this.state);
         }
 
@@ -30,6 +39,8 @@
             this.active_style = this.active_elt.style.cssText;
             this.inactive_style = this.inactive_elt.style.cssText;
             this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
+            this.inactive_elt.setAttribute("style", this.inactive_style);
+            this.active_elt.setAttribute("style", "display:none");
         }
     }
     ||
--- a/svghmi/widgets_common.ysl2	Tue Aug 18 11:42:28 2020 +0200
+++ b/svghmi/widgets_common.ysl2	Wed Sep 16 09:41:52 2020 +0200
@@ -30,7 +30,7 @@
             when "not(@index)" {
                 choose {
                     when "not(@type)" 
-                        error > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
+                        warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
                     when "@type = 'PAGE_LOCAL'" 
                         > "«@value»"`if "position()!=last()" > ,`
                     when "@type = 'HMI_LOCAL'"