// widget_circuralslider.ysl2 widget_desc("CircularSlider") { longdesc || CircularSlider - DEPRECATED, to be replaced by PathSlider This widget moves "handle" labeled group along "range" labeled arc, according to value of the single accepted variable. If "min" a "max" labeled texts are provided, or if first and second argument are given, then they are used as respective minimum and maximum value. Otherwise, value is expected to be in between 0 and 100. If "value" labeled text is found, then its content is replaced by value. During drag, "setpoint" labeled group is moved to position defined by user while "handle" reflects current value from variable. || shortdesc > CircularSlider - DEPRECATED arg name="min" count="optional" accepts="int,real" > minimum value arg name="min" count="optional" accepts="int,real" > maximum value // TODO: add printf-like format path name="value" accepts="HMI_INT,HMI_REAL" > Value to display } widget_class("CircularSlider") || frequency = 5; range = undefined; circle = undefined; handle_pos = undefined; curr_value = 0; drag = false; enTimer = false; last_drag = false; dispatch(value) { let [min,max,start,totallength] = this.range; //save current value inside widget this.curr_value = value; //check if in range if (this.curr_value > max){ this.curr_value = max; this.apply_hmi_value(0, this.curr_value); } else if (this.curr_value < min){ this.curr_value = min; this.apply_hmi_value(0, this.curr_value); } if(this.value_elt) this.value_elt.textContent = String(value); //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,totalDistance] = this.range; let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance))); let tip = this.range_elt.getPointAtLength(length); elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")"); // show or hide ghost if exists if(this.setpoint_elt != undefined){ if(this.last_drag!= this.drag){ if(this.drag){ this.setpoint_elt.setAttribute("style", this.setpoint_style); }else{ this.setpoint_elt.setAttribute("style", "display:none"); } this.last_drag = this.drag; } } } 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); } } update_position(evt){ if(this.drag && this.enTimer){ var svg_dist = 0; //calculate center of widget in html // --TODO maybe it would be better to bind this part to window change size event ??? let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle; let htmlCirc = this.range_elt.getBoundingClientRect(); let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left; let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top; //get mouse coordinates 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; } //calculate angle let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml); // transform from 0 to 2PI if (fi > 0){ fi = 2*Math.PI-fi; } else{ fi = -fi; } //offset it to 0 fi = fi - fiStart; if (fi < 0){ fi = fi + 2*Math.PI; } //get handle distance from mouse position if(fi= 1 ? this.args[0] : 0; let max = this.max_elt ? Number(this.max_elt.textContent) : this.args.length >= 2 ? this.args[1] : 100; //fiStart ==> offset let fiStart = Number(this.range_elt.getAttribute('sodipodi:start')); let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end')); fiEnd = fiEnd - fiStart; //fiEnd ==> size of angle if (fiEnd < 0){ fiEnd = 2*Math.PI + fiEnd; } //min max barrier angle let minMax = (2*Math.PI - fiEnd)/2; //get parameters from svg let cX = Number(this.range_elt.getAttribute('sodipodi:cx')); let cY = Number(this.range_elt.getAttribute('sodipodi:cy')); this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object this.range = [min, max,this.range_elt.getTotalLength()]; let cPos = this.range_elt.getBBox(); this.handle_pos = this.range_elt.getPointAtLength(0); this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height]; //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); //touch recognised as page drag without next command document.body.addEventListener("touchstart", function(e){}, false); //save ghost style //save ghost style if(this.setpoint_elt != undefined){ this.setpoint_style = this.setpoint_elt.getAttribute("style"); this.setpoint_elt.setAttribute("style", "display:none"); } } || widget_defs("CircularSlider") { labels("handle range"); optional_labels("value min max setpoint"); |, }