# HG changeset patch # User Edouard Tisserant # Date 1619989268 -7200 # Node ID 7bdb766c2a4d9df8bb40f52831c4b871e8b7fbdc # Parent 5243c2a2f7f8bd8b99a79f067a007b2263fdc8ae SVGHMI: In order to allow widget signature and description to coexist in same ysl2 file, introduced widget_class, widget_defs to declare widget codegen templates and gen_index_xhtml to mark templates that are only usefull in gen_index_xhtml.xslt. diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/detachable_pages.ysl2 --- a/svghmi/detachable_pages.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/detachable_pages.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -193,8 +193,7 @@ | "«@id»": detachable_elements["«@id»"]`if "position()!=last()" > ,` } | } - /* TODO generate some code for init() instead */ - apply "$parsed_widgets/widget[@id = $all_page_widgets/@id]", mode="per_page_widget_template"{ + apply "$parsed_widgets/widget[@id = $all_page_widgets/@id]", mode="widget_page"{ with "page_desc", "$desc"; } | }`if "position()!=last()" > ,` @@ -208,7 +207,7 @@ | } } -template "*", mode="per_page_widget_template"; +template "*", mode="widget_page"; emit "debug:detachable-pages" { diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_animate.ysl2 --- a/svghmi/widget_animate.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_animate.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_animate.ysl2 -template "widget[@type='Animate']", mode="widget_class"{ +widget_class("Animate") { || - class AnimateWidget extends Widget{ frequency = 5; speed = 0; start = false; @@ -42,12 +41,6 @@ let widget_pos = this.element.getBBox(); this.widget_center = [(widget_pos.x+widget_pos.width/2), (widget_pos.y+widget_pos.height/2)]; } - } || } - -template "widget[@type='Animate']", mode="widget_defs" { - param "hmi_element"; - |, -} diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_animaterotation.ysl2 --- a/svghmi/widget_animaterotation.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_animaterotation.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_animaterotation.ysl2 -template "widget[@type='AnimateRotation']", mode="widget_class"{ +widget_class("AnimateRotation") { || - class AnimateRotationWidget extends Widget{ frequency = 5; speed = 0; widget_center = undefined; @@ -40,12 +39,11 @@ let widget_pos = this.element.getBBox(); this.widget_center = [(widget_pos.x+widget_pos.width/2), (widget_pos.y+widget_pos.height/2)]; } - } || } -template "widget[@type='AnimateRotation']", mode="widget_defs" { +widget_defs("AnimateRotation") { param "hmi_element"; |, } diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_back.ysl2 --- a/svghmi/widget_back.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_back.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_back.ysl2 -template "widget[@type='Back']", mode="widget_class" +widget_class("Back") || - class BackWidget extends Widget{ on_click(evt) { if(jump_history.length > 1){ jump_history.pop(); @@ -13,5 +12,4 @@ init() { this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); } - } || diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_button.ysl2 --- a/svghmi/widget_button.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_button.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -9,6 +9,8 @@ decl show(eltname); decl hmi_value(value); +gen_index_xhtml { + // State machine to drive HMI_BOOL on a potentially laggy connection const "_button_fsm" fsm { state "init" { @@ -104,9 +106,10 @@ | this.apply_hmi_value(0, «@value»); } -template "widget[@type='Button']", mode="widget_class"{ +} + +widget_class("Button"){ const "fsm","exsl:node-set($_button_fsm)"; - | class ButtonWidget extends Widget{ | frequency = 5; | display = "inactive"; @@ -145,11 +148,9 @@ | this.bound_onmouseup = this.onmouseup.bind(this); | this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); | } - | } } - -template "widget[@type='Button']", mode="widget_defs" { +widget_defs("Button") { param "hmi_element"; optional_labels("active inactive"); } diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_circularbar.ysl2 --- a/svghmi/widget_circularbar.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_circularbar.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_circularbar.ysl2 -template "widget[@type='CircularBar']", mode="widget_class"{ +widget_class("CircularBar") { || - class CircularBarWidget extends Widget{ frequency = 10; range = undefined; @@ -49,11 +48,10 @@ this.center = [cx, cy]; this.proportions = [rx, ry]; } - } || } -template "widget[@type='CircularBar']", mode="widget_defs" { +widget_defs("CircularBar") { param "hmi_element"; labels("path"); optional_labels("value min max"); diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_circularslider.ysl2 --- a/svghmi/widget_circularslider.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_circularslider.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_circuralslider.ysl2 -template "widget[@type='CircularSlider']", mode="widget_class" +widget_class("CircularSlider") || - class CircularSliderWidget extends Widget{ frequency = 5; range = undefined; circle = undefined; @@ -228,10 +227,9 @@ } } - } || -template "widget[@type='CircularSlider']", mode="widget_defs" { +widget_defs("CircularSlider") { param "hmi_element"; labels("handle range"); optional_labels("value min max setpoint"); diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_customhtml.ysl2 --- a/svghmi/widget_customhtml.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_customhtml.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_customhtml.ysl2 -template "widget[@type='CustomHtml']", mode="widget_class"{ +widget_class("CustomHtml"){ || - class CustomHtmlWidget extends Widget{ frequency = 5; widget_size = undefined; @@ -21,13 +20,11 @@ this.code_elt.textContent+ ' '; } - } || } -template "widget[@type='CustomHtml']", mode="widget_defs" { +widget_defs("CustomHtml") { param "hmi_element"; labels("container code"); - |, } diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_display.ysl2 --- a/svghmi/widget_display.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_display.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,18 +1,16 @@ // widget_display.ysl2 -template "widget[@type='Display']", mode="widget_class" +widget_class("Display") || - class DisplayWidget extends Widget{ frequency = 5; dispatch(value, oldval, index) { this.fields[index] = value; this.request_animate(); } - } || -template "widget[@type='Display']", mode="widget_defs" { +widget_defs("Display") { param "hmi_element"; const "format" optional_labels("format"); diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_dropdown.ysl2 --- a/svghmi/widget_dropdown.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_dropdown.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,11 +1,7 @@ // widget_dropdown.ysl2 -template "widget[@type='DropDown']", mode="widget_class"{ +widget_class("DropDown") { || - function numb_event(e) { - e.stopPropagation(); - } - class DropDownWidget extends Widget{ dispatch(value) { if(!this.opened) this.set_selection(value); } @@ -119,8 +115,8 @@ } close(){ // Stop hogging all click events - svg_root.removeEventListener("pointerdown", numb_event, true); - svg_root.removeEventListener("pointerup", numb_event, true); + svg_root.removeEventListener("pointerdown", this.numb_event, true); + svg_root.removeEventListener("pointerup", this.numb_event, true); svg_root.removeEventListener("click", this.bound_close_on_click_elsewhere, true); // Restore position and sixe of widget elements this.reset_text(); @@ -237,6 +233,9 @@ c++; } } + numb_event(e) { + e.stopPropagation(); + } open(){ let length = this.content.length; // systematically reset text, to strip eventual whitespace spans @@ -264,8 +263,8 @@ // Rise widget to top by moving it to last position among siblings this.element.parentNode.appendChild(this.element.parentNode.removeChild(this.element)); // disable interaction with background - svg_root.addEventListener("pointerdown", numb_event, true); - svg_root.addEventListener("pointerup", numb_event, true); + svg_root.addEventListener("pointerdown", this.numb_event, true); + svg_root.addEventListener("pointerup", this.numb_event, true); svg_root.addEventListener("click", this.bound_close_on_click_elsewhere, true); this.highlight_selection(); @@ -329,11 +328,10 @@ // b.width.baseVal.value = 2 * lmargin + m.width; b.height.baseVal.value = 2 * tmargin + m.height; } - } || } -template "widget[@type='DropDown']", mode="widget_defs" { +widget_defs("DropDown") { param "hmi_element"; labels("text box button highlight"); // It is assumed that list content conforms to Array interface. diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_foreach.ysl2 --- a/svghmi/widget_foreach.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_foreach.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,5 +1,5 @@ -template "widget[@type='ForEach']", mode="widget_defs" { +widget_defs("ForEach") { param "hmi_element"; if "count(path) != 1" error > ForEach widget «$hmi_element/@id» must have one HMI path given. @@ -49,9 +49,8 @@ | item_offset: 0, } -template "widget[@type='ForEach']", mode="widget_class" +widget_class("ForEach") || -class ForEachWidget extends Widget{ unsub_items(){ for(let item of this.items){ @@ -117,6 +116,5 @@ jumps_need_update = true; requestHMIAnimation(); } -} || diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_input.ysl2 --- a/svghmi/widget_input.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_input.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,44 +1,43 @@ // widget_input.ysl2 -template "widget[@type='Input']", mode="widget_class"{ -|| - class InputWidget extends Widget{ - on_op_click(opstr) { - this.change_hmi_value(0, opstr); - } - edit_callback(new_val) { - this.apply_hmi_value(0, new_val); - } - - is_inhibited = false; - alert(msg){ - this.is_inhibited = true; - this.display = msg; - setTimeout(() => this.stopalert(), 1000); - this.request_animate(); - } - - stopalert(){ - this.is_inhibited = false; - this.display = this.last_value; - this.request_animate(); - } - - overshot(new_val, max) { - this.alert("max"); - } - - undershot(new_val, min) { - this.alert("min"); - } - } -|| + } -template "widget[@type='Input']", mode="widget_defs" { - param "hmi_element"; +widget_class("Input") +|| + on_op_click(opstr) { + this.change_hmi_value(0, opstr); + } + edit_callback(new_val) { + this.apply_hmi_value(0, new_val); + } + + is_inhibited = false; + alert(msg){ + this.is_inhibited = true; + this.display = msg; + setTimeout(() => this.stopalert(), 1000); + this.request_animate(); + } + + stopalert(){ + this.is_inhibited = false; + this.display = this.last_value; + this.request_animate(); + } + + overshot(new_val, max) { + this.alert("max"); + } + + undershot(new_val, min) { + this.alert("min"); + } +|| + +widget_defs("Input") { const "value_elt" optional_labels("value"); const "have_value","string-length($value_elt)>0"; diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_jsontable.ysl2 --- a/svghmi/widget_jsontable.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_jsontable.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_jsontable.ysl2 -template "widget[@type='JsonTable']", mode="widget_class" +widget_class("JsonTable") || - class JsonTableWidget extends Widget{ // arbitrary defaults to avoid missing entries in query cache = [0,0,0]; init_common() { @@ -83,9 +82,10 @@ // on_click(evt, ...options) { // this.do_http_request(...options); // } - } || +gen_index_xhtml { + template "svg:*", mode="json_table_elt_render" { error > JsonTable Widget can't contain element of type «local-name()». } @@ -259,7 +259,9 @@ | } } -template "widget[@type='JsonTable']", mode="widget_defs" { +} + +widget_defs("JsonTable") { param "hmi_element"; labels("data"); const "data_elt", "$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']"; diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_jump.ysl2 --- a/svghmi/widget_jump.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_jump.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,9 +1,7 @@ // widget_jump.ysl2 -template "widget[@type='Jump']", mode="widget_class"{ +widget_class("Jump") { || - class JumpWidget extends Widget{ - activable = false; active = false; disabled = false; @@ -58,11 +56,10 @@ this.update_activity(); } } - } || } -template "widget[@type='Jump']", mode="widget_defs" { +widget_defs("Jump") { param "hmi_element"; const "activity" optional_labels("active inactive"); const "have_activity","string-length($activity)>0"; @@ -90,7 +87,7 @@ | }, } -template "widget[@type='Jump']", mode="per_page_widget_template"{ +widget_page("Jump"){ param "page_desc"; /* check that given path is compatible with page's reference path */ if "path" { diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_keypad.ysl2 --- a/svghmi/widget_keypad.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_keypad.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -13,10 +13,8 @@ | } } -template "widget[@type='Keypad']", mode="widget_class" +widget_class("Keypad") || - class KeypadWidget extends Widget{ - on_key_click(symbols) { var syms = symbols.split(" "); this.shift |= this.caps; @@ -110,10 +108,9 @@ (this.caps?this.activate_activable:this.inactivate_activable)(this.CapsLock_sub); } } - } || -template "widget[@type='Keypad']", mode="widget_defs" { +widget_defs("Keypad") { param "hmi_element"; labels("Esc Enter BackSpace Keys Info Value"); optional_labels("Sign Space NumDot"); diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_list.ysl2 --- a/svghmi/widget_list.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_list.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,6 +1,6 @@ // widget_list.ysl2 -template "widget[@type='List']", mode="widget_defs" { +widget_defs("List") { param "hmi_element"; | items: { foreach "$hmi_element/*[@inkscape:label]" { @@ -9,7 +9,7 @@ | }, } -template "widget[@type='TextStyleList']", mode="widget_defs" { +widget_defs("TextStyleList") { param "hmi_element"; | styles: { foreach "$hmi_element/*[@inkscape:label]" { diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_meter.ysl2 --- a/svghmi/widget_meter.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_meter.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_meter.ysl2 -template "widget[@type='Meter']", mode="widget_class"{ +widget_class("Metter"){ || - class MeterWidget extends Widget{ frequency = 10; origin = undefined; range = undefined; @@ -29,12 +28,10 @@ this.range = [min, max, this.range_elt.getTotalLength()] this.origin = this.needle_elt.getPointAtLength(0); } - - } || } -template "widget[@type='Meter']", mode="widget_defs" { +widget_defs("Meter") { param "hmi_element"; labels("needle range"); optional_labels("value min max"); diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_multistate.ysl2 --- a/svghmi/widget_multistate.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_multistate.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_multistate.ysl2 -template "widget[@type='MultiState']", mode="widget_class" +widget_class("MultiState") || - class MultiStateWidget extends Widget{ frequency = 5; state = 0; dispatch(value) { @@ -41,10 +40,9 @@ init() { this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); } - } || -template "widget[@type='MultiState']", mode="widget_defs" { +widget_defs("MultiState") { param "hmi_element"; | choices: [ const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+|false|true)(#.*)?$'"!; diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_scrollbar.ysl2 --- a/svghmi/widget_scrollbar.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_scrollbar.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_scrollbar.ysl2 -template "widget[@type='ScrollBar']", mode="widget_class"{ +widget_class("ScrollBar") { || - class ScrollBarWidget extends Widget{ frequency = 10; position = undefined; range = undefined; @@ -89,11 +88,10 @@ this.dragpos += movement * units / pixels; this.apply_position(this.dragpos); } - } || } -template "widget[@type='ScrollBar']", mode="widget_defs" { +widget_defs("ScrollBar") { param "hmi_element"; labels("cursor range"); diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_slider.ysl2 --- a/svghmi/widget_slider.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_slider.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,6 +1,6 @@ // widget_slider.ysl2 -template "widget[@type='Slider']", mode="widget_class" +widget_class("Slider") || class SliderWidget extends Widget{ frequency = 5; @@ -339,9 +339,8 @@ } || -template "widget[@type='Slider']", mode="widget_defs" { +widget_defs("Slider") { param "hmi_element"; labels("handle range"); optional_labels("value min max setpoint"); - |, } diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_switch.ysl2 --- a/svghmi/widget_switch.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_switch.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,8 +1,7 @@ // widget_switch.ysl2 -template "widget[@type='Switch']", mode="widget_class" +widget_class("Switch") || - class SwitchWidget extends Widget{ frequency = 5; dispatch(value) { for(let choice of this.choices){ @@ -13,10 +12,9 @@ } } } - } || -template "widget[@type='Switch']", mode="widget_defs" { +widget_defs("Switch") { param "hmi_element"; | choices: [ const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+|false|true)(#.*)?$'"!; diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widget_tooglebutton.ysl2 --- a/svghmi/widget_tooglebutton.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widget_tooglebutton.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -1,9 +1,8 @@ // widget_tooglebutton.ysl2 -template "widget[@type='ToggleButton']", mode="widget_class"{ +widget_class("ToggleButton") { || - class ToggleButtonWidget extends Widget{ frequency = 5; state = 0; active_style = undefined; @@ -41,11 +40,10 @@ this.activate(false); this.element.onclick = (evt) => this.on_click(evt); } - } || } -template "widget[@type='ToggleButton']", mode="widget_defs" { +widget_defs("ToggleButton") { param "hmi_element"; optional_labels("active inactive"); } diff -r 5243c2a2f7f8 -r 7bdb766c2a4d svghmi/widgets_common.ysl2 --- a/svghmi/widgets_common.ysl2 Thu Apr 15 09:15:23 2021 +0200 +++ b/svghmi/widgets_common.ysl2 Sun May 02 23:01:08 2021 +0200 @@ -21,6 +21,26 @@ } }; +in xsl decl widget_class(%name, *clsname="%nameWidget", match="widget[@type='%name']", mode="widget_class") alias template { + | class `text **clsname` extends Widget{ + content; + | } +}; + +in xsl decl widget_defs(%name, match="widget[@type='%name']", mode="widget_defs") alias template { + param "hmi_element"; + content; +}; + +in xsl decl widget_page(%name, match="widget[@type='%name']", mode="widget_page") alias template { + param "page_desc"; + content; +}; + +decl gen_index_xhtml alias - { + content; +}; + template "svg:*", mode="hmi_widgets" { const "widget", "func:widget(@id)"; const "eltid","@id";