Merged SVGHMI branches svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Tue, 18 May 2021 09:28:44 +0200
branchsvghmi
changeset 3242 f037e901a17c
parent 3241 fe945f1f48b7 (diff)
parent 3240 5f756332ada1 (current diff)
child 3243 92cc21f88bf8
Merged SVGHMI branches
svghmi/gen_index_xhtml.xslt
svghmi/widget_jump.ysl2
--- a/svghmi/Makefile	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/Makefile	Tue May 18 09:28:44 2021 +0200
@@ -11,7 +11,7 @@
 
 yml2path ?= $(abspath ../../yml2)
 
-ysl2files := gen_index_xhtml.ysl2 gen_dnd_widget_svg.ysl2
+ysl2files := gen_index_xhtml.ysl2 gen_dnd_widget_svg.ysl2 analyse_widget.ysl2
 ysl2includes := $(filter-out $(ysl2files), $(wildcard *.ysl2))
 xsltfiles := $(patsubst %.ysl2, %.xslt, $(ysl2files))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/analyse_widget.xslt	Tue May 18 09:28:44 2021 +0200
@@ -0,0 +1,673 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:svg="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn svg inkscape">
+  <xsl:output method="xml"/>
+  <xsl:variable name="indexed_hmitree" select="/.."/>
+  <xsl:variable name="pathregex" select="'^([^\[,]+)(\[[^\]]+\])?([\d,]*)$'"/>
+  <xsl:template mode="parselabel" match="*">
+    <xsl:variable name="label" select="@inkscape:label"/>
+    <xsl:variable name="id" select="@id"/>
+    <xsl:variable name="description" select="substring-after($label,'HMI:')"/>
+    <xsl:variable name="_args" select="substring-before($description,'@')"/>
+    <xsl:variable name="args">
+      <xsl:choose>
+        <xsl:when test="$_args">
+          <xsl:value-of select="$_args"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$description"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="_type" select="substring-before($args,':')"/>
+    <xsl:variable name="type">
+      <xsl:choose>
+        <xsl:when test="$_type">
+          <xsl:value-of select="$_type"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$args"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$type">
+      <widget>
+        <xsl:attribute name="id">
+          <xsl:value-of select="$id"/>
+        </xsl:attribute>
+        <xsl:attribute name="type">
+          <xsl:value-of select="$type"/>
+        </xsl:attribute>
+        <xsl:for-each select="str:split(substring-after($args, ':'), ':')">
+          <arg>
+            <xsl:attribute name="value">
+              <xsl:value-of select="."/>
+            </xsl:attribute>
+          </arg>
+        </xsl:for-each>
+        <xsl:variable name="paths" select="substring-after($description,'@')"/>
+        <xsl:for-each select="str:split($paths, '@')">
+          <xsl:if test="string-length(.) &gt; 0">
+            <path>
+              <xsl:variable name="path_match" select="regexp:match(.,$pathregex)"/>
+              <xsl:variable name="pathminmax" select="str:split($path_match[4],',')"/>
+              <xsl:variable name="path" select="$path_match[2]"/>
+              <xsl:variable name="path_accepts" select="$path_match[3]"/>
+              <xsl:variable name="pathminmaxcount" select="count($pathminmax)"/>
+              <xsl:attribute name="value">
+                <xsl:value-of select="$path"/>
+              </xsl:attribute>
+              <xsl:if test="string-length($path_accepts)">
+                <xsl:attribute name="accepts">
+                  <xsl:value-of select="$path_accepts"/>
+                </xsl:attribute>
+              </xsl:if>
+              <xsl:choose>
+                <xsl:when test="$pathminmaxcount = 2">
+                  <xsl:attribute name="min">
+                    <xsl:value-of select="$pathminmax[1]"/>
+                  </xsl:attribute>
+                  <xsl:attribute name="max">
+                    <xsl:value-of select="$pathminmax[2]"/>
+                  </xsl:attribute>
+                </xsl:when>
+                <xsl:when test="$pathminmaxcount = 1 or $pathminmaxcount &gt; 2">
+                  <xsl:message terminate="yes">
+                    <xsl:text>Widget id:</xsl:text>
+                    <xsl:value-of select="$id"/>
+                    <xsl:text> label:</xsl:text>
+                    <xsl:value-of select="$label"/>
+                    <xsl:text> has wrong syntax of path section </xsl:text>
+                    <xsl:value-of select="$pathminmax"/>
+                  </xsl:message>
+                </xsl:when>
+              </xsl:choose>
+              <xsl:if test="$indexed_hmitree">
+                <xsl:choose>
+                  <xsl:when test="regexp:test($path,'^\.[a-zA-Z0-9_]+$')">
+                    <xsl:attribute name="type">
+                      <xsl:text>PAGE_LOCAL</xsl:text>
+                    </xsl:attribute>
+                  </xsl:when>
+                  <xsl:when test="regexp:test($path,'^[a-zA-Z0-9_]+$')">
+                    <xsl:attribute name="type">
+                      <xsl:text>HMI_LOCAL</xsl:text>
+                    </xsl:attribute>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:variable name="item" select="$indexed_hmitree/*[@hmipath = $path]"/>
+                    <xsl:variable name="pathtype" select="local-name($item)"/>
+                    <xsl:if test="$pathminmaxcount = 3 and not($pathtype = 'HMI_INT' or $pathtype = 'HMI_REAL')">
+                      <xsl:message terminate="yes">
+                        <xsl:text>Widget id:</xsl:text>
+                        <xsl:value-of select="$id"/>
+                        <xsl:text> label:</xsl:text>
+                        <xsl:value-of select="$label"/>
+                        <xsl:text> path section </xsl:text>
+                        <xsl:value-of select="$pathminmax"/>
+                        <xsl:text> use min and max on non mumeric value</xsl:text>
+                      </xsl:message>
+                    </xsl:if>
+                    <xsl:if test="count($item) = 1">
+                      <xsl:attribute name="index">
+                        <xsl:value-of select="$item/@index"/>
+                      </xsl:attribute>
+                      <xsl:attribute name="type">
+                        <xsl:value-of select="$pathtype"/>
+                      </xsl:attribute>
+                    </xsl:if>
+                  </xsl:otherwise>
+                </xsl:choose>
+              </xsl:if>
+            </path>
+          </xsl:if>
+        </xsl:for-each>
+      </widget>
+    </xsl:if>
+  </xsl:template>
+  <xsl:template mode="genlabel" match="arg">
+    <xsl:text>:</xsl:text>
+    <xsl:value-of select="@value"/>
+  </xsl:template>
+  <xsl:template mode="genlabel" match="path">
+    <xsl:text>@</xsl:text>
+    <xsl:value-of select="@value"/>
+    <xsl:if test="string-length(@min)&gt;0 or string-length(@max)&gt;0">
+      <xsl:text>,</xsl:text>
+      <xsl:value-of select="@min"/>
+      <xsl:text>,</xsl:text>
+      <xsl:value-of select="@max"/>
+    </xsl:if>
+  </xsl:template>
+  <xsl:template mode="genlabel" match="widget">
+    <xsl:text>HMI:</xsl:text>
+    <xsl:value-of select="@type"/>
+    <xsl:apply-templates mode="genlabel" select="arg"/>
+    <xsl:apply-templates mode="genlabel" select="path"/>
+  </xsl:template>
+  <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
+  <xsl:template match="widget[@type='AnimateRotation']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>AnimateRotation - DEPRECATED, do not use.
+</xsl:text>
+      <xsl:text>Doesn't follow WYSIWYG principle, and forces user to add animateTransform tag in SVG (using inkscape XML editor for exemple)
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>AnimateRotation - DEPRECATED</xsl:text>
+    </shortdesc>
+    <path name="speed" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>speed</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Back']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Back widget brings focus back to previous page in history when clicked.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Jump to previous page</xsl:text>
+    </shortdesc>
+  </xsl:template>
+  <xsl:template match="widget[@type='Button']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Button widget takes one boolean variable path, and reflect current true
+</xsl:text>
+      <xsl:text>or false value by showing "active" or "inactive" labeled element
+</xsl:text>
+      <xsl:text>respectively. Pressing and releasing button changes variable to true and
+</xsl:text>
+      <xsl:text>false respectively. Potential inconsistency caused by quick consecutive
+</xsl:text>
+      <xsl:text>presses on the button is mitigated by using a state machine that wait for
+</xsl:text>
+      <xsl:text>previous state change to be reflected on variable before applying next one.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Push button reflecting consistently given boolean variable</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_BOOL">
+      <xsl:text>Boolean variable</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='CircularBar']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>CircularBar widget changes the end angle of a "path" labeled arc according
+</xsl:text>
+      <xsl:text>to value of the single accepted variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "min" a "max" labeled texts are provided, then they are used as
+</xsl:text>
+      <xsl:text>respective minimum and maximum value. Otherwise, value is expected to be
+</xsl:text>
+      <xsl:text>in between 0 and 100.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "value" labeled text is found, then its content is replaced by value.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Change end angle of Inkscape's arc</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>Value to display</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='CircularSlider']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>CircularSlider - DEPRECATED, to be replaced by PathSlider
+</xsl:text>
+      <xsl:text>This widget moves "handle" labeled group along "range" labeled
+</xsl:text>
+      <xsl:text>arc, according to value of the single accepted variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "min" a "max" labeled texts are provided, or if first and second
+</xsl:text>
+      <xsl:text>argument are given, then they are used as respective minimum and maximum
+</xsl:text>
+      <xsl:text>value. Otherwise, value is expected to be in between 0 and 100.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "value" labeled text is found, then its content is replaced by value.
+</xsl:text>
+      <xsl:text>During drag, "setpoint" labeled group is moved to position defined by user
+</xsl:text>
+      <xsl:text>while "handle" reflects current value from variable.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>CircularSlider - DEPRECATED</xsl:text>
+    </shortdesc>
+    <arg name="min" count="optional" accepts="int,real">
+      <xsl:text>minimum value</xsl:text>
+    </arg>
+    <arg name="min" count="optional" accepts="int,real">
+      <xsl:text>maximum value</xsl:text>
+    </arg>
+    <path name="value" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>Value to display</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='CustomHtml']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>CustomHtml widget allows insertion of HTML code in a svg:foreignObject.
+</xsl:text>
+      <xsl:text>Widget content is replaced by foreignObject. HTML code is obtained from
+</xsl:text>
+      <xsl:text>"code" labeled text content. HTML insert position and size is given with
+</xsl:text>
+      <xsl:text>"container" labeled element.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Custom HTML insert</xsl:text>
+    </shortdesc>
+  </xsl:template>
+  <xsl:template match="widget[@type='Display']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>If Display widget is a svg:text element, then text content is replaced by
+</xsl:text>
+      <xsl:text>value of given variables, space separated.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Otherwise, if Display widget is a group containing a svg:text element
+</xsl:text>
+      <xsl:text>labelled "format", then text content is replaced by printf-like formated
+</xsl:text>
+      <xsl:text>string. In other words, if "format" labeled text is "%d %s %f", then 3
+</xsl:text>
+      <xsl:text>variables paths are expected : HMI_IN, HMI_STRING and HMI_REAL.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>In case Display widget is a svg::text element, it is also possible to give
+</xsl:text>
+      <xsl:text>format string as first argument.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Printf-like formated text display </xsl:text>
+    </shortdesc>
+    <arg name="format" count="optional" accepts="string">
+      <xsl:text>printf-like format string when not given as svg:text</xsl:text>
+    </arg>
+    <path name="fields" count="many" accepts="HMI_INT,HMI_REAL,HMI_STRING,HMI_BOOL">
+      <xsl:text>variables to be displayed</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='DropDown']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>DropDown widget let user select an entry in a list of texts, given as
+</xsl:text>
+      <xsl:text>arguments. Single variable path is index of selection.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>It needs "text" (svg:text), "box" (svg:rect), "button" (svg:*),
+</xsl:text>
+      <xsl:text>and "highlight" (svg:rect) labeled elements.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>When user clicks on "button", "text" is duplicated to display enties in the
+</xsl:text>
+      <xsl:text>limit of available space in page, and "box" is extended to contain all
+</xsl:text>
+      <xsl:text>texts. "highlight" is moved over pre-selected entry.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>When only one argument is given, and argment contains "#langs" then list of
+</xsl:text>
+      <xsl:text>texts is automatically set to the list of human-readable languages supported
+</xsl:text>
+      <xsl:text>by this HMI. 
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Let user select text entry in a drop-down menu</xsl:text>
+    </shortdesc>
+    <arg name="entries" count="many" accepts="string">
+      <xsl:text>drop-down menu entries</xsl:text>
+    </arg>
+    <path name="selection" accepts="HMI_INT">
+      <xsl:text>selection index</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='ForEach']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>ForEach widget is used to span a small set of widget over a larger set of
+</xsl:text>
+      <xsl:text>repeated HMI_NODEs. 
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Idea is somewhat similar to relative page, but it all happens inside the
+</xsl:text>
+      <xsl:text>ForEach widget, no page involved.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Together with relative Jump widgets it can be used to build a menu to reach
+</xsl:text>
+      <xsl:text>relative pages covering many identical HMI_NODES siblings.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>ForEach widget takes a HMI_CLASS name as argument and a HMI_NODE path as
+</xsl:text>
+      <xsl:text>variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Direct sub-elements can be either groups of widget to be spanned, labeled
+</xsl:text>
+      <xsl:text>"ClassName:offset", or buttons to control the spanning, labeled
+</xsl:text>
+      <xsl:text>"ClassName:+/-number".
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>span widgets over a set of repeated HMI_NODEs</xsl:text>
+    </shortdesc>
+    <arg name="class_name" accepts="string">
+      <xsl:text>HMI_CLASS name</xsl:text>
+    </arg>
+    <path name="root" accepts="HMI_NODE">
+      <xsl:text> where to find HMI_NODEs whose HMI_CLASS is class_name</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Input']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Input widget takes one variable path, and displays current value in
+</xsl:text>
+      <xsl:text>optional "value" labeled sub-element. 
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Click on optional "edit" labeled element opens keypad to edit value.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Operation on current value is performed when click on sub-elements with
+</xsl:text>
+      <xsl:text>label starting with '=', '+' or '-' sign. Value after sign is used as
+</xsl:text>
+      <xsl:text>operand.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Input field with predefined operation buttons</xsl:text>
+    </shortdesc>
+    <arg name="format" accepts="string">
+      <xsl:text>optional printf-like format </xsl:text>
+    </arg>
+    <path name="edit" accepts="HMI_INT, HMI_REAL, HMI_STRING">
+      <xsl:text>single variable to edit</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='JsonTable']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Send given variables as POST to http URL argument, spread returned JSON in
+</xsl:text>
+      <xsl:text>SVG sub-elements of "data" labeled element.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Documentation to be written. see svbghmi exemple.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Http POST variables, spread JSON back</xsl:text>
+    </shortdesc>
+    <arg name="url" accepts="string">
+      <xsl:text> </xsl:text>
+    </arg>
+    <path name="edit" accepts="HMI_INT, HMI_REAL, HMI_STRING">
+      <xsl:text>single variable to edit</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Jump']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Jump widget brings focus to a different page. Mandatory single argument
+</xsl:text>
+      <xsl:text>gives name of the page.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Optional single path is used as new reference when jumping to a relative
+</xsl:text>
+      <xsl:text>page, it must point to a HMI_NODE.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>"active"+"inactive" labeled elements can be provided and reflect current
+</xsl:text>
+      <xsl:text>page being shown.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>"disabled" labeled element, if provided, is shown instead of "active" or
+</xsl:text>
+      <xsl:text>"inactive" widget when pointed HMI_NODE is null.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Jump to given page</xsl:text>
+    </shortdesc>
+    <arg name="page" accepts="string">
+      <xsl:text>name of page to jump to</xsl:text>
+    </arg>
+    <path name="reference" count="optional" accepts="HMI_NODE">
+      <xsl:text>reference for relative jump</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Keypad']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Keypad - to be written
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Keypad </xsl:text>
+    </shortdesc>
+    <arg name="supported_types" accepts="string">
+      <xsl:text>keypad can input those types </xsl:text>
+    </arg>
+  </xsl:template>
+  <xsl:template match="widget[@type='List']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+  </xsl:template>
+  <xsl:template match="widget[@type='Meter']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Meter widget moves the end of "needle" labeled path along "range" labeled
+</xsl:text>
+      <xsl:text>path, according to value of the single accepted variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Needle is reduced to a single segment. If "min" a "max" labeled texts
+</xsl:text>
+      <xsl:text>are provided, or if first and second argument are given, then they are used
+</xsl:text>
+      <xsl:text>as respective minimum and maximum value. Otherwise, value is expected to be
+</xsl:text>
+      <xsl:text>in between 0 and 100.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "value" labeled text is found, then its content is replaced by value.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Moves "needle" along "range"</xsl:text>
+    </shortdesc>
+    <arg name="min" count="optional" accepts="int,real">
+      <xsl:text>minimum value</xsl:text>
+    </arg>
+    <arg name="max" count="optional" accepts="int,real">
+      <xsl:text>maximum value</xsl:text>
+    </arg>
+    <path name="value" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>Value to display</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='ScrollBar']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>ScrollBar - documentation to be written
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>ScrollBar</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT">
+      <xsl:text>value</xsl:text>
+    </path>
+    <path name="range" accepts="HMI_INT">
+      <xsl:text>range</xsl:text>
+    </path>
+    <path name="visible" accepts="HMI_INT">
+      <xsl:text>visible</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Slider']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Slider - DEPRECATED - use ScrollBar or PathSlider instead
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Slider - DEPRECATED - use ScrollBar instead</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT">
+      <xsl:text>value</xsl:text>
+    </path>
+    <path name="range" accepts="HMI_INT">
+      <xsl:text>range</xsl:text>
+    </path>
+    <path name="visible" accepts="HMI_INT">
+      <xsl:text>visible</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Switch']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Switch widget hides all subelements whose label do not match given
+</xsl:text>
+      <xsl:text>variable current value representation. For exemple if given variable type
+</xsl:text>
+      <xsl:text>is HMI_INT and value is 1, then elements with label '1' will be displayed.
+</xsl:text>
+      <xsl:text>Label can have comments, so '1#some comment' would also match. If matching
+</xsl:text>
+      <xsl:text>variable of type HMI_STRING, then double quotes must be used. For exemple,
+</xsl:text>
+      <xsl:text>'"hello"' or '"hello"#another comment' match HMI_STRING 'hello'.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Show elements whose label match value.</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT,HMI_STRING">
+      <xsl:text>value to compare to labels</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='ToggleButton']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Button widget takes one boolean variable path, and reflect current true
+</xsl:text>
+      <xsl:text>or false value by showing "active" or "inactive" labeled element
+</xsl:text>
+      <xsl:text>respectively. Clicking or touching button toggles variable.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Toggle button reflecting given boolean variable</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_BOOL">
+      <xsl:text>Boolean variable</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template mode="document" match="@* | node()">
+    <xsl:copy>
+      <xsl:apply-templates mode="document" select="@* | node()"/>
+    </xsl:copy>
+  </xsl:template>
+  <xsl:template mode="document" match="widget">
+    <xsl:copy>
+      <xsl:apply-templates mode="document" select="@* | node()"/>
+      <defs>
+        <xsl:apply-templates mode="widget_desc" select="."/>
+      </defs>
+    </xsl:copy>
+  </xsl:template>
+  <xsl:template match="/">
+    <xsl:variable name="widgets">
+      <xsl:apply-templates mode="parselabel" select="$hmi_elements"/>
+    </xsl:variable>
+    <xsl:variable name="widget_ns" select="exsl:node-set($widgets)"/>
+    <widgets>
+      <xsl:apply-templates mode="document" select="$widget_ns"/>
+    </widgets>
+  </xsl:template>
+</xsl:stylesheet>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/analyse_widget.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -0,0 +1,49 @@
+include yslt_noindent.yml2
+
+in xsl decl widget_desc(%name, match="widget[@type='%name']", mode="widget_desc") alias template {
+    type > «@type»
+    content;
+};
+
+decl nothing alias - ;
+decl widget_class(%name) alias - {nothing};
+decl widget_defs(%name) alias - {nothing};
+decl widget_page(%name) alias - {nothing};
+decl gen_index_xhtml alias - {nothing};
+decl emit(*name) alias - {nothing};
+
+istylesheet
+            /* From Inkscape */
+            xmlns:svg="http://www.w3.org/2000/svg"
+            xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+
+            extension-element-prefixes="ns func exsl regexp str dyn"
+            exclude-result-prefixes="ns func exsl regexp str dyn svg inkscape" {
+
+    const "indexed_hmitree", "/.."; // compatibility with parse_labels.ysl2
+    include parse_labels.ysl2
+
+    const "hmi_elements", "//svg:*[starts-with(@inkscape:label, 'HMI:')]";
+
+    include widget_*.ysl2
+
+    template "@* | node()", mode="document" {
+        xsl:copy apply "@* | node()", mode="document";
+    }
+
+    template "widget", mode="document" {
+        xsl:copy {
+            apply "@* | node()", mode="document";
+            defs apply ".", mode="widget_desc";
+        }
+    }
+
+    template "/" {
+        const "widgets"
+            apply "$hmi_elements", mode="parselabel";
+        const "widget_ns", "exsl:node-set($widgets)";
+        widgets 
+            apply "$widget_ns", mode="document";
+    }
+
+}
--- a/svghmi/detachable_pages.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/detachable_pages.ysl2	Tue May 18 09:28:44 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/gen_dnd_widget_svg.xslt	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/gen_dnd_widget_svg.xslt	Tue May 18 09:28:44 2021 +0200
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:ns="beremiz" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn">
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:ns="beremiz" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn">
   <xsl:output method="xml"/>
   <xsl:param name="hmi_path"/>
