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.
--- 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" {
--- 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";
- |,
-}
--- 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";
|,
}
--- 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)");
}
- }
||
--- 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");
}
--- 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");
--- 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");
--- 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+
' </foreignObject>';
}
- }
||
}
-template "widget[@type='CustomHtml']", mode="widget_defs" {
+widget_defs("CustomHtml") {
param "hmi_element";
labels("container code");
- |,
}
--- 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");
--- 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.
--- 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();
}
-}
||
--- 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";
--- 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']";
--- 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" {
--- 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");
--- 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]" {
--- 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");
--- 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)(#.*)?$'"!;
--- 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");
--- 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");
- |,
}
--- 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)(#.*)?$'"!;
--- 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");
}
--- 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";