// widget_circuralslider.ysl2
widget_desc("CircularSlider") {
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")
let [min,max,start,totallength] = this.range;
//save current value inside widget
if (this.curr_value > max){
this.apply_hmi_value(0, this.curr_value);
else if (this.curr_value < min){
this.apply_hmi_value(0, this.curr_value);
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);
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){
this.setpoint_elt.setAttribute("style", this.setpoint_style);
this.setpoint_elt.setAttribute("style", "display:none");
this.last_drag = this.drag;
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);
this.update_position(evt);
//ignore drag event for X amount of time and if not selected
if(this.enTimer && this.drag){
this.update_position(evt);
setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
if(this.drag && this.enTimer){
//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;
if (evt.type.startsWith("touch")){
mouseX = Math.ceil(evt.touches[0].clientX);
mouseY = Math.ceil(evt.touches[0].clientY);
let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml);
// transform from 0 to 2PI
//get handle distance from mouse position
this.curr_value=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
else if(fiEnd<fi && fi<fiEnd+minMax){
this.curr_value = this.range[1];
this.curr_value = this.range[0];
this.apply_hmi_value(0, Math.ceil(this.curr_value));
// 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);
this.update_DOM(this.curr_value, this.handle_elt);
//enable drag flag and timer
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);
//update postion on mouse press
this.update_position(evt);
Number(this.min_elt.textContent) :
this.args.length >= 1 ? this.args[0] : 0;
Number(this.max_elt.textContent) :
this.args.length >= 2 ? this.args[1] : 100;
let fiStart = Number(this.range_elt.getAttribute('sodipodi:start'));
let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end'));
//fiEnd ==> size of angle
fiEnd = 2*Math.PI + fiEnd;
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];
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);
if(this.setpoint_elt != undefined){
this.setpoint_style = this.setpoint_elt.getAttribute("style");
this.setpoint_elt.setAttribute("style", "display:none");
widget_defs("CircularSlider") {
optional_labels("value min max setpoint");