-  <xsl:variable name="svg" select="/svg:svg"/>
   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
   <xsl:variable name="subhmitree" select="ns:GetSubHMITree()"/>
   <xsl:variable name="indexed_hmitree" select="/.."/>
+  <xsl:variable name="pathregex" select="'^([^\[,]+)(\[[^\]]+\])?([\d,]*)$'"/>
   <xsl:template mode="parselabel" match="*">
     <xsl:variable name="label" select="@inkscape:label"/>
     <xsl:variable name="id" select="@id"/>
@@ -51,22 +51,29 @@
         <xsl:for-each select="str:split($paths, '@')">
           <xsl:if test="string-length(.) &gt; 0">
             <path>
-              <xsl:variable name="pathminmax" select="str:split(.,',')"/>
-              <xsl:variable name="path" select="$pathminmax[1]"/>
+              <xsl:variable name="path_match" select="regexp:match(.,$pathregex)"/>
+              <xsl:variable name="pathminmax" select="str:split($path_match[4],',')"/>
+              <xsl:variable name="path" select="$path_match[2]"/>
+              <xsl:variable name="path_accepts" select="$path_match[3]"/>
               <xsl:variable name="pathminmaxcount" select="count($pathminmax)"/>
               <xsl:attribute name="value">
                 <xsl:value-of select="$path"/>
               </xsl:attribute>
