# HG changeset patch # User Edouard Tisserant # Date 1597060735 -7200 # Node ID 497aac6522a3a6433382e137c2a13f96a11237e6 # Parent 22b969b409b033ab0f6862f865dcfd11f39edfcc SVGHMI: provide request_animate() to Widget authors so that they can register redraw code when events lead to redraw. Widget member animate() is called when it is time to update DOM. diff -r 22b969b409b0 -r 497aac6522a3 svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Mon Aug 10 11:30:06 2020 +0200 +++ b/svghmi/gen_index_xhtml.xslt Mon Aug 10 13:58:55 2020 +0200 @@ -1,6 +1,6 @@ - - + + @@ -14,7 +14,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -103,7 +103,7 @@ - + @@ -113,14 +113,14 @@ - + - + @@ -184,7 +184,7 @@ - + @@ -203,7 +203,7 @@ - + @@ -217,7 +217,7 @@ - + @@ -235,18 +235,18 @@ Raw HMI tree - + Indexed HMI tree - + Parsed Widgets - + @@ -459,7 +459,7 @@ - + @@ -559,7 +559,7 @@ } - + } @@ -583,13 +583,13 @@ var page_desc = { - + } - + @@ -621,16 +621,16 @@ - + - + - - - + + + none @@ -641,15 +641,15 @@ 100vw - + - + ViewBox settings other than X=0, Y=0 and Scale=1 are not supported - + All units must be set to "px" in Inkscape's document properties @@ -658,7 +658,7 @@ - + @@ -668,7 +668,7 @@ - + @@ -729,7 +729,7 @@ - + @@ -739,14 +739,14 @@ - + - + @@ -754,10 +754,10 @@ - + - + @@ -769,7 +769,7 @@ - + @@ -777,7 +777,7 @@ - + @@ -828,7 +828,7 @@ - + @@ -876,7 +876,7 @@ ],{ - + }) @@ -915,6 +915,10 @@ + var pending_widget_animates = []; + + + class Widget { offset = 0; @@ -923,6 +927,10 @@ unsubscribable = false; + pending_animate = false; + + + constructor(elt_id,args,indexes,members){ this.element_id = elt_id; @@ -1091,6 +1099,34 @@ } + + + _animate(){ + + this.animate(); + + this.pending_animate = false; + + } + + + + request_animate(){ + + if(!this.pending_animate){ + + pending_widget_animates.push(this); + + this.pending_animate = true; + + requestHMIAnimation(); + + } + + + + } + } @@ -1107,11 +1143,11 @@ - - - - - + + + + + class Widget extends Widget{ @@ -1137,7 +1173,7 @@ var hmi_widgets = { - + } @@ -1231,7 +1267,7 @@ - + class BackWidget extends Widget{ on_click(evt) { @@ -1257,7 +1293,7 @@ } - + t{ 5; @@ -1331,7 +1367,7 @@ || - + @@ -1343,7 +1379,7 @@ - + frequency: 10, @@ -1439,7 +1475,7 @@ }, - + class CircularSliderWidget extends Widget{ frequency = 5; @@ -1614,7 +1650,7 @@ this.value_elt.textContent = String(Math.ceil(svg_dist)); - change_hmi_value(this.indexes[0], "="+Math.ceil(svg_dist)); + this.apply_hmi_value(0, Math.ceil(svg_dist)); @@ -1735,7 +1771,7 @@ } - + @@ -1753,7 +1789,7 @@ - + class DisplayWidget extends Widget{ frequency = 5; @@ -1771,7 +1807,7 @@ } - + @@ -2262,7 +2298,7 @@ - + @@ -2775,7 +2811,7 @@ }, - + @@ -2889,7 +2925,7 @@ item_offset: 0, - + class ForEachWidget extends Widget{ @@ -3025,7 +3061,7 @@ } - + @@ -3109,7 +3145,7 @@ }, - + class JsonTableWidget extends Widget{ do_http_request() { @@ -3169,14 +3205,14 @@ } - + JsonTable Widget can't contain element of type . - + @@ -3198,7 +3234,7 @@ ]); - + id(" @@ -3207,16 +3243,16 @@ ); - + - + - + let obj_ @@ -3225,14 +3261,14 @@ ; - + obj_ - + @@ -3250,7 +3286,7 @@ spread_json_data: function(jdata) { - + } @@ -3276,7 +3312,7 @@ - + @@ -3411,7 +3447,7 @@ }, - + @@ -3513,7 +3549,7 @@ - + class KeypadWidget extends Widget{ moving = undefined; @@ -3805,7 +3841,7 @@ } - + @@ -3884,7 +3920,7 @@ ], - + items: { @@ -3899,7 +3935,7 @@ }, - + frequency: 10, @@ -3957,7 +3993,7 @@ }, - + class MultiStateWidget extends Widget{ frequency = 5; @@ -4024,7 +4060,7 @@ //post value to plc - change_hmi_value(this.indexes[0], "="+this.state); + this.apply_hmi_value(0, this.state); } @@ -4039,7 +4075,7 @@ } - + choices: [ @@ -4070,7 +4106,7 @@ ], - + class SliderWidget extends Widget{ frequency = 5; @@ -4083,19 +4119,43 @@ enTimer = false; + svg_dist = 0 + dispatch(value) { - if(!this.drag){ - - if(this.value_elt) - - this.value_elt.textContent = String(value); - - - - this.handle_position(value); + if(this.value_elt) + + this.value_elt.textContent = String(value); + + + + this.handle_position(value); + + } + + + + handle_position(value){ + + let [min,max,start,totallength] = this.range; + + let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); + + let tip = this.range_elt.getPointAtLength(length); + + this.handle_elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")"); + + } + + + + on_release(evt) { + + if(this.drag){ + + this.drag = false; } @@ -4103,214 +4163,202 @@ - handle_position(value){ - - let [min,max,start,totallength] = this.range; - - let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); - - let tip = this.range_elt.getPointAtLength(length); - - this.handle_elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")"); + update_position(evt){ + + if(this.drag){ + + var html_dist = 0; + + + + //calculate size of widget in html + + var range_borders = this.range_elt.getBoundingClientRect(); + + var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width ); + + var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top]; + + + + //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; + + } + + + + //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{ + + // calculate distace + + if(this.fi > 0.7){ + + html_dist = (minY - mouseY)/Math.sin(this.fi); + + } + + else{ + + html_dist = (mouseX - minX)/Math.cos(this.fi); + + } + + + + //check if in range + + if (html_dist > range_length){ + + html_dist = range_length; + + } + + else if (html_dist < 0){ + + html_dist = 0; + + } + + } + + + + this.svg_dist=(html_dist/range_length)*this.range[1]; + + + + //redraw handle + + //this.handle_position(svg_dist=(html_dist/range_length)*this.range[1]); + + //this.value_elt.textContent = String(Math.ceil(svg_dist)); + + + + if(this.enTimer){ + + this.apply_hmi_value(0, Math.ceil(this.svg_dist)); + + + + // TODO : update ghost cursor and call this.request_animate() + + + + //reset timer + + this.enTimer = false; + + setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); + + } + + } } - on_release(evt) { - - if(this.drag){ - - this.drag = false; - - } + on_select(evt){ + + this.drag = true; + + this.enTimer = true; + + this.update_position(evt); } - update_position(evt){ - - if(this.drag && this.enTimer){ - - var html_dist = 0; - - var svg_dist = 0; - - - - //calculate size of widget in html - - var range_borders = this.range_elt.getBoundingClientRect(); - - var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width ); - - var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top]; - - - - //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; - - } - - - - //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{ - - // calculate distace - - if(this.fi > 0.7){ - - html_dist = (minY - mouseY)/Math.sin(this.fi); - - } - - else{ - - html_dist = (mouseX - minX)/Math.cos(this.fi); - - } - - - - //check if in range - - if (html_dist > range_length){ - - html_dist = range_length; - - } - - else if (html_dist < 0){ - - html_dist = 0; - - } - - } - - //redraw handle - - this.handle_position(svg_dist=(html_dist/range_length)*this.range[1]); - - this.value_elt.textContent = String(Math.ceil(svg_dist)); - - change_hmi_value(this.indexes[0], "="+Math.ceil(svg_dist)); - - //reset timer - - this.enTimer = false; - - setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); - - } + init() { + + 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; + + + + 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_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)); } - - - on_select(evt){ - - this.drag = true; - - this.enTimer = true; - - this.update_position(evt); - - } - - - - init() { - - 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; - - - - 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_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)); - - - - } - } - + @@ -4328,7 +4376,7 @@ - + class SwitchWidget extends Widget{ frequency = 5; @@ -4354,7 +4402,7 @@ } - + choices: [ @@ -4385,7 +4433,7 @@ ], - + class ToggleButtonWidget extends Widget{ frequency = 5; @@ -4426,7 +4474,7 @@ on_click(evt) { - change_hmi_value(this.indexes[0], "="+this.state); + this.apply_hmi_value(0, this.state); } @@ -4445,7 +4493,7 @@ } - + @@ -4656,6 +4704,14 @@ apply_updates(); + + + pending_widget_animates.forEach(widget => widget._animate()); + + pending_widget_animates = []; + + + requestAnimationFrameID = null; } @@ -5252,7 +5308,7 @@ var edit_callback; - function edit_value(path, valuetype, callback, initial,size) { + function edit_value(path, valuetype, callback, initial, size) { @@ -5264,7 +5320,7 @@ let widget = hmi_widgets[keypadid]; - widget.start_edit(path, valuetype, callback, initial,size); + widget.start_edit(path, valuetype, callback, initial, size); }; diff -r 22b969b409b0 -r 497aac6522a3 svghmi/svghmi.js --- a/svghmi/svghmi.js Mon Aug 10 11:30:06 2020 +0200 +++ b/svghmi/svghmi.js Mon Aug 10 13:58:55 2020 +0200 @@ -76,6 +76,10 @@ if(jumps_need_update) update_jumps(); apply_updates(); + + pending_widget_animates.forEach(widget => widget._animate()); + pending_widget_animates = []; + requestAnimationFrameID = null; } diff -r 22b969b409b0 -r 497aac6522a3 svghmi/widgets_common.ysl2 --- a/svghmi/widgets_common.ysl2 Mon Aug 10 11:30:06 2020 +0200 +++ b/svghmi/widgets_common.ysl2 Mon Aug 10 13:58:55 2020 +0200 @@ -64,10 +64,14 @@ emit "preamble:widget-base-class" { || + var pending_widget_animates = []; + class Widget { offset = 0; frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */ unsubscribable = false; + pending_animate = false; + constructor(elt_id,args,indexes,members){ this.element_id = elt_id; this.element = id(elt_id); @@ -152,6 +156,20 @@ console.log(err); } } + + _animate(){ + this.animate(); + this.pending_animate = false; + } + + request_animate(){ + if(!this.pending_animate){ + pending_widget_animates.push(this); + this.pending_animate = true; + requestHMIAnimation(); + } + + } } || }