# HG changeset patch # User Edouard Tisserant # Date 1586418754 -7200 # Node ID ddce4ebdf01098e7b4e3581f64d2e63bbc3f9e5b # Parent 2670f5c53caf598e29a74c96bc71c8d56d263b82 SVGHMI: intermediate commit while working on dropdown widget. Here is the plan : HMI:DropDown : svg:g of svg:rect + svg:text rect is extended to match content size, and if content size exceed page size, user can scroll HMI:List : either HMI:List:ListName as svg:text, one tspan per list entry or HMI:List:ListName:Foreach:HMI_CLASS:SUB/PATH/TO/VALUE@/ROOT/PATH as empty svg:g diff -r 2670f5c53caf -r ddce4ebdf010 svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Tue Apr 07 10:01:23 2020 +0200 +++ b/svghmi/gen_index_xhtml.xslt Thu Apr 09 09:52:34 2020 +0200 @@ -817,6 +817,55 @@ }, + + + + + + text box + + + dispatch: function(value) { + + this.text_elt.textContent = String(value); + + }, + + init: function() { + + this.element.setAttribute("onclick", "hmi_widgets[' + + '].on_click()"); + + }, + + + + original_box: [ + + , + + , + + , + + ], + + on_click: function() { + + let [x,y,w,h] = page_desc[current_visible_page].bbox; + + let p = new DOMPoint(this.box_elt.x.baseVal.value, this.box_elt.y.baseVal.value); + + let k = DOMMatrix.fromMatrix(this.box_elt.getCTM()); + + let l = DOMMatrix.fromMatrix(this.box_elt.getScreenCTM()); + + console.log(p, k.transformPoint(p), l.transformPoint(p)); + + }, + + @@ -2157,573 +2206,567 @@ let old_val = cache[index] - console.log("apply", index, new_val); - - if(new_val != undefined && old_val != new_val){ - - console.log("sending", new_val); + if(new_val != undefined && old_val != new_val) send_hmi_value(index, new_val); + return new_val; + + } + + + + function change_hmi_value(index, opstr) { + + let op = opstr[0]; + + let given_val = opstr.slice(1); + + let old_val = cache[index] + + let new_val; + + switch(op){ + + case "=": + + eval("new_val"+opstr); + + break; + + case "+": + + case "-": + + case "*": + + case "/": + + if(old_val != undefined) + + new_val = eval("old_val"+opstr); + + break; + } + if(new_val != undefined && old_val != new_val) + + send_hmi_value(index, new_val); + return new_val; } - function change_hmi_value(index, opstr) { - - let op = opstr[0]; - - let given_val = opstr.slice(1); - - let old_val = cache[index] - - let new_val; - - switch(op){ - - case "=": - - eval("new_val"+opstr); - - break; - - case "+": - - case "-": - - case "*": - - case "/": - - if(old_val != undefined) - - new_val = eval("old_val"+opstr); - - break; + var current_visible_page; + + var current_subscribed_page; + + var current_page_index; + + + + function prepare_svg() { + + for(let eltid in detachable_elements){ + + let [element,parent] = detachable_elements[eltid]; + + parent.removeChild(element); } - if(new_val != undefined && old_val != new_val) - - send_hmi_value(index, new_val); - - return new_val; + }; + + + + function switch_page(page_name, page_index) { + + if(current_subscribed_page != current_visible_page){ + + /* page switch already going */ + + /* TODO LOG ERROR */ + + return false; + + } + + + + if(page_name == undefined) + + page_name = current_subscribed_page; + + + + + + let old_desc = page_desc[current_subscribed_page]; + + let new_desc = page_desc[page_name]; + + + + if(new_desc == undefined){ + + /* TODO LOG ERROR */ + + return false; + + } + + + + if(page_index == undefined){ + + page_index = new_desc.page_index; + + } + + + + if(old_desc){ + + old_desc.absolute_widgets.map(w=>w.unsub()); + + old_desc.relative_widgets.map(w=>w.unsub()); + + } + + new_desc.absolute_widgets.map(w=>w.sub()); + + var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; + + new_desc.relative_widgets.map(w=>w.sub(new_offset)); + + + + update_subscriptions(); + + + + current_subscribed_page = page_name; + + current_page_index = page_index; + + + + jumps_need_update = true; + + + + requestHMIAnimation(); + + + + jump_history.push([page_name, page_index]); + + if(jump_history.length > 42) + + jump_history.shift(); + + + + return true; + + }; + + + + function* chain(a,b){ + + yield* a; + + yield* b; + + }; + + + + function unsubscribe(){ + + /* remove subsribers */ + + for(let index of this.indexes){ + + let idx = index + this.offset; + + subscribers[idx].delete(this); + + } + + this.offset = 0; } - var current_visible_page; - - var current_subscribed_page; - - var current_page_index; - - - - function prepare_svg() { - - for(let eltid in detachable_elements){ - - let [element,parent] = detachable_elements[eltid]; - - parent.removeChild(element); + function subscribe(new_offset=0){ + + /* set the offset because relative */ + + this.offset = new_offset; + + /* add this's subsribers */ + + for(let index of this.indexes){ + + subscribers[index + new_offset].add(this); } + need_cache_apply.push(this); + + } + + + + function foreach_unsubscribe(){ + + for(let item of this.items){ + + for(let widget of item) { + + unsubscribe.call(widget); + + } + + } + + this.offset = 0; + + } + + + + function foreach_widgets_do(new_offset, todo){ + + this.offset = new_offset; + + for(let i = 0; i < this.items.length; i++) { + + let item = this.items[i]; + + let orig_item_index = this.index_pool[i]; + + let item_index = this.index_pool[i+this.item_offset]; + + let item_index_offset = item_index - orig_item_index; + + for(let widget of item) { + + todo.call(widget, new_offset + item_index_offset); + + } + + } + + } + + + + function foreach_subscribe(new_offset=0){ + + foreach_widgets_do.call(this, new_offset, subscribe); + + } + + + + function widget_apply_cache() { + + for(let index of this.indexes){ + + /* dispatch current cache in newly opened page widgets */ + + let realindex = index+this.offset; + + let cached_val = cache[realindex]; + + if(cached_val != undefined) + + dispatch_value_to_widget(this, realindex, cached_val, cached_val); + + } + + } + + + + function foreach_apply_cache() { + + foreach_widgets_do.call(this, this.offset, widget_apply_cache); + + } + + + + function foreach_onclick(opstr, evt) { + + new_item_offset = eval(String(this.item_offset)+opstr) + + if(new_item_offset + this.items.length > this.index_pool.length) { + + if(this.item_offset + this.items.length == this.index_pool.length) + + new_item_offset = 0; + + else + + new_item_offset = this.index_pool.length - this.items.length; + + } else if(new_item_offset < 0) { + + if(this.item_offset == 0) + + new_item_offset = this.index_pool.length - this.items.length; + + else + + new_item_offset = 0; + + } + + this.item_offset = new_item_offset; + + off = this.offset; + + foreach_unsubscribe.call(this); + + foreach_subscribe.call(this,off); + + update_subscriptions(); + + need_cache_apply.push(this); + + jumps_need_update = true; + + requestHMIAnimation(); + + } + + + + + + function switch_visible_page(page_name) { + + + + let old_desc = page_desc[current_visible_page]; + + let new_desc = page_desc[page_name]; + + + + if(old_desc){ + + for(let eltid in old_desc.required_detachables){ + + if(!(eltid in new_desc.required_detachables)){ + + let [element, parent] = old_desc.required_detachables[eltid]; + + parent.removeChild(element); + + } + + } + + for(let eltid in new_desc.required_detachables){ + + if(!(eltid in old_desc.required_detachables)){ + + let [element, parent] = new_desc.required_detachables[eltid]; + + parent.appendChild(element); + + } + + } + + }else{ + + for(let eltid in new_desc.required_detachables){ + + let [element, parent] = new_desc.required_detachables[eltid]; + + parent.appendChild(element); + + } + + } + + + + svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); + + current_visible_page = page_name; + }; - function switch_page(page_name, page_index) { - - if(current_subscribed_page != current_visible_page){ - - /* page switch already going */ - - /* TODO LOG ERROR */ - - return false; - - } - - - - if(page_name == undefined) - - page_name = current_subscribed_page; - - - - - - let old_desc = page_desc[current_subscribed_page]; - - let new_desc = page_desc[page_name]; - - - - if(new_desc == undefined){ - - /* TODO LOG ERROR */ - - return false; - - } - - - - if(page_index == undefined){ - - page_index = new_desc.page_index; - - } - - - - if(old_desc){ - - old_desc.absolute_widgets.map(w=>w.unsub()); - - old_desc.relative_widgets.map(w=>w.unsub()); - - } - - new_desc.absolute_widgets.map(w=>w.sub()); - - var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; - - new_desc.relative_widgets.map(w=>w.sub(new_offset)); - - - - update_subscriptions(); - - - - current_subscribed_page = page_name; - - current_page_index = page_index; - - - - jumps_need_update = true; - - - - requestHMIAnimation(); - - - - jump_history.push([page_name, page_index]); - - if(jump_history.length > 42) - - jump_history.shift(); - - - - return true; + function update_jumps() { + + page_desc[current_visible_page].jumps.map(w=>w.notify_page_change(current_visible_page,current_page_index)); + + jumps_need_update = false; }; - function* chain(a,b){ - - yield* a; - - yield* b; + + + // Once connection established + + ws.onopen = function (evt) { + + init_widgets(); + + send_reset(); + + // show main page + + prepare_svg(); + + switch_page(default_page); }; - function unsubscribe(){ - - /* remove subsribers */ - - for(let index of this.indexes){ - - let idx = index + this.offset; - - subscribers[idx].delete(this); - - } - - this.offset = 0; - - } - - - - function subscribe(new_offset=0){ - - /* set the offset because relative */ - - this.offset = new_offset; - - /* add this's subsribers */ - - for(let index of this.indexes){ - - subscribers[index + new_offset].add(this); - - } - - need_cache_apply.push(this); - - } - - - - function foreach_unsubscribe(){ - - for(let item of this.items){ - - for(let widget of item) { - - unsubscribe.call(widget); - - } - - } - - this.offset = 0; - - } - - - - function foreach_widgets_do(new_offset, todo){ - - this.offset = new_offset; - - for(let i = 0; i < this.items.length; i++) { - - let item = this.items[i]; - - let orig_item_index = this.index_pool[i]; - - let item_index = this.index_pool[i+this.item_offset]; - - let item_index_offset = item_index - orig_item_index; - - for(let widget of item) { - - todo.call(widget, new_offset + item_index_offset); - - } - - } - - } - - - - function foreach_subscribe(new_offset=0){ - - foreach_widgets_do.call(this, new_offset, subscribe); - - } - - - - function widget_apply_cache() { - - for(let index of this.indexes){ - - /* dispatch current cache in newly opened page widgets */ - - let realindex = index+this.offset; - - let cached_val = cache[realindex]; - - if(cached_val != undefined) - - dispatch_value_to_widget(this, realindex, cached_val, cached_val); - - } - - } - - - - function foreach_apply_cache() { - - foreach_widgets_do.call(this, this.offset, widget_apply_cache); - - } - - - - function foreach_onclick(opstr, evt) { - - new_item_offset = eval(String(this.item_offset)+opstr) - - if(new_item_offset + this.items.length > this.index_pool.length) { - - if(this.item_offset + this.items.length == this.index_pool.length) - - new_item_offset = 0; - - else - - new_item_offset = this.index_pool.length - this.items.length; - - } else if(new_item_offset < 0) { - - if(this.item_offset == 0) - - new_item_offset = this.index_pool.length - this.items.length; - - else - - new_item_offset = 0; - - } - - this.item_offset = new_item_offset; - - off = this.offset; - - foreach_unsubscribe.call(this); - - foreach_subscribe.call(this,off); - - update_subscriptions(); - - need_cache_apply.push(this); - - jumps_need_update = true; - - requestHMIAnimation(); - - } - - - - - - function switch_visible_page(page_name) { - - - - let old_desc = page_desc[current_visible_page]; - - let new_desc = page_desc[page_name]; - - - - if(old_desc){ - - for(let eltid in old_desc.required_detachables){ - - if(!(eltid in new_desc.required_detachables)){ - - let [element, parent] = old_desc.required_detachables[eltid]; - - parent.removeChild(element); - - } - - } - - for(let eltid in new_desc.required_detachables){ - - if(!(eltid in old_desc.required_detachables)){ - - let [element, parent] = new_desc.required_detachables[eltid]; - - parent.appendChild(element); - - } - - } - - }else{ - - for(let eltid in new_desc.required_detachables){ - - let [element, parent] = new_desc.required_detachables[eltid]; - - parent.appendChild(element); - - } - - } - - - - svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); - - current_visible_page = page_name; + ws.onclose = function (evt) { + + // TODO : add visible notification while waiting for reload + + console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s."); + + // TODO : re-enable auto reload when not in debug + + //window.setTimeout(() => location.reload(true), 10000); + + alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+"."); + + }; - function update_jumps() { - - page_desc[current_visible_page].jumps.map(w=>w.notify_page_change(current_visible_page,current_page_index)); - - jumps_need_update = false; + var xmlns = "http://www.w3.org/2000/svg"; + + var edit_callback; + + function edit_value(path, valuetype, callback, initial) { + + + + let [keypadid, xcoord, ycoord] = keypads[valuetype]; + + console.log('XXX TODO : Edit value', path, valuetype, callback, initial, keypadid); + + edit_callback = callback; + + let widget = hmi_widgets[keypadid]; + + widget.start_edit(path, valuetype, callback, initial); }; - - - // Once connection established - - ws.onopen = function (evt) { - - init_widgets(); - - send_reset(); - - // show main page - - prepare_svg(); - - switch_page(default_page); + var current_modal; /* TODO stack ?*/ + + + + function show_modal() { + + let [element, parent] = detachable_elements[this.element.id]; + + + + tmpgrp = document.createElementNS(xmlns,"g"); + + tmpgrpattr = document.createAttribute("transform"); + + + + let [xcoord,ycoord] = this.coordinates; + + let [xdest,ydest] = page_desc[current_visible_page].bbox; + + tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")"; + + tmpgrp.setAttributeNode(tmpgrpattr); + + + + tmpgrp.appendChild(element); + + parent.appendChild(tmpgrp); + + + + current_modal = [this.element.id, tmpgrp]; }; - ws.onclose = function (evt) { - - // TODO : add visible notification while waiting for reload - - console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s."); - - // TODO : re-enable auto reload when not in debug - - //window.setTimeout(() => location.reload(true), 10000); - - alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+"."); - - + function end_modal() { + + let [eltid, tmpgrp] = current_modal; + + let [element, parent] = detachable_elements[this.element.id]; + + + + parent.removeChild(tmpgrp); + + + + current_modal = undefined; }; - var xmlns = "http://www.w3.org/2000/svg"; - - var edit_callback; - - function edit_value(path, valuetype, callback, initial) { - - - - let [keypadid, xcoord, ycoord] = keypads[valuetype]; - - console.log('XXX TODO : Edit value', path, valuetype, callback, initial, keypadid); - - edit_callback = callback; - - let widget = hmi_widgets[keypadid]; - - widget.start_edit(path, valuetype, callback, initial); + function widget_active_activable(eltsub) { + + if(eltsub.inactive_style === undefined) + + eltsub.inactive_style = eltsub.inactive.getAttribute("style"); + + eltsub.inactive.setAttribute("style", "display:none"); + + if(eltsub.active_style !== undefined) + + eltsub.active.setAttribute("style", eltsub.active_style); + + console.log("active", eltsub); }; - - - var current_modal; /* TODO stack ?*/ - - - - function show_modal() { - - let [element, parent] = detachable_elements[this.element.id]; - - - - tmpgrp = document.createElementNS(xmlns,"g"); - - tmpgrpattr = document.createAttribute("transform"); - - - - let [xcoord,ycoord] = this.coordinates; - - let [xdest,ydest] = page_desc[current_visible_page].bbox; - - tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")"; - - tmpgrp.setAttributeNode(tmpgrpattr); - - - - tmpgrp.appendChild(element); - - parent.appendChild(tmpgrp); - - - - current_modal = [this.element.id, tmpgrp]; + function widget_inactive_activable(eltsub) { + + if(eltsub.active_style === undefined) + + eltsub.active_style = eltsub.active.getAttribute("style"); + + eltsub.active.setAttribute("style", "display:none"); + + if(eltsub.inactive_style !== undefined) + + eltsub.inactive.setAttribute("style", eltsub.inactive_style); + + console.log("inactive", eltsub); }; - - - function end_modal() { - - let [eltid, tmpgrp] = current_modal; - - let [element, parent] = detachable_elements[this.element.id]; - - - - parent.removeChild(tmpgrp); - - - - current_modal = undefined; - - }; - - - - function widget_active_activable(eltsub) { - - if(eltsub.inactive_style === undefined) - - eltsub.inactive_style = eltsub.inactive.getAttribute("style"); - - eltsub.inactive.setAttribute("style", "display:none"); - - if(eltsub.active_style !== undefined) - - eltsub.active.setAttribute("style", eltsub.active_style); - - console.log("active", eltsub); - - }; - - function widget_inactive_activable(eltsub) { - - if(eltsub.active_style === undefined) - - eltsub.active_style = eltsub.active.getAttribute("style"); - - eltsub.active.setAttribute("style", "display:none"); - - if(eltsub.inactive_style !== undefined) - - eltsub.inactive.setAttribute("style", eltsub.inactive_style); - - console.log("inactive", eltsub); - - }; - diff -r 2670f5c53caf -r ddce4ebdf010 svghmi/widget_dropdown.ysl2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widget_dropdown.ysl2 Thu Apr 09 09:52:34 2020 +0200 @@ -0,0 +1,24 @@ +// widget_dropdown.ysl2 + +template "widget[@type='DropDown']", mode="widget_defs" { + param "hmi_element"; + labels("text box"); + | dispatch: function(value) { + /* TODO : get selection text by index */ + | this.text_elt.textContent = String(value); + | }, + | init: function() { + | this.element.setAttribute("onclick", "hmi_widgets['«$hmi_element/@id»'].on_click()"); + | }, + const "box_elt","$hmi_element/*[@inkscape:label='box'][1]"; + const "g", "$geometry[@Id = $box_elt/@id]"; + | original_box: [«$g/@x», «$g/@y», «$g/@w», «$g/@h»], + | on_click: function() { + | let [x,y,w,h] = page_desc[current_visible_page].bbox; + | let p = new DOMPoint(this.box_elt.x.baseVal.value, this.box_elt.y.baseVal.value); + | let k = DOMMatrix.fromMatrix(this.box_elt.getCTM()); + | let l = DOMMatrix.fromMatrix(this.box_elt.getScreenCTM()); + | console.log(p, k.transformPoint(p), l.transformPoint(p)); + | }, +} + diff -r 2670f5c53caf -r ddce4ebdf010 tests/svghmi/svghmi_0@svghmi/svghmi.svg --- a/tests/svghmi/svghmi_0@svghmi/svghmi.svg Tue Apr 07 10:01:23 2020 +0200 +++ b/tests/svghmi/svghmi_0@svghmi/svghmi.svg Thu Apr 09 09:52:34 2020 +0200 @@ -2490,4 +2490,108 @@ id="tspan469" sodipodi:role="line">information + + + sel_0 + + + + + + + + onetwothreefiveseveneleven +