+              <xsl:if test="string-length($path_accepts)">
+                <xsl:attribute name="accepts">
+                  <xsl:value-of select="$path_accepts"/>
+                </xsl:attribute>
+              </xsl:if>
               <xsl:choose>
-                <xsl:when test="$pathminmaxcount = 3">
+                <xsl:when test="$pathminmaxcount = 2">
                   <xsl:attribute name="min">
+                    <xsl:value-of select="$pathminmax[1]"/>
+                  </xsl:attribute>
+                  <xsl:attribute name="max">
                     <xsl:value-of select="$pathminmax[2]"/>
                   </xsl:attribute>
-                  <xsl:attribute name="max">
-                    <xsl:value-of select="$pathminmax[3]"/>
-                  </xsl:attribute>
                 </xsl:when>
-                <xsl:when test="$pathminmaxcount = 2">
+                <xsl:when test="$pathminmaxcount = 1 or $pathminmaxcount &gt; 2">
                   <xsl:message terminate="yes">
                     <xsl:text>Widget id:</xsl:text>
                     <xsl:value-of select="$id"/>
@@ -128,9 +135,9 @@
     <xsl:text>@</xsl:text>
     <xsl:value-of select="@value"/>
     <xsl:if test="string-length(@min)&gt;0 or string-length(@max)&gt;0">
-      <xsl:text>:</xsl:text>
+      <xsl:text>,</xsl:text>
       <xsl:value-of select="@min"/>
-      <xsl:text>:</xsl:text>
+      <xsl:text>,</xsl:text>
       <xsl:value-of select="@max"/>
     </xsl:if>
   </xsl:template>
@@ -214,7 +221,7 @@
         <xsl:value-of select="$svg_widget_type"/>
       </msg>
     </xsl:variable>
-    <xsl:value-of select="ns:GiveDetails($testmsg)"/>
+    <xsl:value-of select="ns:PassMessage($testmsg)"/>
     <xsl:apply-templates mode="inline_svg" select="/"/>
   </xsl:template>
 </xsl:stylesheet>
--- a/svghmi/gen_dnd_widget_svg.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/gen_dnd_widget_svg.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -11,7 +11,6 @@
             xmlns:xlink="http://www.w3.org/1999/xlink"
             xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
             xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-            xmlns:xhtml="http://www.w3.org/1999/xhtml"
 
             /* Namespace to invoke python code */
             xmlns:ns="beremiz"
@@ -20,7 +19,6 @@
             exclude-result-prefixes="ns func exsl regexp str dyn" {
 
     param "hmi_path";
-    const "svg", "/svg:svg";
     const "hmi_elements", "//svg:*[starts-with(@inkscape:label, 'HMI:')]";
     const "subhmitree", "ns:GetSubHMITree()";
 
@@ -84,7 +82,7 @@
             msg value "$svg_widget_type";
         }
 
-        value "ns:GiveDetails($testmsg)";
+        value "ns:PassMessage($testmsg)";
 
         apply "/", mode="inline_svg";
     }
--- a/svghmi/gen_index_xhtml.xslt	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/gen_index_xhtml.xslt	Tue May 18 09:28:44 2021 +0200
@@ -136,6 +136,7 @@
       </xsl:with-param>
     </xsl:apply-templates>
   </xsl:template>
+  <xsl:variable name="pathregex" select="'^([^\[,]+)(\[[^\]]+\])?([\d,]*)$'"/>
   <xsl:template mode="parselabel" match="*">
     <xsl:variable name="label" select="@inkscape:label"/>
     <xsl:variable name="id" select="@id"/>
@@ -181,22 +182,29 @@
         <xsl:for-each select="str:split($paths, '@')">
           <xsl:if test="string-length(.) &gt; 0">
             <path>
-              <xsl:variable name="pathminmax" select="str:split(.,',')"/>
-              <xsl:variable name="path" select="$pathminmax[1]"/>
+              <xsl:variable name="path_match" select="regexp:match(.,$pathregex)"/>
+              <xsl:variable name="pathminmax" select="str:split($path_match[4],',')"/>
+              <xsl:variable name="path" select="$path_match[2]"/>
+              <xsl:variable name="path_accepts" select="$path_match[3]"/>
               <xsl:variable name="pathminmaxcount" select="count($pathminmax)"/>
               <xsl:attribute name="value">
                 <xsl:value-of select="$path"/>
               </xsl:attribute>
+              <xsl:if test="string-length($path_accepts)">
+                <xsl:attribute name="accepts">
+                  <xsl:value-of select="$path_accepts"/>
+                </xsl:attribute>
+              </xsl:if>
               <xsl:choose>
-                <xsl:when test="$pathminmaxcount = 3">
+                <xsl:when test="$pathminmaxcount = 2">
                   <xsl:attribute name="min">
+                    <xsl:value-of select="$pathminmax[1]"/>
+                  </xsl:attribute>
+                  <xsl:attribute name="max">
                     <xsl:value-of select="$pathminmax[2]"/>
                   </xsl:attribute>
-                  <xsl:attribute name="max">
-                    <xsl:value-of select="$pathminmax[3]"/>
-                  </xsl:attribute>
                 </xsl:when>
-                <xsl:when test="$pathminmaxcount = 2">
+                <xsl:when test="$pathminmaxcount = 1 or $pathminmaxcount &gt; 2">
                   <xsl:message terminate="yes">
                     <xsl:text>Widget id:</xsl:text>
                     <xsl:value-of select="$id"/>
@@ -677,7 +685,7 @@
     </xsl:for-each>
     <xsl:text>    }
 </xsl:text>
-    <xsl:apply-templates mode="per_page_widget_template" select="$parsed_widgets/widget[@id = $all_page_widgets/@id]">
+    <xsl:apply-templates mode="widget_page" select="$parsed_widgets/widget[@id = $all_page_widgets/@id]">
       <xsl:with-param name="page_desc" select="$desc"/>
     </xsl:apply-templates>
     <xsl:text>  }</xsl:text>
@@ -708,7 +716,7 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="per_page_widget_template" match="*"/>
+  <xsl:template mode="widget_page" match="*"/>
   <debug:detachable-pages/>
   <xsl:template match="debug:detachable-pages">
     <xsl:text>
@@ -1870,8 +1878,10 @@
       </xsl:otherwise>
     </xsl:choose>
   </func:function>
-  <xsl:template mode="widget_class" match="widget[@type='Animate']">
-    <xsl:text>class AnimateWidget extends Widget{
+  <xsl:template match="widget[@type='Animate']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>AnimateWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -1954,13 +1964,27 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Animate']">
-    <xsl:param name="hmi_element"/>
-    <xsl:text>
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='AnimateRotation']">
-    <xsl:text>class AnimateRotationWidget extends Widget{
+  <xsl:template match="widget[@type='AnimateRotation']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>AnimateRotation - DEPRECATED, do not use.
+</xsl:text>
+      <xsl:text>Doesn't follow WYSIWYG principle, and forces user to add animateTransform tag in SVG (using inkscape XML editor for exemple)
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>AnimateRotation - DEPRECATED</xsl:text>
+    </shortdesc>
+    <path name="speed" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>speed</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='AnimateRotation']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>AnimateRotationWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -1988,6 +2012,8 @@
 </xsl:text>
     <xsl:text>       // change animation properties
 </xsl:text>
