SVGHMI: Fixed problems happening when value is higher than maximum or lower than minimum in Input widget. "max" and "min" string was getting into keypad text field, was not diplayed systematically, and was staying forever sometime.
// widget_circuralslider.ysl2
template "widget[@type='CircularSlider']", mode="widget_class"
||
class CircularSliderWidget extends Widget{
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<fiEnd){
this.curr_value=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
}
else if(fiEnd<fi && fi<fiEnd+minMax){
this.curr_value = this.range[1];
}
else{
this.curr_value = this.range[0];
}
//apply value to hmi
this.apply_hmi_value(0, Math.ceil(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{
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);
//update postion on mouse press
this.update_position(evt);
//prevent next events
evt.stopPropagation();
}
init() {
//get min max
let min = this.min_elt ?
Number(this.min_elt.textContent) :
this.args.length >= 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");
}
}
}
||
template "widget[@type='CircularSlider']", mode="widget_defs" {
param "hmi_element";
labels("handle range");
optional_labels("value min max setpoint");
|,
}