Button, ToggleButton and slider updated. Error to warning when building
Button fixed so it doesn't release until it gets feedback from plc
Toggle button changed so it makes changes instantly. There was too big delay if we waited for feedback.
Slider added new features need some changes for final version.
// widget_slider.ysl2
template "widget[@type='Slider']", mode="widget_class"
||
class SliderWidget extends Widget{
frequency = 5;
range = undefined;
handle_orig = undefined;
scroll_size = 10;
min_size = 0.07;
fi = undefined;
curr_value = 0;
drag = false;
enTimer = false;
handle_click = undefined;
last_drag = false;
dispatch(value) {
//save current value inside widget
this.curr_value = 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,start,totallength] = this.range;
// check if handle is resizeable
if (this.scroll_size != undefined){ //size changes
//get parameters
let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min)));
let tip = this.range_elt.getPointAtLength(length);
let handle_min = totallength*this.min_size;
let step = 1;
//check if range is bigger than max displayed and recalculate step
if ((totallength/handle_min) < (max-min+1)){
step = (max-min+1)/(totallength/handle_min-1);
}
let kx,ky,offseY,offseX = undefined;
//scale on x or y axes
if (this.fi > 0.75){
//get scale factor
if(step > 1){
ky = handle_min/this.handle_orig.height;
}
else{
ky = (totallength-handle_min*(max-min))/this.handle_orig.height;
}
kx = 1;
//get 0 offset to stay inside range
offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky;
offseX = 0;
//get distance from value
tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min;
}
else{
//get scale factor
if(step > 1){
kx = handle_min/this.handle_orig.width;
}
else{
kx = (totallength-handle_min*(max-min))/this.handle_orig.width;
}
ky = 1;
//get 0 offset to stay inside range
offseX = start.x - (this.handle_orig.x * kx);
offseY = 0;
//get distance from value
tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min;
}
elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")");
}
else{ //size stays the same
let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
let tip = this.range_elt.getPointAtLength(length);
elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.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){
var html_dist = 0;
let [min,max,start,totallength] = this.range;
//calculate size of widget in html
var range_borders = this.range_elt.getBoundingClientRect();
var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
//get range and mouse coordinates
var mouseX = undefined;
var 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 position
if (this.handle_click){ //if clicked on handle
let moveDist = 0, resizeAdd = 0;
let range_percent = 1;
//set paramters for resizeable handle
if (this.scroll_size != undefined){
// add one more object to stay inside range
resizeAdd = 1;
//chack if range is bigger than display option and
// calculate percent of range with out handle
if(((max/(max*this.min_size)) < (max-min+1))){
range_percent = 1-this.min_size;
}
else{
range_percent = 1-(max-max*this.min_size*(max-min))/max;
}
}
//calculate value difference on x or y axis
if(this.fi > 0.7){
moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi));
}
else{
moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi));
}
this.curr_value = Math.ceil(this.handle_click[2] + moveDist);
}
else{ //if clicked on widget
//get handle distance from mouse position
if (minX > mouseX && minY < mouseY){
html_dist = 0;
}
else if (maxX < mouseX && maxY > mouseY){
html_dist = range_length;
}
else{
if(this.fi > 0.7){
html_dist = (minY - mouseY)/Math.sin(this.fi);
}
else{
html_dist = (mouseX - minX)/Math.cos(this.fi);
}
}
//calculate distance
this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]);
}
//check if in range
if (this.curr_value > max){
this.curr_value = max;
}
else if (this.curr_value < min){
this.curr_value = min;
}
this.apply_hmi_value(0, 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);
// check if handle was pressed
if (evt.currentTarget == this.handle_elt){
//get mouse position on the handle
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;
}
//save coordinates and orig value
this.handle_click = [mouseX,mouseY,this.curr_value];
}
else{
// get new handle position and reset if handle was not pressed
this.handle_click = undefined;
this.update_position(evt);
}
//prevent next events
evt.stopPropagation();
}
init() {
//set min max value if not defined
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;
// save initial parameters
this.range_elt.style.strokeMiterlimit="0";
this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
let start = this.range_elt.getPointAtLength(0);
let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
this.fi = Math.atan2(start.y-end.y, end.x-start.x);
this.handle_orig = this.handle_elt.getBBox();
//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);
if(this.setpoint_elt != undefined){
this.setpoint_style = this.setpoint_elt.getAttribute("style");
this.setpoint_elt.setAttribute("style", "display:none");
}
}
}
||
template "widget[@type='Slider']", mode="widget_defs" {
param "hmi_element";
labels("handle range");
optional_labels("value min max setpoint");
|,
}