+    <xsl:text>       // TODO : rewrite with proper es6
+</xsl:text>
     <xsl:text>       for(let child of this.element.children){
 </xsl:text>
     <xsl:text>            if(child.nodeName == "animateTransform"){
@@ -2039,13 +2065,22 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='AnimateRotation']">
-    <xsl:param name="hmi_element"/>
-    <xsl:text>
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Back']">
-    <xsl:text>class BackWidget extends Widget{
+  <xsl:template match="widget[@type='Back']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Back widget brings focus back to previous page in history when clicked.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Jump to previous page</xsl:text>
+    </shortdesc>
+  </xsl:template>
+  <xsl:template match="widget[@type='Back']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>BackWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    on_click(evt) {
 </xsl:text>
@@ -2070,6 +2105,31 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
+  <xsl:template match="widget[@type='Button']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Button widget takes one boolean variable path, and reflect current true
+</xsl:text>
+      <xsl:text>or false value by showing "active" or "inactive" labeled element
+</xsl:text>
+      <xsl:text>respectively. Pressing and releasing button changes variable to true and
+</xsl:text>
+      <xsl:text>false respectively. Potential inconsistency caused by quick consecutive
+</xsl:text>
+      <xsl:text>presses on the button is mitigated by using a state machine that wait for
+</xsl:text>
+      <xsl:text>previous state change to be reflected on variable before applying next one.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Push button reflecting consistently given boolean variable</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_BOOL">
+      <xsl:text>Boolean variable</xsl:text>
+    </path>
+  </xsl:template>
   <xsl:variable name="_button_fsm">
     <fsm>
       <state name="init">
@@ -2218,10 +2278,12 @@
     <xsl:text>);
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Button']">
+  <xsl:template match="widget[@type='Button']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>ButtonWidget</xsl:text>
+    <xsl:text> extends Widget{
+</xsl:text>
     <xsl:variable name="fsm" select="exsl:node-set($_button_fsm)"/>
-    <xsl:text>class ButtonWidget extends Widget{
-</xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
     <xsl:text>    display = "inactive";
@@ -2287,7 +2349,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Button']">
+  <xsl:template match="widget[@type='Button']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -2297,8 +2359,39 @@
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='CircularBar']">
-    <xsl:text>class CircularBarWidget extends Widget{
+  <xsl:template match="widget[@type='CircularBar']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>CircularBar widget changes the end angle of a "path" labeled arc according
+</xsl:text>
+      <xsl:text>to value of the single accepted variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "min" a "max" labeled texts are provided, then they are used as
+</xsl:text>
+      <xsl:text>respective minimum and maximum value. Otherwise, value is expected to be
+</xsl:text>
+      <xsl:text>in between 0 and 100.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "value" labeled text is found, then its content is replaced by value.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Change end angle of Inkscape's arc</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>Value to display</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='CircularBar']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>CircularBarWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 10;
 </xsl:text>
@@ -2395,7 +2488,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='CircularBar']">
+  <xsl:template match="widget[@type='CircularBar']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -2411,8 +2504,51 @@
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='CircularSlider']">
-    <xsl:text>class CircularSliderWidget extends Widget{
+  <xsl:template match="widget[@type='CircularSlider']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>CircularSlider - DEPRECATED, to be replaced by PathSlider
+</xsl:text>
+      <xsl:text>This widget moves "handle" labeled group along "range" labeled
+</xsl:text>
+      <xsl:text>arc, according to value of the single accepted variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "min" a "max" labeled texts are provided, or if first and second
+</xsl:text>
+      <xsl:text>argument are given, then they are used as respective minimum and maximum
+</xsl:text>
+      <xsl:text>value. Otherwise, value is expected to be in between 0 and 100.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "value" labeled text is found, then its content is replaced by value.
+</xsl:text>
+      <xsl:text>During drag, "setpoint" labeled group is moved to position defined by user
+</xsl:text>
+      <xsl:text>while "handle" reflects current value from variable.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>CircularSlider - DEPRECATED</xsl:text>
+    </shortdesc>
+    <arg name="min" count="optional" accepts="int,real">
+      <xsl:text>minimum value</xsl:text>
+    </arg>
+    <arg name="min" count="optional" accepts="int,real">
+      <xsl:text>maximum value</xsl:text>
+    </arg>
+    <path name="value" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>Value to display</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='CircularSlider']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>CircularSliderWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -2867,7 +3003,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='CircularSlider']">
+  <xsl:template match="widget[@type='CircularSlider']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -2885,8 +3021,28 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='CustomHtml']">
-    <xsl:text>class CustomHtmlWidget extends Widget{
+  <xsl:template match="widget[@type='CustomHtml']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>CustomHtml widget allows insertion of HTML code in a svg:foreignObject.
+</xsl:text>
+      <xsl:text>Widget content is replaced by foreignObject. HTML code is obtained from
+</xsl:text>
+      <xsl:text>"code" labeled text content. HTML insert position and size is given with
+</xsl:text>
+      <xsl:text>"container" labeled element.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Custom HTML insert</xsl:text>
+    </shortdesc>
+  </xsl:template>
+  <xsl:template match="widget[@type='CustomHtml']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>CustomHtmlWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -2927,7 +3083,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='CustomHtml']">
+  <xsl:template match="widget[@type='CustomHtml']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -2935,11 +3091,47 @@
         <xsl:text>container code</xsl:text>
       </xsl:with-param>
     </xsl:call-template>
-    <xsl:text>
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Display']">
-    <xsl:text>class DisplayWidget extends Widget{
+  </xsl:template>
+  <xsl:template match="widget[@type='Display']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>If Display widget is a svg:text element, then text content is replaced by
+</xsl:text>
+      <xsl:text>value of given variables, space separated.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Otherwise, if Display widget is a group containing a svg:text element
+</xsl:text>
+      <xsl:text>labelled "format", then text content is replaced by printf-like formated
+</xsl:text>
+      <xsl:text>string. In other words, if "format" labeled text is "%d %s %f", then 3
+</xsl:text>
+      <xsl:text>variables paths are expected : HMI_IN, HMI_STRING and HMI_REAL.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>In case Display widget is a svg::text element, it is also possible to give
+</xsl:text>
+      <xsl:text>format string as first argument.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Printf-like formated text display </xsl:text>
+    </shortdesc>
+    <arg name="format" count="optional" accepts="string">
+      <xsl:text>printf-like format string when not given as svg:text</xsl:text>
+    </arg>
+    <path name="fields" count="many" accepts="HMI_INT,HMI_REAL,HMI_STRING,HMI_BOOL">
+      <xsl:text>variables to be displayed</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Display']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>DisplayWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -2954,7 +3146,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Display']">
+  <xsl:template match="widget[@type='Display']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:variable name="format">
       <xsl:call-template name="defs_by_labels">
@@ -3507,14 +3699,52 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='DropDown']">
-    <xsl:text>    function numb_event(e) {
-</xsl:text>
-    <xsl:text>        e.stopPropagation();
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    class DropDownWidget extends Widget{
+  <xsl:template match="widget[@type='DropDown']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>DropDown widget let user select an entry in a list of texts, given as
+</xsl:text>
+      <xsl:text>arguments. Single variable path is index of selection.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>It needs "text" (svg:text), "box" (svg:rect), "button" (svg:*),
+</xsl:text>
+      <xsl:text>and "highlight" (svg:rect) labeled elements.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>When user clicks on "button", "text" is duplicated to display enties in the
+</xsl:text>
+      <xsl:text>limit of available space in page, and "box" is extended to contain all
+</xsl:text>
+      <xsl:text>texts. "highlight" is moved over pre-selected entry.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>When only one argument is given, and argment contains "#langs" then list of
+</xsl:text>
+      <xsl:text>texts is automatically set to the list of human-readable languages supported
+</xsl:text>
+      <xsl:text>by this HMI. 
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Let user select text entry in a drop-down menu</xsl:text>
+    </shortdesc>
+    <arg name="entries" count="many" accepts="string">
+      <xsl:text>drop-down menu entries</xsl:text>
+    </arg>
+    <path name="selection" accepts="HMI_INT">
+      <xsl:text>selection index</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='DropDown']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>DropDownWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>        dispatch(value) {
 </xsl:text>
@@ -3742,9 +3972,9 @@
 </xsl:text>
     <xsl:text>            // Stop hogging all click events
 </xsl:text>
-    <xsl:text>            svg_root.removeEventListener("pointerdown", numb_event, true);
-</xsl:text>
-    <xsl:text>            svg_root.removeEventListener("pointerup", numb_event, true);
+    <xsl:text>            svg_root.removeEventListener("pointerdown", this.numb_event, true);
+</xsl:text>
+    <xsl:text>            svg_root.removeEventListener("pointerup", this.numb_event, true);
 </xsl:text>
     <xsl:text>            svg_root.removeEventListener("click", this.bound_close_on_click_elsewhere, true);
 </xsl:text>
@@ -3978,6 +4208,12 @@
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
+    <xsl:text>        numb_event(e) {
+</xsl:text>
+    <xsl:text>             e.stopPropagation();
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
     <xsl:text>        open(){
 </xsl:text>
     <xsl:text>            let length = this.content.length;
@@ -4032,9 +4268,9 @@
 </xsl:text>
     <xsl:text>            // disable interaction with background
 </xsl:text>
-    <xsl:text>            svg_root.addEventListener("pointerdown", numb_event, true);
-</xsl:text>
-    <xsl:text>            svg_root.addEventListener("pointerup", numb_event, true);
+    <xsl:text>            svg_root.addEventListener("pointerdown", this.numb_event, true);
+</xsl:text>
+    <xsl:text>            svg_root.addEventListener("pointerup", this.numb_event, true);
 </xsl:text>
     <xsl:text>            svg_root.addEventListener("click", this.bound_close_on_click_elsewhere, true);
 </xsl:text>
@@ -4162,10 +4398,10 @@
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>    }
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='DropDown']">
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template match="widget[@type='DropDown']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -4193,7 +4429,53 @@
     <xsl:text>,
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='ForEach']">
+  <xsl:template match="widget[@type='ForEach']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>ForEach widget is used to span a small set of widget over a larger set of
+</xsl:text>
+      <xsl:text>repeated HMI_NODEs. 
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Idea is somewhat similar to relative page, but it all happens inside the
+</xsl:text>
+      <xsl:text>ForEach widget, no page involved.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Together with relative Jump widgets it can be used to build a menu to reach
+</xsl:text>
+      <xsl:text>relative pages covering many identical HMI_NODES siblings.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>ForEach widget takes a HMI_CLASS name as argument and a HMI_NODE path as
+</xsl:text>
+      <xsl:text>variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Direct sub-elements can be either groups of widget to be spanned, labeled
+</xsl:text>
+      <xsl:text>"ClassName:offset", or buttons to control the spanning, labeled
+</xsl:text>
+      <xsl:text>"ClassName:+/-number".
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>span widgets over a set of repeated HMI_NODEs</xsl:text>
+    </shortdesc>
+    <arg name="class_name" accepts="string">
+      <xsl:text>HMI_CLASS name</xsl:text>
+    </arg>
+    <path name="root" accepts="HMI_NODE">
+      <xsl:text> where to find HMI_NODEs whose HMI_CLASS is class_name</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='ForEach']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:if test="count(path) != 1">
       <xsl:message terminate="yes">
