SVGHMI: Refactor the way JsonTable generate javascript code to access json data. Now support multiple assignments, used in the case of text to change both content and style on the same element.
// widget_circuralslider.ysl2
template "widget[@type='CircularSlider']", mode="widget_class"
||
class CircularSliderWidget extends Widget{
frequency = 5;
range = undefined;
circle = undefined;
handle_pos = undefined;
drag = false;
enTimer = false;
dispatch(value) {
if(!this.drag){
if(this.value_elt)
this.value_elt.textContent = String(value);
this.handle_position(value);
}
}
handle_position(value){
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);
this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
}
on_release(evt) {
if(this.drag){
this.drag = false;
}
}
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){
svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
}
else if(fiEnd<fi && fi<fiEnd+minMax){
svg_dist = this.range[1];
}
else{
svg_dist = this.range[0];
}
//redraw handle --TODO is it fast enough if I just call change_hmi_value???
this.handle_position(svg_dist);
if(this.value_elt)
this.value_elt.textContent = String(Math.ceil(svg_dist));
this.apply_hmi_value(0, Math.ceil(svg_dist));
//reset timer
this.enTimer = false;
setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
}
}
on_select(evt){
this.drag = true;
this.enTimer = true;
this.update_position(evt);
}
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];
//init events
this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this));
this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this));
window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this));
window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this))
window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this));
window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this));
}
}
||
template "widget[@type='CircularSlider']", mode="widget_defs" {
param "hmi_element";
labels("handle range");
optional_labels("value min max");
|,
}