usveticic@3012: // widget_slider.ysl2 usveticic@3012: usveticic@3012: template "widget[@type='Slider']", mode="widget_class" usveticic@3012: || usveticic@3012: class SliderWidget extends Widget{ usveticic@3012: frequency = 5; usveticic@3012: range = undefined; usveticic@3056: handle_orig = undefined; usveticic@3062: scroll_size = undefined; usveticic@3062: scroll_range = 0; usveticic@3062: scroll_visible = 7; usveticic@3056: min_size = 0.07; usveticic@3012: fi = undefined; usveticic@3056: curr_value = 0; usveticic@3012: drag = false; usveticic@3012: enTimer = false; usveticic@3056: handle_click = undefined; usveticic@3056: last_drag = false; usveticic@3012: usveticic@3062: dispatch(value,oldval, index) { usveticic@3062: if (index == 0){ usveticic@3062: let [min,max,start,totallength] = this.range; usveticic@3062: //save current value inside widget usveticic@3062: this.curr_value = value; usveticic@3062: usveticic@3062: //check if in range usveticic@3062: if (this.curr_value > max){ usveticic@3062: this.curr_value = max; usveticic@3062: this.apply_hmi_value(0, this.curr_value); usveticic@3062: } usveticic@3062: else if (this.curr_value < min){ usveticic@3062: this.curr_value = min; usveticic@3062: this.apply_hmi_value(0, this.curr_value); usveticic@3062: } usveticic@3062: usveticic@3062: if(this.value_elt) usveticic@3062: this.value_elt.textContent = String(value); usveticic@3062: } usveticic@3062: else if(index == 1){ usveticic@3062: this.scroll_range = value; usveticic@3062: this.set_scroll(); usveticic@3062: } usveticic@3062: else if(index == 2){ usveticic@3062: this.scroll_visible = value; usveticic@3062: this.set_scroll(); usveticic@3062: } usveticic@3012: usveticic@3056: //don't update if draging and setpoint ghost doesn't exist usveticic@3056: if(!this.drag || (this.setpoint_elt != undefined)){ usveticic@3062: this.update_DOM(this.curr_value, this.handle_elt); usveticic@3062: } usveticic@3062: } usveticic@3062: usveticic@3062: set_scroll(){ usveticic@3062: //check if range is bigger than visible and set scroll size usveticic@3062: if(this.scroll_range > this.scroll_visible){ usveticic@3062: this.scroll_size = this.scroll_range - this.scroll_visible; usveticic@3062: this.range[0] = 0; usveticic@3062: this.range[1] = this.scroll_size; usveticic@3062: } usveticic@3062: else{ usveticic@3062: this.scroll_size = 1; usveticic@3062: this.range[0] = 0; usveticic@3062: this.range[1] = 1; usveticic@3056: } usveticic@3056: } Edouard@3021: Edouard@3021: update_DOM(value, elt){ usveticic@3012: let [min,max,start,totallength] = this.range; usveticic@3056: // check if handle is resizeable usveticic@3056: if (this.scroll_size != undefined){ //size changes usveticic@3056: //get parameters usveticic@3056: let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min))); usveticic@3056: let tip = this.range_elt.getPointAtLength(length); usveticic@3056: let handle_min = totallength*this.min_size; usveticic@3056: usveticic@3056: let step = 1; usveticic@3056: //check if range is bigger than max displayed and recalculate step usveticic@3056: if ((totallength/handle_min) < (max-min+1)){ usveticic@3056: step = (max-min+1)/(totallength/handle_min-1); usveticic@3056: } usveticic@3056: usveticic@3056: let kx,ky,offseY,offseX = undefined; usveticic@3056: //scale on x or y axes usveticic@3056: if (this.fi > 0.75){ usveticic@3056: //get scale factor usveticic@3056: if(step > 1){ usveticic@3056: ky = handle_min/this.handle_orig.height; usveticic@3056: } usveticic@3056: else{ usveticic@3056: ky = (totallength-handle_min*(max-min))/this.handle_orig.height; usveticic@3056: } usveticic@3056: kx = 1; usveticic@3056: //get 0 offset to stay inside range usveticic@3056: offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky; usveticic@3056: offseX = 0; usveticic@3056: //get distance from value usveticic@3056: tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min; usveticic@3056: } usveticic@3056: else{ usveticic@3056: //get scale factor usveticic@3056: if(step > 1){ usveticic@3056: kx = handle_min/this.handle_orig.width; usveticic@3056: } usveticic@3056: else{ usveticic@3056: kx = (totallength-handle_min*(max-min))/this.handle_orig.width; usveticic@3056: } usveticic@3056: ky = 1; usveticic@3056: //get 0 offset to stay inside range usveticic@3056: offseX = start.x - (this.handle_orig.x * kx); usveticic@3056: offseY = 0; usveticic@3056: //get distance from value usveticic@3056: tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min; usveticic@3056: } usveticic@3056: elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")"); usveticic@3056: } usveticic@3056: else{ //size stays the same usveticic@3056: let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); usveticic@3056: let tip = this.range_elt.getPointAtLength(length); usveticic@3056: elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")"); usveticic@3056: } usveticic@3056: usveticic@3056: // show or hide ghost if exists Edouard@3021: if(this.setpoint_elt != undefined){ Edouard@3021: if(this.last_drag!= this.drag){ Edouard@3021: if(this.drag){ Edouard@3021: this.setpoint_elt.setAttribute("style", this.setpoint_style); Edouard@3021: }else{ Edouard@3021: this.setpoint_elt.setAttribute("style", "display:none"); Edouard@3021: } Edouard@3021: this.last_drag = this.drag; Edouard@3021: } Edouard@3021: } usveticic@3012: } usveticic@3012: usveticic@3012: on_release(evt) { usveticic@3056: //unbind events Edouard@3021: window.removeEventListener("touchmove", this.on_bound_drag, true); Edouard@3021: window.removeEventListener("mousemove", this.on_bound_drag, true); Edouard@3021: usveticic@3059: window.removeEventListener("mouseup", this.bound_on_release, true); Edouard@3021: window.removeEventListener("touchend", this.bound_on_release, true); Edouard@3021: window.removeEventListener("touchcancel", this.bound_on_release, true); usveticic@3056: usveticic@3056: //reset drag flag usveticic@3012: if(this.drag){ usveticic@3012: this.drag = false; usveticic@3012: } usveticic@3056: usveticic@3056: // get final position Edouard@3021: this.update_position(evt); usveticic@3056: usveticic@3056: } Edouard@3021: Edouard@3021: on_drag(evt){ usveticic@3056: //ignore drag event for X amount of time and if not selected Edouard@3021: if(this.enTimer && this.drag){ Edouard@3021: this.update_position(evt); usveticic@3056: Edouard@3021: //reset timer Edouard@3021: this.enTimer = false; Edouard@3021: setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); Edouard@3021: } usveticic@3012: } usveticic@3012: usveticic@3012: update_position(evt){ Edouard@3021: var html_dist = 0; usveticic@3056: let [min,max,start,totallength] = this.range; usveticic@3012: Edouard@3021: //calculate size of widget in html Edouard@3021: var range_borders = this.range_elt.getBoundingClientRect(); usveticic@3056: var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top]; Edouard@3021: var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width ); usveticic@3012: Edouard@3021: //get range and mouse coordinates Edouard@3021: var mouseX = undefined; Edouard@3021: var mouseY = undefined; Edouard@3021: if (evt.type.startsWith("touch")){ Edouard@3021: mouseX = Math.ceil(evt.touches[0].clientX); Edouard@3021: mouseY = Math.ceil(evt.touches[0].clientY); Edouard@3021: } Edouard@3021: else{ Edouard@3021: mouseX = evt.pageX; Edouard@3021: mouseY = evt.pageY; Edouard@3021: } Edouard@3021: usveticic@3056: // calculate position usveticic@3056: if (this.handle_click){ //if clicked on handle usveticic@3056: let moveDist = 0, resizeAdd = 0; usveticic@3056: let range_percent = 1; usveticic@3056: usveticic@3056: //set paramters for resizeable handle usveticic@3056: if (this.scroll_size != undefined){ usveticic@3056: // add one more object to stay inside range usveticic@3056: resizeAdd = 1; usveticic@3056: usveticic@3056: //chack if range is bigger than display option and usveticic@3056: // calculate percent of range with out handle usveticic@3056: if(((max/(max*this.min_size)) < (max-min+1))){ usveticic@3056: range_percent = 1-this.min_size; usveticic@3056: } usveticic@3056: else{ usveticic@3056: range_percent = 1-(max-max*this.min_size*(max-min))/max; usveticic@3056: } usveticic@3056: } usveticic@3056: usveticic@3056: //calculate value difference on x or y axis usveticic@3056: if(this.fi > 0.7){ usveticic@3056: moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi)); usveticic@3056: } usveticic@3056: else{ usveticic@3056: moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi)); usveticic@3056: } usveticic@3056: usveticic@3056: this.curr_value = Math.ceil(this.handle_click[2] + moveDist); usveticic@3056: } usveticic@3056: else{ //if clicked on widget usveticic@3056: //get handle distance from mouse position usveticic@3056: if (minX > mouseX && minY < mouseY){ usveticic@3056: html_dist = 0; usveticic@3056: } usveticic@3056: else if (maxX < mouseX && maxY > mouseY){ usveticic@3056: html_dist = range_length; usveticic@3056: } usveticic@3056: else{ usveticic@3056: if(this.fi > 0.7){ usveticic@3056: html_dist = (minY - mouseY)/Math.sin(this.fi); usveticic@3056: } usveticic@3056: else{ usveticic@3056: html_dist = (mouseX - minX)/Math.cos(this.fi); usveticic@3056: } usveticic@3056: } usveticic@3056: //calculate distance usveticic@3056: this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]); usveticic@3056: } usveticic@3056: usveticic@3062: //check if in range and apply usveticic@3056: if (this.curr_value > max){ usveticic@3056: this.curr_value = max; usveticic@3056: } usveticic@3056: else if (this.curr_value < min){ usveticic@3056: this.curr_value = min; usveticic@3056: } usveticic@3056: this.apply_hmi_value(0, this.curr_value); usveticic@3056: usveticic@3056: //redraw handle usveticic@3056: this.request_animate(); usveticic@3056: usveticic@3056: } usveticic@3056: usveticic@3056: animate(){ usveticic@3056: // redraw handle on screen refresh usveticic@3056: // check if setpoint(ghost) handle exsist otherwise update main handle usveticic@3056: if(this.setpoint_elt != undefined){ usveticic@3056: this.update_DOM(this.curr_value, this.setpoint_elt); Edouard@3021: } Edouard@3021: else{ usveticic@3056: this.update_DOM(this.curr_value, this.handle_elt); usveticic@3056: } usveticic@3012: } usveticic@3012: usveticic@3012: on_select(evt){ usveticic@3062: //enable drag flag and timer usveticic@3062: this.drag = true; usveticic@3062: this.enTimer = true; usveticic@3062: usveticic@3062: //bind events usveticic@3062: window.addEventListener("touchmove", this.on_bound_drag, true); usveticic@3062: window.addEventListener("mousemove", this.on_bound_drag, true); usveticic@3062: usveticic@3062: window.addEventListener("mouseup", this.bound_on_release, true); usveticic@3062: window.addEventListener("touchend", this.bound_on_release, true); usveticic@3062: window.addEventListener("touchcancel", this.bound_on_release, true); usveticic@3062: usveticic@3062: // check if handle was pressed usveticic@3062: if (evt.currentTarget == this.handle_elt){ usveticic@3062: //get mouse position on the handle usveticic@3062: let mouseX = undefined; usveticic@3062: let mouseY = undefined; usveticic@3062: if (evt.type.startsWith("touch")){ usveticic@3062: mouseX = Math.ceil(evt.touches[0].clientX); usveticic@3062: mouseY = Math.ceil(evt.touches[0].clientY); usveticic@3056: } usveticic@3056: else{ usveticic@3062: mouseX = evt.pageX; usveticic@3062: mouseY = evt.pageY; usveticic@3062: } usveticic@3062: //save coordinates and orig value usveticic@3062: this.handle_click = [mouseX,mouseY,this.curr_value]; usveticic@3056: } usveticic@3056: else{ usveticic@3062: // get new handle position and reset if handle was not pressed usveticic@3062: this.handle_click = undefined; usveticic@3062: this.update_position(evt); usveticic@3062: } usveticic@3062: usveticic@3062: //prevent next events usveticic@3062: evt.stopPropagation(); usveticic@3062: usveticic@3062: } usveticic@3062: usveticic@3012: usveticic@3012: init() { usveticic@3056: //set min max value if not defined usveticic@3012: let min = this.min_elt ? usveticic@3012: Number(this.min_elt.textContent) : usveticic@3012: this.args.length >= 1 ? this.args[0] : 0; usveticic@3012: let max = this.max_elt ? usveticic@3012: Number(this.max_elt.textContent) : usveticic@3012: this.args.length >= 2 ? this.args[1] : 100; usveticic@3012: usveticic@3059: usveticic@3056: // save initial parameters usveticic@3056: this.range_elt.style.strokeMiterlimit="0"; usveticic@3012: this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()]; usveticic@3012: let start = this.range_elt.getPointAtLength(0); usveticic@3012: let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength()); usveticic@3012: this.fi = Math.atan2(start.y-end.y, end.x-start.x); usveticic@3056: this.handle_orig = this.handle_elt.getBBox(); usveticic@3056: usveticic@3056: //bind functions Edouard@3021: this.bound_on_select = this.on_select.bind(this); Edouard@3021: this.bound_on_release = this.on_release.bind(this); Edouard@3021: this.on_bound_drag = this.on_drag.bind(this); usveticic@3012: usveticic@3056: this.handle_elt.addEventListener("mousedown", this.bound_on_select); usveticic@3062: this.element.addEventListener("mousedown", this.bound_on_select); usveticic@3062: this.element.addEventListener("touchstart", this.bound_on_select); usveticic@3062: //touch recognised as page drag without next command usveticic@3062: document.body.addEventListener("touchstart", function(e){}, false); usveticic@3062: usveticic@3062: //save ghost style Edouard@3021: if(this.setpoint_elt != undefined){ Edouard@3021: this.setpoint_style = this.setpoint_elt.getAttribute("style"); Edouard@3021: this.setpoint_elt.setAttribute("style", "display:none"); Edouard@3021: } usveticic@3012: usveticic@3012: } usveticic@3012: } usveticic@3012: || usveticic@3012: usveticic@3012: template "widget[@type='Slider']", mode="widget_defs" { usveticic@3012: param "hmi_element"; usveticic@3012: labels("handle range"); usveticic@3062: optional_labels("value min max setpoint"); usveticic@3012: |, Edouard@3018: }