@@ -4307,8 +4589,10 @@
     <xsl:text>    item_offset: 0,
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='ForEach']">
-    <xsl:text>class ForEachWidget extends Widget{
+  <xsl:template match="widget[@type='ForEach']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>ForEachWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -4443,73 +4727,103 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Input']">
-    <xsl:text>    class InputWidget extends Widget{
-</xsl:text>
-    <xsl:text>         on_op_click(opstr) {
-</xsl:text>
-    <xsl:text>             this.change_hmi_value(0, opstr);
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>         edit_callback(new_val) {
-</xsl:text>
-    <xsl:text>             this.apply_hmi_value(0, new_val);
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>         is_inhibited = false;
-</xsl:text>
-    <xsl:text>         alert(msg){
-</xsl:text>
-    <xsl:text>             this.is_inhibited = true;
-</xsl:text>
-    <xsl:text>             this.display = msg;
-</xsl:text>
-    <xsl:text>             setTimeout(() =&gt; this.stopalert(), 1000);
-</xsl:text>
-    <xsl:text>             this.request_animate();
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>         stopalert(){
-</xsl:text>
-    <xsl:text>             this.is_inhibited = false;
-</xsl:text>
-    <xsl:text>             this.display = this.last_value;
-</xsl:text>
-    <xsl:text>             this.request_animate();
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>         overshot(new_val, max) {
-</xsl:text>
-    <xsl:text>             this.alert("max");
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>         undershot(new_val, min) {
-</xsl:text>
-    <xsl:text>             this.alert("min");
-</xsl:text>
-    <xsl:text>         }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Input']">
+  <xsl:template match="widget[@type='Input']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Input widget takes one variable path, and displays current value in
+</xsl:text>
+      <xsl:text>optional "value" labeled sub-element. 
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Click on optional "edit" labeled element opens keypad to edit value.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Operation on current value is performed when click on sub-elements with
+</xsl:text>
+      <xsl:text>label starting with '=', '+' or '-' sign. Value after sign is used as
+</xsl:text>
+      <xsl:text>operand.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Input field with predefined operation buttons</xsl:text>
+    </shortdesc>
+    <arg name="format" accepts="string">
+      <xsl:text>optional printf-like format </xsl:text>
+    </arg>
+    <path name="edit" accepts="HMI_INT, HMI_REAL, HMI_STRING">
+      <xsl:text>single variable to edit</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Input']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>InputWidget</xsl:text>
+    <xsl:text> extends Widget{
+</xsl:text>
+    <xsl:text>     on_op_click(opstr) {
+</xsl:text>
+    <xsl:text>         this.change_hmi_value(0, opstr);
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>     edit_callback(new_val) {
+</xsl:text>
+    <xsl:text>         this.apply_hmi_value(0, new_val);
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>     is_inhibited = false;
+</xsl:text>
+    <xsl:text>     alert(msg){
+</xsl:text>
+    <xsl:text>         this.is_inhibited = true;
+</xsl:text>
+    <xsl:text>         this.display = msg;
+</xsl:text>
+    <xsl:text>         setTimeout(() =&gt; this.stopalert(), 1000);
+</xsl:text>
+    <xsl:text>         this.request_animate();
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>     stopalert(){
+</xsl:text>
+    <xsl:text>         this.is_inhibited = false;
+</xsl:text>
+    <xsl:text>         this.display = this.last_value;
+</xsl:text>
+    <xsl:text>         this.request_animate();
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>     overshot(new_val, max) {
+</xsl:text>
+    <xsl:text>         this.alert("max");
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>     undershot(new_val, min) {
+</xsl:text>
+    <xsl:text>         this.alert("min");
+</xsl:text>
+    <xsl:text>     }
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template match="widget[@type='Input']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:variable name="value_elt">
       <xsl:call-template name="defs_by_labels">
@@ -4598,8 +4912,34 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='JsonTable']">
-    <xsl:text>class JsonTableWidget extends Widget{
+  <xsl:template match="widget[@type='JsonTable']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Send given variables as POST to http URL argument, spread returned JSON in
+</xsl:text>
+      <xsl:text>SVG sub-elements of "data" labeled element.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Documentation to be written. see svbghmi exemple.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Http POST variables, spread JSON back</xsl:text>
+    </shortdesc>
+    <arg name="url" accepts="string">
+      <xsl:text> </xsl:text>
+    </arg>
+    <path name="edit" accepts="HMI_INT, HMI_REAL, HMI_STRING">
+      <xsl:text>single variable to edit</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='JsonTable']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>JsonTableWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    // arbitrary defaults to avoid missing entries in query
 </xsl:text>
@@ -5018,7 +5358,7 @@
     <xsl:text>        }
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='JsonTable']">
+  <xsl:template match="widget[@type='JsonTable']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -5064,10 +5404,48 @@
     <xsl:text>    }
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Jump']">
-    <xsl:text>    class JumpWidget extends Widget{
-</xsl:text>
-    <xsl:text>
+  <xsl:template match="widget[@type='Jump']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Jump widget brings focus to a different page. Mandatory single argument
+</xsl:text>
+      <xsl:text>gives name of the page.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Optional single path is used as new reference when jumping to a relative
+</xsl:text>
+      <xsl:text>page, it must point to a HMI_NODE.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>"active"+"inactive" labeled elements can be provided and reflect current
+</xsl:text>
+      <xsl:text>page being shown.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>"disabled" labeled element, if provided, is shown instead of "active" or
+</xsl:text>
+      <xsl:text>"inactive" widget when pointed HMI_NODE is null.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Jump to given page</xsl:text>
+    </shortdesc>
+    <arg name="page" accepts="string">
+      <xsl:text>name of page to jump to</xsl:text>
+    </arg>
+    <path name="reference" count="optional" accepts="HMI_NODE">
+      <xsl:text>reference for relative jump</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Jump']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>JumpWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>        activable = false;
 </xsl:text>
@@ -5193,10 +5571,10 @@
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>    }
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Jump']">
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template match="widget[@type='Jump']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:variable name="activity">
       <xsl:call-template name="defs_by_labels">
@@ -5249,7 +5627,8 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="per_page_widget_template" match="widget[@type='Jump']">
+  <xsl:template match="widget[@type='Jump']" mode="widget_page">
+    <xsl:param name="page_desc"/>
     <xsl:param name="page_desc"/>
     <xsl:if test="path">
       <xsl:variable name="target_page_name">
@@ -5316,6 +5695,21 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
+  <xsl:template match="widget[@type='Keypad']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Keypad - to be written
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Keypad </xsl:text>
+    </shortdesc>
+    <arg name="supported_types" accepts="string">
+      <xsl:text>keypad can input those types </xsl:text>
+    </arg>
+  </xsl:template>
   <declarations:keypad/>
   <xsl:template match="declarations:keypad">
     <xsl:text>
@@ -5351,10 +5745,10 @@
     <xsl:text>
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Keypad']">
-    <xsl:text>class KeypadWidget extends Widget{
-</xsl:text>
-    <xsl:text>
+  <xsl:template match="widget[@type='Keypad']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>KeypadWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>     on_key_click(symbols) {
 </xsl:text>
@@ -5545,7 +5939,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Keypad']">
+  <xsl:template match="widget[@type='Keypad']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -5606,7 +6000,12 @@
     <xsl:text>],
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='List']">
+  <xsl:template match="widget[@type='List']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+  </xsl:template>
+  <xsl:template match="widget[@type='List']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:text>    items: {
 </xsl:text>
@@ -5621,7 +6020,10 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='TextStyleList']">
+  <xsl:template match="widget[@type='TextStyleList']" mode="widget_defs">
+    <xsl:param name="hmi_element"/>
+  </xsl:template>
+  <xsl:template match="widget[@type='TextStyleList']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:text>    styles: {
 </xsl:text>
@@ -5637,8 +6039,47 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Meter']">
-    <xsl:text>class MeterWidget extends Widget{
+  <xsl:template match="widget[@type='Meter']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Meter widget moves the end of "needle" labeled path along "range" labeled
+</xsl:text>
+      <xsl:text>path, according to value of the single accepted variable.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Needle is reduced to a single segment. If "min" a "max" labeled texts
+</xsl:text>
+      <xsl:text>are provided, or if first and second argument are given, then they are used
+</xsl:text>
+      <xsl:text>as respective minimum and maximum value. Otherwise, value is expected to be
+</xsl:text>
+      <xsl:text>in between 0 and 100.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>If "value" labeled text is found, then its content is replaced by value.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Moves "needle" along "range"</xsl:text>
+    </shortdesc>
+    <arg name="min" count="optional" accepts="int,real">
+      <xsl:text>minimum value</xsl:text>
+    </arg>
+    <arg name="max" count="optional" accepts="int,real">
+      <xsl:text>maximum value</xsl:text>
+    </arg>
+    <path name="value" accepts="HMI_INT,HMI_REAL">
+      <xsl:text>Value to display</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Metter']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>MetterWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 10;
 </xsl:text>
@@ -5692,12 +6133,10 @@
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
-    <xsl:text>
-</xsl:text>
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Meter']">
+  <xsl:template match="widget[@type='Meter']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -5713,8 +6152,39 @@
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='MultiState']">
-    <xsl:text>class MultiStateWidget extends Widget{
+  <xsl:template match="widget[@type='MultiState']" mode="widget_defs">
+    <xsl:param name="hmi_element"/>
+    <longdesc>
+      <xsl:text>Mutlistateh widget hides all subelements whose label do not match given
+</xsl:text>
+      <xsl:text>variable value representation. For exemple if given variable type
+</xsl:text>
+      <xsl:text>is HMI_INT and value is 1, then elements with label '1' will be displayed.
+</xsl:text>
+      <xsl:text>Label can have comments, so '1#some comment' would also match. If matching
+</xsl:text>
+      <xsl:text>variable of type HMI_STRING, then double quotes must be used. For exemple,
+</xsl:text>
+      <xsl:text>'"hello"' or '"hello"#another comment' match HMI_STRING 'hello'.
+</xsl:text>
+      <xsl:text>
+</xsl:text>
+      <xsl:text>Click on widget changes variable value to next value in given list, or to
+</xsl:text>
+      <xsl:text>first one if not initialized to value already part of the list.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Show elements whose label match value.</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT,HMI_STRING">
+      <xsl:text>value to compare to labels</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='MultiState']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>MultiStateWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -5795,7 +6265,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='MultiState']">
+  <xsl:template match="widget[@type='MultiState']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:text>    choices: [
 </xsl:text>
@@ -5826,8 +6296,31 @@
     <xsl:text>    ],
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='ScrollBar']">
-    <xsl:text>class ScrollBarWidget extends Widget{
+  <xsl:template match="widget[@type='ScrollBar']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>ScrollBar - documentation to be written
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>ScrollBar</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT">
+      <xsl:text>value</xsl:text>
+    </path>
+    <path name="range" accepts="HMI_INT">
+      <xsl:text>range</xsl:text>
+    </path>
+    <path name="visible" accepts="HMI_INT">
+      <xsl:text>visible</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='ScrollBar']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>ScrollBarWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 10;
 </xsl:text>
@@ -6004,7 +6497,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='ScrollBar']">
+  <xsl:template match="widget[@type='ScrollBar']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -6036,7 +6529,32 @@
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Slider']">
+  <xsl:template match="widget[@type='Slider']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Slider - DEPRECATED - use ScrollBar or PathSlider instead
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Slider - DEPRECATED - use ScrollBar instead</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT">
+      <xsl:text>value</xsl:text>
+    </path>
+    <path name="range" accepts="HMI_INT">
+      <xsl:text>range</xsl:text>
+    </path>
+    <path name="visible" accepts="HMI_INT">
+      <xsl:text>visible</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Slider']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>SliderWidget</xsl:text>
+    <xsl:text> extends Widget{
+</xsl:text>
     <xsl:text>class SliderWidget extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
@@ -6707,8 +7225,10 @@
 </xsl:text>
     <xsl:text>}
 </xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Slider']">
+    <xsl:text>}
+</xsl:text>
+  </xsl:template>
+  <xsl:template match="widget[@type='Slider']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
@@ -6723,11 +7243,36 @@
       </xsl:with-param>
       <xsl:with-param name="mandatory" select="'no'"/>
     </xsl:call-template>
-    <xsl:text>
-</xsl:text>
-  </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='Switch']">
-    <xsl:text>class SwitchWidget extends Widget{
+  </xsl:template>
+  <xsl:template match="widget[@type='Switch']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Switch widget hides all subelements whose label do not match given
+</xsl:text>
+      <xsl:text>variable current value representation. For exemple if given variable type
+</xsl:text>
+      <xsl:text>is HMI_INT and value is 1, then elements with label '1' will be displayed.
+</xsl:text>
+      <xsl:text>Label can have comments, so '1#some comment' would also match. If matching
+</xsl:text>
+      <xsl:text>variable of type HMI_STRING, then double quotes must be used. For exemple,
+</xsl:text>
+      <xsl:text>'"hello"' or '"hello"#another comment' match HMI_STRING 'hello'.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Show elements whose label match value.</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_INT,HMI_STRING">
+      <xsl:text>value to compare to labels</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='Switch']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>SwitchWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -6752,7 +7297,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='Switch']">
+  <xsl:template match="widget[@type='Switch']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:text>    choices: [
 </xsl:text>
