# HG changeset patch # User usveticic # Date 1600242112 -7200 # Node ID 827bf284feec26247d76a422849eb3442c2f7515 # Parent f6d428330e0471cbb7c4f75a4c8edf7785f3cd4f 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. diff -r f6d428330e04 -r 827bf284feec svghmi/widget_button.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() { diff -r f6d428330e04 -r 827bf284feec svghmi/widget_slider.ysl2 --- 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); diff -r f6d428330e04 -r 827bf284feec svghmi/widget_tooglebutton.ysl2 --- 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"); } } || diff -r f6d428330e04 -r 827bf284feec svghmi/widgets_common.ysl2 --- 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'"