@@ -6786,8 +7331,29 @@
     <xsl:text>    ],
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_class" match="widget[@type='ToggleButton']">
-    <xsl:text>class ToggleButtonWidget extends Widget{
+  <xsl:template match="widget[@type='ToggleButton']" mode="widget_desc">
+    <type>
+      <xsl:value-of select="@type"/>
+    </type>
+    <longdesc>
+      <xsl:text>Button widget takes one boolean variable path, and reflect current true
+</xsl:text>
+      <xsl:text>or false value by showing "active" or "inactive" labeled element
+</xsl:text>
+      <xsl:text>respectively. Clicking or touching button toggles variable.
+</xsl:text>
+    </longdesc>
+    <shortdesc>
+      <xsl:text>Toggle button reflecting given boolean variable</xsl:text>
+    </shortdesc>
+    <path name="value" accepts="HMI_BOOL">
+      <xsl:text>Boolean variable</xsl:text>
+    </path>
+  </xsl:template>
+  <xsl:template match="widget[@type='ToggleButton']" mode="widget_class">
+    <xsl:text>class </xsl:text>
+    <xsl:text>ToggleButtonWidget</xsl:text>
+    <xsl:text> extends Widget{
 </xsl:text>
     <xsl:text>    frequency = 5;
 </xsl:text>
@@ -6866,7 +7432,7 @@
     <xsl:text>}
 </xsl:text>
   </xsl:template>
-  <xsl:template mode="widget_defs" match="widget[@type='ToggleButton']">
+  <xsl:template match="widget[@type='ToggleButton']" mode="widget_defs">
     <xsl:param name="hmi_element"/>
     <xsl:call-template name="defs_by_labels">
       <xsl:with-param name="hmi_element" select="$hmi_element"/>
--- a/svghmi/parse_labels.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/parse_labels.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -13,7 +13,10 @@
 //      path value="path4" index="path4" type="HMI_LOCAL";
 //  }
 //
-template "*", mode="parselabel" {
+const "pathregex",!"'^([^\[,]+)(\[[^\]]+\])?([\d,]*)$'"!;
+
+template "*", mode="parselabel" 
+{
     const "label","@inkscape:label";
     const "id","@id";
     const "description", "substring-after($label,'HMI:')";
@@ -41,16 +44,24 @@
         const "paths", "substring-after($description,'@')";
         foreach "str:split($paths, '@')" {
             if "string-length(.) > 0" path {
-                const "pathminmax", "str:split(.,',')";
-                const "path", "$pathminmax[1]";
+                // 1 : global match
+                // 2 : /path
+                // 3 : [accepts]
+                // 4 : min,max
+                const "path_match", "regexp:match(.,$pathregex)";
+                const "pathminmax", "str:split($path_match[4],',')";
+                const "path", "$path_match[2]";
+                const "path_accepts", "$path_match[3]";
                 const "pathminmaxcount", "count($pathminmax)";
                 attrib "value" > «$path»
+                if "string-length($path_accepts)"
+                    attrib "accepts" > «$path_accepts»
                 choose {
-                    when "$pathminmaxcount = 3" {
-                        attrib "min" > «$pathminmax[2]»
-                        attrib "max" > «$pathminmax[3]»
+                    when "$pathminmaxcount = 2" {
+                        attrib "min" > «$pathminmax[1]»
+                        attrib "max" > «$pathminmax[2]»
                     }
-                    when "$pathminmaxcount = 2" {
+                    when "$pathminmaxcount = 1 or $pathminmaxcount > 2" {
                         error > Widget id:«$id» label:«$label» has wrong syntax of path section «$pathminmax»
                     }
                 }
--- a/svghmi/ui.py	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/ui.py	Tue May 18 09:28:44 2021 +0200
@@ -128,6 +128,7 @@
 
 _conf_key = "SVGHMIWidgetLib"
 _preview_height = 200
+_preview_margin = 5
 class WidgetLibBrowser(wx.Panel):
     def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
                  size=wx.DefaultSize):
@@ -142,22 +143,24 @@
         self.Config = wx.ConfigBase.Get()
         self.libdir = self.RecallLibDir()
 
-        sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=0)
-        sizer.AddGrowableCol(0)
-        sizer.AddGrowableRow(1)
+        self.main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=5, vgap=0)
+        self.main_sizer.AddGrowableCol(0)
+        self.main_sizer.AddGrowableRow(1)
         self.libbutton = wx.Button(self, -1, _("Select SVG widget library"))
         self.widgetpicker = WidgetPicker(self, self.libdir)
-        self.preview = wx.Panel(self, size=(-1, _preview_height + 10))
-        self.comment = wx.TextCtrl(self, size=wx.Size(-1, 80),
+        self.preview = wx.Panel(self, size=(-1, _preview_height + _preview_margin*2))
+        self.desc = wx.TextCtrl(self, size=wx.Size(-1, 160),
                                    style=wx.TE_READONLY | wx.TE_MULTILINE)
-        sizer.AddWindow(self.libbutton, flag=wx.GROW)
-        sizer.AddWindow(self.widgetpicker, flag=wx.GROW)
-        sizer.AddWindow(self.preview, flag=wx.GROW)
-        sizer.AddWindow(self.comment, flag=wx.GROW)
-        sizer.Layout()
+        self.signature_sizer = wx.BoxSizer(wx.VERTICAL)
+        self.main_sizer.Add(self.libbutton, flag=wx.GROW)
+        self.main_sizer.Add(self.widgetpicker, flag=wx.GROW)
+        self.main_sizer.Add(self.preview, flag=wx.GROW)
+        self.main_sizer.Add(self.desc, flag=wx.GROW)
+        self.main_sizer.Add(self.signature_sizer, flag=wx.GROW)
+        self.main_sizer.Layout()
         self.SetAutoLayout(True)
-        self.SetSizer(sizer)
-        sizer.Fit(self)
+        self.SetSizer(self.main_sizer)
+        self.main_sizer.Fit(self)
         self.Bind(wx.EVT_BUTTON, self.OnSelectLibDir, self.libbutton)
         self.preview.Bind(wx.EVT_PAINT, self.OnPaint)
         self.preview.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
@@ -167,6 +170,21 @@
         self.msg = _("Drag selected Widget from here to Inkscape")
         self.tempf = None 
 
+        self.paths_editors = []
+
+    def ResetSignature(self):
+        self.signature_sizer.Clear()
+        for editor in self.paths_editors:
+            editor.Destroy()
+        self.paths_editors = []
+        self.main_sizer.Layout()
+
+    def AddPathToSignature(self, path):
+        new_editor = wx.TextCtrl(self, size=wx.Size(-1, -1))
+        self.paths_editors.append(new_editor)
+        self.signature_sizer.Add(new_editor, flag=wx.GROW)
+        self.main_sizer.Layout()
+
     def RecallLibDir(self):
         conf = self.Config.Read(_conf_key)
         if len(conf) == 0:
@@ -191,9 +209,9 @@
             # Get Preview panel size
             sz = self.preview.GetClientSize()
             w = self.bmp.GetWidth()
-            dc.DrawBitmap(self.bmp, (sz.width - w)/2, 5)
-
-        self.comment.SetValue(self.msg)
+            dc.DrawBitmap(self.bmp, (sz.width - w)/2, _preview_margin)
+
+        self.desc.SetValue(self.msg)
 
 
     def OnSelectLibDir(self, event):
@@ -268,7 +286,9 @@
                 self.bmp = wx.Bitmap(thumbpath) if have_thumb else None
 
                 self.selected_SVG = svgpath if have_thumb else None
-                self.ValidateWidget()
+
+                self.AnalyseWidgetAndUpdateUI()
+
             except IOError:
                 self.msg = _("Widget library must be writable")
 
@@ -293,9 +313,65 @@
         for msg in msgs:
             self.msg += msg.text + "\n"
         
+    def PassMessage(self, _context, msgs):
+        for msg in msgs:
+            self.msg += msg.text + "\n"
+
     def GetSubHMITree(self, _context):
         return [self.hmitree_node.etree()]
 
+    def AnalyseWidgetAndUpdateUI(self):
+        self.msg = ""
+
+        try:
+            if self.selected_SVG is None:
+                raise Exception(_("No widget selected"))
+
+            transform = XSLTransform(
+                os.path.join(ScriptDirectory, "analyse_widget.xslt"),[])
+
+            svgdom = etree.parse(self.selected_SVG)
+
+            signature = transform.transform(svgdom)
+
+            for entry in transform.get_error_log():
+                self.msg += "XSLT: " + entry.message + "\n" 
+
+        except Exception as e:
+            self.msg += str(e)
+        except XSLTApplyError as e:
+            self.msg += "Widget analysis error: " + e.message
+        else:
+            
+            self.ResetSignature()
+
+            print(etree.tostring(signature, pretty_print=True))
+            widgets = signature.getroot()
+            for defs in widgets.iter("defs"):
+
+                # Keep double newlines (to mark paragraphs)
+                self.msg += defs.find("type").text + ":\n" + "\n\n".join(map(
+                    lambda s:s.replace("\n"," ").replace("  ", " "), 
+                    defs.find("longdesc").text.split("\n\n")))
+                for arg in defs.iter("arg"):
+                    print(arg.get("name"))
+                    print(arg.get("accepts"))
+                for path in defs.iter("path"):
+                    self.AddPathToSignature(path)
+                    print(path.get("name"))
+                    print(path.get("accepts"))
+
+            for widget in widgets:
+                widget_type = widget.get("type")
+                print(widget_type)
+                for path in widget.iterchildren("path"):
+                    path_value = path.get("value")
+                    path_accepts = map(
+                        str.strip, path.get("accepts", '')[1:-1].split(','))
+                    print(path, path_value, path_accepts)
+
+
+
     def ValidateWidget(self):
         self.msg = ""
 
@@ -312,7 +388,7 @@
             transform = XSLTransform(
                 os.path.join(ScriptDirectory, "gen_dnd_widget_svg.xslt"),
                 [("GetSubHMITree", self.GetSubHMITree),
-                 ("GiveDetails", self.GiveDetails)])
+                 ("PassMessage", self.GiveDetails)])
 
             svgdom = etree.parse(self.selected_SVG)
 
--- a/svghmi/widget_animate.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_animate.ysl2	Tue May 18 09:28:44 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	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_animaterotation.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,20 @@
 // widget_animaterotation.ysl2
 
-template "widget[@type='AnimateRotation']", mode="widget_class"{
+widget_desc("AnimateRotation") {
+    longdesc
     ||
-    class AnimateRotationWidget extends Widget{
+    AnimateRotation - DEPRECATED, do not use.
+    Doesn't follow WYSIWYG principle, and forces user to add animateTransform tag in SVG (using inkscape XML editor for exemple)
+    ||
+
+    shortdesc > AnimateRotation - DEPRECATED
+
+    path name="speed" accepts="HMI_INT,HMI_REAL" > speed
+    
+}
+
+widget_class("AnimateRotation") {
+    ||
         frequency = 5;
         speed = 0;
         widget_center = undefined;
@@ -16,6 +28,7 @@
 
         animate(){
            // change animation properties
+           // TODO : rewrite with proper es6
            for(let child of this.element.children){
                 if(child.nodeName == "animateTransform"){
                     if(this.speed > 0){
@@ -40,12 +53,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='AnimateRotation']", mode="widget_defs" {
-    param "hmi_element";
-    |,
-}
--- a/svghmi/widget_back.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_back.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,17 @@
 // widget_back.ysl2
 
-template "widget[@type='Back']", mode="widget_class"
+widget_desc("Back") {
+    longdesc
     ||
-    class BackWidget extends Widget{
+    Back widget brings focus back to previous page in history when clicked.
+    ||
+
+    shortdesc > Jump to previous page
+}
+
+// TODO: use es6
+widget_class("Back")
+    ||
         on_click(evt) {
             if(jump_history.length > 1){
                jump_history.pop();
@@ -13,5 +22,4 @@
         init() {
             this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
         }
-    }
     ||
--- a/svghmi/widget_button.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_button.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,5 +1,22 @@
 // widget_button.ysl2
 
+widget_desc("Button") {
+    longdesc
+    ||
+    Button widget takes one boolean variable path, and reflect current true
+    or false value by showing "active" or "inactive" labeled element
+    respectively. Pressing and releasing button changes variable to true and
+    false respectively. Potential inconsistency caused by quick consecutive
+    presses on the button is mitigated by using a state machine that wait for
+    previous state change to be reflected on variable before applying next one.
+    ||
+
+    shortdesc > Push button reflecting consistently given boolean variable
+
+    path name="value" accepts="HMI_BOOL" > Boolean variable
+    
+}
+
 // Finite state machine
 decl fsm(name);
 decl state(name);
@@ -9,6 +26,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 +123,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 +165,8 @@
     |         this.bound_onmouseup = this.onmouseup.bind(this);
     |         this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
     |     }
-    | }
 }
 
-
-template "widget[@type='Button']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("Button") {
     optional_labels("active inactive");
 }
--- a/svghmi/widget_circularbar.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_circularbar.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,28 @@
 // widget_circularbar.ysl2
 
-template "widget[@type='CircularBar']", mode="widget_class"{
+widget_desc("CircularBar") {
+    longdesc
     ||
-    class CircularBarWidget extends Widget{
+    CircularBar widget changes the end angle of a "path" labeled arc according
+    to value of the single accepted variable.
+
+    If "min" a "max" labeled texts are provided, then they are used as
+    respective minimum and maximum value. Otherwise, value is expected to be
+    in between 0 and 100.
+
+    If "value" labeled text is found, then its content is replaced by value.
+    ||
+
+    shortdesc > Change end angle of Inkscape's arc
+
+    // TODO: add min/max arguments
+    // TODO: add printf-like format
+
+    path name="value" accepts="HMI_INT,HMI_REAL" > Value to display
+    
+}
+widget_class("CircularBar") {
+    ||
         frequency = 10;
         range = undefined;
 
@@ -49,12 +69,10 @@
             this.center = [cx, cy];
             this.proportions = [rx, ry];
         }
-    }
     ||
 }
 
-template "widget[@type='CircularBar']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("CircularBar") {
     labels("path");
     optional_labels("value min max");
 }
--- a/svghmi/widget_circularslider.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_circularslider.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,35 @@
 // widget_circuralslider.ysl2
 
-template "widget[@type='CircularSlider']", mode="widget_class"
-    ||
-    class CircularSliderWidget extends Widget{
+widget_desc("CircularSlider") {
+    longdesc
+    ||
+    CircularSlider - DEPRECATED, to be replaced by PathSlider
+    This widget moves "handle" labeled group along "range" labeled
+    arc, according to value of the single accepted variable.
+
+    If "min" a "max" labeled texts are provided, or if first and second
+    argument are given, then they are used as respective minimum and maximum
+    value. Otherwise, value is expected to be in between 0 and 100.
+
+    If "value" labeled text is found, then its content is replaced by value.
+    During drag, "setpoint" labeled group is moved to position defined by user
+    while "handle" reflects current value from variable.
+    ||
+
+    shortdesc > CircularSlider - DEPRECATED
+
+    arg name="min" count="optional" accepts="int,real" > minimum value
+
+    arg name="min" count="optional" accepts="int,real" > maximum value
+
+    // TODO: add printf-like format
+
+    path name="value" accepts="HMI_INT,HMI_REAL" > Value to display
+    
+}
+
+widget_class("CircularSlider")
+    ||
         frequency = 5;
         range = undefined;
         circle = undefined;
@@ -228,11 +255,9 @@
             }
 
         }
-    }
-    ||
-
-template "widget[@type='CircularSlider']", mode="widget_defs" {
-    param "hmi_element";
+    ||
+
+widget_defs("CircularSlider") {
     labels("handle range");
     optional_labels("value min max setpoint");
     |,
--- a/svghmi/widget_customhtml.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_customhtml.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,21 @@
 // widget_customhtml.ysl2
 
-template "widget[@type='CustomHtml']", mode="widget_class"{
+widget_desc("CustomHtml") {
+    longdesc
     ||
-    class CustomHtmlWidget extends Widget{
+    CustomHtml widget allows insertion of HTML code in a svg:foreignObject.
+    Widget content is replaced by foreignObject. HTML code is obtained from
+    "code" labeled text content. HTML insert position and size is given with
+    "container" labeled element.
+    ||
+
+    shortdesc > Custom HTML insert
+
+    // TODO: support reload and POST based on variable content
+}
+
+widget_class("CustomHtml"){
+    ||
         frequency = 5;
         widget_size = undefined;
 
@@ -21,13 +34,10 @@
                 this.code_elt.textContent+
                 ' </foreignObject>';
         }
-    }
     ||
 }
 
 
-template "widget[@type='CustomHtml']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("CustomHtml") {
     labels("container code");
-    |,
 }
--- a/svghmi/widget_display.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_display.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,20 +1,39 @@
 // widget_display.ysl2
 
-
-template "widget[@type='Display']", mode="widget_class"
+widget_desc("Display") {
+    longdesc
     ||
-    class DisplayWidget extends Widget{
+    If Display widget is a svg:text element, then text content is replaced by
+    value of given variables, space separated.
+
+    Otherwise, if Display widget is a group containing a svg:text element
+    labelled "format", then text content is replaced by printf-like formated
+    string. In other words, if "format" labeled text is "%d %s %f", then 3
+    variables paths are expected : HMI_IN, HMI_STRING and HMI_REAL.
+
+    In case Display widget is a svg::text element, it is also possible to give
+    format string as first argument.
+    ||
+
+    shortdesc > Printf-like formated text display 
+
+    arg name="format" count="optional" accepts="string" > printf-like format string when not given as svg:text
+
+    path name="fields" count="many" accepts="HMI_INT,HMI_REAL,HMI_STRING,HMI_BOOL" > variables to be displayed
+    
+}
+
+
+widget_class("Display")
+    ||
         frequency = 5;
         dispatch(value, oldval, index) {
             this.fields[index] = value;    
             this.request_animate();
         }
-    }
     ||
 
-template "widget[@type='Display']", mode="widget_defs" {
-    param "hmi_element";
-
+widget_defs("Display") {
     const "format" optional_labels("format");
     const "has_format","string-length($format)>0";
     value "$format";
--- a/svghmi/widget_dropdown.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_dropdown.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,11 +1,35 @@
 // widget_dropdown.ysl2
 
-template "widget[@type='DropDown']", mode="widget_class"{
+widget_desc("DropDown") {
+
+    longdesc
+    ||
+    DropDown widget let user select an entry in a list of texts, given as
+    arguments. Single variable path is index of selection.
+
+    It needs "text" (svg:text), "box" (svg:rect), "button" (svg:*),
+    and "highlight" (svg:rect) labeled elements.
+
+    When user clicks on "button", "text" is duplicated to display enties in the
+    limit of available space in page, and "box" is extended to contain all
+    texts. "highlight" is moved over pre-selected entry.
+
+    When only one argument is given, and argment contains "#langs" then list of
+    texts is automatically set to the list of human-readable languages supported
+    by this HMI. 
+    ||
+
+    shortdesc > Let user select text entry in a drop-down menu
+
+    arg name="entries" count="many" accepts="string" > drop-down menu entries
+
+    path name="selection" accepts="HMI_INT" > selection index
+}
+
+// TODO: support i18n of menu entries using svg:text elements with labels starting with "_"
+
+widget_class("DropDown") {
 ||
-    function numb_event(e) {
-        e.stopPropagation();
-    }
-    class DropDownWidget extends Widget{
         dispatch(value) {
             if(!this.opened) this.set_selection(value);
         }
@@ -119,8 +143,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 +261,9 @@
                 c++;
             }
         }
+        numb_event(e) {
+             e.stopPropagation();
+        }
         open(){
             let length = this.content.length;
             // systematically reset text, to strip eventual whitespace spans
@@ -264,8 +291,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,12 +356,10 @@
             // b.width.baseVal.value = 2 * lmargin + m.width;
             b.height.baseVal.value = 2 * tmargin + m.height;
         }
-    }
 ||
 }
 
-template "widget[@type='DropDown']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("DropDown") {
     labels("text box button highlight");
     // It is assumed that list content conforms to Array interface.
     >   content:
--- a/svghmi/widget_foreach.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_foreach.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,6 +1,34 @@
+// widget_foreach.ysl2
 
-template "widget[@type='ForEach']", mode="widget_defs" {
-    param "hmi_element";
+widget_desc("ForEach") {
+
+    longdesc
+    ||
+    ForEach widget is used to span a small set of widget over a larger set of
+    repeated HMI_NODEs. 
+
+    Idea is somewhat similar to relative page, but it all happens inside the
+    ForEach widget, no page involved.
+
+    Together with relative Jump widgets it can be used to build a menu to reach
+    relative pages covering many identical HMI_NODES siblings.
+
+    ForEach widget takes a HMI_CLASS name as argument and a HMI_NODE path as
+    variable.
+
+    Direct sub-elements can be either groups of widget to be spanned, labeled
+    "ClassName:offset", or buttons to control the spanning, labeled
+    "ClassName:+/-number".
+    ||
+
+    shortdesc > span widgets over a set of repeated HMI_NODEs
+
+    arg name="class_name" accepts="string" > HMI_CLASS name
+
+    path name="root" accepts="HMI_NODE" >  where to find HMI_NODEs whose HMI_CLASS is class_name
+}
+
+widget_defs("ForEach") {
 
     if "count(path) != 1" error > ForEach widget «$hmi_element/@id» must have one HMI path given.
     if "count(arg) != 1" error > ForEach widget «$hmi_element/@id» must have one argument given : a class name.
@@ -49,9 +77,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 +144,5 @@
         jumps_need_update = true;
         requestHMIAnimation();
     }
-}
 ||
 
--- a/svghmi/widget_input.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_input.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,44 +1,59 @@
 // 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);
-         }
+widget_desc("Input") {
+    longdesc
+    || 
+    Input widget takes one variable path, and displays current value in
+    optional "value" labeled sub-element. 
 
-         is_inhibited = false;
-         alert(msg){
-             this.is_inhibited = true;
-             this.display = msg;
-             setTimeout(() => this.stopalert(), 1000);
-             this.request_animate();
-         }
+    Click on optional "edit" labeled element opens keypad to edit value.
+    
+    Operation on current value is performed when click on sub-elements with
+    label starting with '=', '+' or '-' sign. Value after sign is used as
+    operand.
+    ||
 
-         stopalert(){
-             this.is_inhibited = false;
-             this.display = this.last_value;
-             this.request_animate();
-         }
+    shortdesc > Input field with predefined operation buttons
 
-         overshot(new_val, max) {
-             this.alert("max");
-         }
+    arg name="format" accepts="string" > optional printf-like format 
 
-         undershot(new_val, min) {
-             this.alert("min");
-         }
-
-
-    }
-||
+    path name="edit" accepts="HMI_INT, HMI_REAL, HMI_STRING" > single variable to edit
+    
 }
 
-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	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_jsontable.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,24 @@
 // widget_jsontable.ysl2
 
-template "widget[@type='JsonTable']", mode="widget_class"
+widget_desc("JsonTable") {
+    longdesc
+    || 
+    Send given variables as POST to http URL argument, spread returned JSON in
+    SVG sub-elements of "data" labeled element.
+    
+    Documentation to be written. see svbghmi exemple.
     ||
-    class JsonTableWidget extends Widget{
+
+    shortdesc > Http POST variables, spread JSON back
+
+    arg name="url" accepts="string" >  
+
+    path name="edit" accepts="HMI_INT, HMI_REAL, HMI_STRING" > single variable to edit
+    
+}
+
+widget_class("JsonTable")
+    ||
         // arbitrary defaults to avoid missing entries in query
         cache = [0,0,0];
         init_common() {
@@ -83,9 +99,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,8 +276,9 @@
     |         }
 }
 
-template "widget[@type='JsonTable']", mode="widget_defs" {
-    param "hmi_element";
+}
+
+widget_defs("JsonTable") {
     labels("data");
     const "data_elt", "$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']";
     |     visible: «count($data_elt/*[@inkscape:label])»,
--- a/svghmi/widget_jump.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_jump.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,9 +1,30 @@
 // widget_jump.ysl2
 
-template "widget[@type='Jump']", mode="widget_class"{
+widget_desc("Jump") {
+    longdesc
+    ||
+    Jump widget brings focus to a different page. Mandatory single argument
+    gives name of the page.
+
+    Optional single path is used as new reference when jumping to a relative
+    page, it must point to a HMI_NODE.
+
+    "active"+"inactive" labeled elements can be provided and reflect current
+    page being shown.
+
+    "disabled" labeled element, if provided, is shown instead of "active" or
+    "inactive" widget when pointed HMI_NODE is null.
+    ||
+
+    shortdesc > Jump to given page
+
+    arg name="page" accepts="string" > name of page to jump to
+
+    path name="reference" count="optional" accepts="HMI_NODE" > reference for relative jump
+}
+
+widget_class("Jump") {
 ||
-    class JumpWidget extends Widget{
-
         activable = false;
         active = false;
         disabled = false;
@@ -66,12 +87,11 @@
             this.disabled = !Number(value);
             this.update_state();
         }
-    }
 ||
 }
 
-template "widget[@type='Jump']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("Jump") {
+    // TODO: ensure both active and inactive are provided
     const "activity" optional_labels("active inactive");
     const "have_activity","string-length($activity)>0";
     value "$activity";
@@ -103,7 +123,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	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_keypad.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,5 +1,17 @@
 // widget_keypad.ysl2
 
+widget_desc("Keypad") {
+    longdesc
+    ||
+    Keypad - to be written
+    ||
+
+    shortdesc > Keypad 
+
+    arg name="supported_types" accepts="string" > keypad can input those types 
+    
+}
+
 emit "declarations:keypad" {
     |
     | var keypads = {
@@ -13,10 +25,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,11 +120,9 @@
                  (this.caps?this.activate_activable:this.inactivate_activable)(this.CapsLock_sub);
              }
          }
-    }
     ||
 
-template "widget[@type='Keypad']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("Keypad") {
     labels("Esc Enter BackSpace Keys Info Value");
     optional_labels("Sign Space NumDot");
     activable_labels("CapsLock Shift");
--- a/svghmi/widget_list.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_list.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,7 +1,9 @@
 // widget_list.ysl2
+widget_desc("List") {
+    // TODO
+}
 
-template "widget[@type='List']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("List") {
     |     items: {
     foreach "$hmi_element/*[@inkscape:label]" {
     |         «@inkscape:label»: "«@id»",
@@ -9,8 +11,11 @@
     |     },
 }
 
-template "widget[@type='TextStyleList']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("TextStyleList") {
+    // TODO
+}
+
+widget_defs("TextStyleList") {
     |     styles: {
     foreach "$hmi_element/*[@inkscape:label]" {
         const "style", "func:refered_elements(.)[self::svg:text]/@style";
--- a/svghmi/widget_meter.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_meter.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,33 @@
 // widget_meter.ysl2
 
-template "widget[@type='Meter']", mode="widget_class"{
+widget_desc("Meter") {
+    longdesc
     ||
-    class MeterWidget extends Widget{
+    Meter widget moves the end of "needle" labeled path along "range" labeled
+    path, according to value of the single accepted variable.
+
+    Needle is reduced to a single segment. If "min" a "max" labeled texts
+    are provided, or if first and second argument are given, then they are used
+    as respective minimum and maximum value. Otherwise, value is expected to be
+    in between 0 and 100.
+
+    If "value" labeled text is found, then its content is replaced by value.
+    ||
+
+    shortdesc > Moves "needle" along "range"
+
+    arg name="min" count="optional" accepts="int,real" > minimum value
+
+    arg name="max" count="optional" accepts="int,real" > maximum value
+
+    // TODO: add printf-like format
+
+    path name="value" accepts="HMI_INT,HMI_REAL" > Value to display
+    
+}
+
+widget_class("Metter"){
+    ||
         frequency = 10;
         origin = undefined;
         range = undefined;
@@ -29,13 +54,10 @@
             this.range = [min, max, this.range_elt.getTotalLength()]
             this.origin = this.needle_elt.getPointAtLength(0);
         }
-
-    }
     ||
 }
 
-template "widget[@type='Meter']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("Meter") {
     labels("needle range");
     optional_labels("value min max");
 }
--- a/svghmi/widget_multistate.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_multistate.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,30 @@
 // widget_multistate.ysl2
 
-template "widget[@type='MultiState']", mode="widget_class"
+widget_defs("MultiState") {
+
+    longdesc
     ||
-    class MultiStateWidget extends Widget{
+    Mutlistateh widget hides all subelements whose label do not match given
+    variable value representation. For exemple if given variable type
+    is HMI_INT and value is 1, then elements with label '1' will be displayed.
+    Label can have comments, so '1#some comment' would also match. If matching
+    variable of type HMI_STRING, then double quotes must be used. For exemple,
+    '"hello"' or '"hello"#another comment' match HMI_STRING 'hello'.
+
+    Click on widget changes variable value to next value in given list, or to
+    first one if not initialized to value already part of the list.
+    ||
+
+    shortdesc > Show elements whose label match value.
+
+    // TODO: add optional format/precision argument to support floating points
+
+    path name="value" accepts="HMI_INT,HMI_STRING" > value to compare to labels
+    
+}
+
+widget_class("MultiState")
+    ||
         frequency = 5;
         state = 0;
         dispatch(value) {
@@ -41,11 +63,9 @@
         init() {
             this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
         }
-    }
     ||
 
-template "widget[@type='MultiState']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("MultiState") {
     |     choices: [
     const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+|false|true)(#.*)?$'"!;
     foreach "$result_svg_ns//*[@id = $hmi_element/@id]//*[regexp:test(@inkscape:label,$regex)]" {
--- a/svghmi/widget_scrollbar.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_scrollbar.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,20 @@
 // widget_scrollbar.ysl2
+widget_desc("ScrollBar") {
+    longdesc
+    || 
+    ScrollBar - documentation to be written
+    ||
 
-template "widget[@type='ScrollBar']", mode="widget_class"{
+    shortdesc > ScrollBar
+
+    path name="value" accepts="HMI_INT" > value
+    path name="range" accepts="HMI_INT" > range
+    path name="visible" accepts="HMI_INT" > visible
+    
+}
+
+widget_class("ScrollBar") {
     ||
-    class ScrollBarWidget extends Widget{
         frequency = 10;
         position = undefined;
         range = undefined;
@@ -89,12 +101,10 @@
             this.dragpos += movement * units / pixels;
             this.apply_position(this.dragpos);
         }
-    }
     ||
 }
 
-template "widget[@type='ScrollBar']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("ScrollBar") {
     labels("cursor range");
 
     const "pagebuttons" optional_labels("pageup pagedown");
--- a/svghmi/widget_slider.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_slider.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,6 +1,20 @@
 // widget_slider.ysl2
 
-template "widget[@type='Slider']", mode="widget_class"
+widget_desc("Slider") {
+    longdesc
+    || 
+    Slider - DEPRECATED - use ScrollBar or PathSlider instead
+    ||
+
+    shortdesc > Slider - DEPRECATED - use ScrollBar instead
+
+    path name="value" accepts="HMI_INT" > value
+    path name="range" accepts="HMI_INT" > range
+    path name="visible" accepts="HMI_INT" > visible
+    
+}
+
+widget_class("Slider")
     ||
     class SliderWidget extends Widget{
         frequency = 5;
@@ -339,9 +353,7 @@
     }
     ||
 
-template "widget[@type='Slider']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("Slider") {
     labels("handle range");
     optional_labels("value min max setpoint");
-    |,
 }
--- a/svghmi/widget_switch.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_switch.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,8 +1,27 @@
 // widget_switch.ysl2
 
-template "widget[@type='Switch']", mode="widget_class"
+widget_desc("Switch") {
+    longdesc
     ||
-    class SwitchWidget extends Widget{
+    Switch widget hides all subelements whose label do not match given
+    variable current value representation. For exemple if given variable type
+    is HMI_INT and value is 1, then elements with label '1' will be displayed.
+    Label can have comments, so '1#some comment' would also match. If matching
+    variable of type HMI_STRING, then double quotes must be used. For exemple,
+    '"hello"' or '"hello"#another comment' match HMI_STRING 'hello'.
+    ||
+
+    shortdesc > Show elements whose label match value.
+
+    // TODO: add optional format/precision argument to support floating points
+    // TODO: support (in)equations and ranges
+
+    path name="value" accepts="HMI_INT,HMI_STRING" > value to compare to labels
+    
+}
+
+widget_class("Switch")
+    ||
         frequency = 5;
         dispatch(value) {
             for(let choice of this.choices){
@@ -13,11 +32,9 @@
                 }
             }
         }
-    }
     ||
 
-template "widget[@type='Switch']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("Switch") {
     |     choices: [
     const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+|false|true)(#.*)?$'"!;
 
@@ -29,6 +46,7 @@
         const "literal", "regexp:match(@inkscape:label,$regex)[2]";
     |         {
     |             elt:id("«@id»"),
+                  // TODO : use style.display = "none" to hide element
     |             style:"«@style»",
     |             value:«$literal»
     |         }`if "position()!=last()" > ,`
--- a/svghmi/widget_tooglebutton.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widget_tooglebutton.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -1,9 +1,22 @@
 // widget_tooglebutton.ysl2
 
 
-template "widget[@type='ToggleButton']", mode="widget_class"{
+widget_desc("ToggleButton") {
+    longdesc
     ||
-    class ToggleButtonWidget extends Widget{
+    Button widget takes one boolean variable path, and reflect current true
+    or false value by showing "active" or "inactive" labeled element
+    respectively. Clicking or touching button toggles variable.
+    ||
+
+    shortdesc > Toggle button reflecting given boolean variable
+
+    path name="value" accepts="HMI_BOOL" > Boolean variable
+    
+}
+
+widget_class("ToggleButton") {
+    ||
         frequency = 5;
         state = 0;
         active_style = undefined;
@@ -41,11 +54,9 @@
             this.activate(false);
             this.element.onclick = (evt) => this.on_click(evt);
         }
-    }
     ||
 }
 
-template "widget[@type='ToggleButton']", mode="widget_defs" {
-    param "hmi_element";
+widget_defs("ToggleButton") {
     optional_labels("active inactive");
 }
--- a/svghmi/widgetlib/modern_knob_1.svg	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widgetlib/modern_knob_1.svg	Tue May 18 09:28:44 2021 +0200
@@ -159,16 +159,16 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="0.35"
-     inkscape:cx="41.428571"
-     inkscape:cy="555.71429"
+     inkscape:zoom="1.979899"
+     inkscape:cx="208.80035"
+     inkscape:cy="841.9769"
      inkscape:document-units="mm"
-     inkscape:current-layer="svg2637"
+     inkscape:current-layer="g3058"
      showgrid="false"
-     inkscape:window-width="1414"
-     inkscape:window-height="840"
-     inkscape:window-x="1690"
-     inkscape:window-y="117"
+     inkscape:window-width="1623"
+     inkscape:window-height="1446"
+     inkscape:window-x="3346"
+     inkscape:window-y="244"
      inkscape:window-maximized="0" />
   <metadata
      id="metadata2634">
@@ -178,7 +178,7 @@
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
+        <dc:title />
       </cc:Work>
     </rdf:RDF>
   </metadata>
@@ -206,7 +206,7 @@
     <g
        style="fill:none;stroke-width:1.47405899"
        transform="matrix(0.53304115,0,0,0.53229017,1417.5153,1776.3135)"
-       inkscape:label="HMI:CircularBar@/CIRCULARBAR"
+       inkscape:label="HMI:CircularBar@value[HMI_REAL,HMI_INT]0,111@label[HMI_STRING]"
        id="g30664">
       <text
          inkscape:label="value"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/widgetlib/rounded_scrollbar.svg	Tue May 18 09:28:44 2021 +0200
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg1084"
+   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
+   sodipodi:docname="rounded_scrollbar.svg">
+  <defs
+     id="defs1078" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.49497475"
+     inkscape:cx="763.74455"
+     inkscape:cy="633.06732"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="3840"
+     inkscape:window-height="2096"
+     inkscape:window-x="1600"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata1081">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g1766"
+       inkscape:label="HMI:ScrollBar@.range@.position@.visibleAlarms"
+       transform="matrix(0.26458333,0,0,0.26458333,151.87374,-19.244728)">
+      <path
+         sodipodi:nodetypes="cccc"
+         inkscape:connector-curvature="0"
+         id="path1266"
+         d="m -234.01097,332.35504 21.18736,28.36866 h -42.37471 z"
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42391574px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         inkscape:label="pageup" />
+      <path
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.4007318px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         d="m -234.01097,686.72773 21.18736,-27.45222 h -42.37471 z"
+         id="path1268"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cccc"
+         inkscape:label="pagedown" />
+      <rect
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.30952382;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.03627348px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         id="rect1264-3"
+         width="42.374725"
+         height="276.64423"
+         x="-255.19838"
+         y="371.91068"
+         rx="7.6034913"
+         ry="6.8822322"
+         inkscape:label="range" />
+      <rect
+         style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.11429262px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         id="rect1264"
+         width="42.374725"
+         height="82.841492"
+         x="-255.19838"
+         y="371.91068"
+         rx="7.6034913"
+         ry="7"
+         inkscape:label="cursor" />
+    </g>
+  </g>
+</svg>
--- a/svghmi/widgets_common.ysl2	Mon May 17 08:52:38 2021 +0200
+++ b/svghmi/widgets_common.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -21,6 +21,31 @@
     }
 };
 
+in xsl decl widget_desc(%name, match="widget[@type='%name']", mode="widget_desc") alias template {
+    type > «@type»
+    content;
+};
+
+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";