--- a/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:46:26 2022 +0200
+++ b/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:53:35 2022 +0200
@@ -1251,14 +1251,14 @@
inkscape:pageopacity="0.0"
- inkscape:cx="205.65994"
- inkscape:cy="103.00174"
+ inkscape:cx="52.116754" + inkscape:cy="96.940825" inkscape:document-units="px"
inkscape:current-layer="layer1"
- inkscape:window-width="1600"
- inkscape:window-height="836"
+ inkscape:window-width="3840" + inkscape:window-height="2096" inkscape:window-maximized="1"
@@ -1274,7 +1274,7 @@
<dc:format>image/svg+xml</dc:format>
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
@@ -1283,6 +1283,14 @@
inkscape:groupmode="layer"
transform="translate(37.474617,-760.93329)">
+ style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" + inkscape:label="HMI:Page:Home" /> style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#282828;fill-opacity:1;stroke:none;stroke-width:2.04116011;marker:none;enable-background:accumulate"
d="m 114.28125,14.28125 v 130 h 18.9375 v 93.5625 h 5.71875 V 176.4375 h 8.90625 v 15.71875 h 36.4375 v -32.5 h -36.4375 v 12.125 h -8.90625 v -27.5 h 21.78125 v -130 z"
@@ -1529,13 +1537,5 @@
style="font-size:6.17188501px;line-height:1.25;font-family:sans-serif">ON</tspan></text>
- style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
- inkscape:label="HMI:Page:Home" />
--- a/svghmi/gen_index_xhtml.xslt Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/gen_index_xhtml.xslt Sun Jul 17 22:53:35 2022 +0200
@@ -2059,6 +2059,10 @@
+ <xsl:message terminate="no"> + <xsl:value-of select="@type"/> + <xsl:text> widget is used in SVG but widget type is not declared</xsl:text> <xsl:variable name="included_ids" select="$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id"/>
<xsl:variable name="hmi_widgets" select="$hmi_elements[@id = $included_ids]"/>
@@ -2080,6 +2084,8 @@
<xsl:template name="defs_by_labels">
<xsl:param name="labels" select="''"/>
@@ -4880,6 +4886,7 @@
<xsl:variable name="have_edit" select="string-length($edit_elt)>0"/>
<xsl:value-of select="$edit_elt"/>
+ <xsl:variable name="action_elements" select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]"/> <xsl:if test="$have_value">
@@ -4920,6 +4927,14 @@
+ <xsl:for-each select="$action_elements"> + <xsl:text> action_elt_</xsl:text> + <xsl:value-of select="position()"/> + <xsl:text>: id("</xsl:text> + <xsl:value-of select="@id"/> <xsl:text> init: function() {
<xsl:if test="$have_edit">
@@ -4936,10 +4951,10 @@
<xsl:text> this.animate();
- <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
- <xsl:text> id("</xsl:text>
- <xsl:value-of select="@id"/>
- <xsl:text>").onclick = () => this.on_op_click("</xsl:text>
+ <xsl:for-each select="$action_elements"> + <xsl:text> this.action_elt_</xsl:text> + <xsl:value-of select="position()"/> + <xsl:text>.onclick = () => this.on_op_click("</xsl:text> <xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
@@ -5696,7 +5711,7 @@
<xsl:text>.fade-out-page {
- <xsl:text> animation: fadeOut 0.6s both;
+ <xsl:text> animation: cubic-bezier(0, 0.8, 0.6, 1) fadeOut 0.6s both; @@ -6105,6 +6120,14 @@
+ <xsl:template match="widget[@type='List']" mode="widget_class"> + <xsl:text>class </xsl:text> + <xsl:text>ListWidget</xsl:text> + <xsl:text> extends Widget{ <xsl:template match="widget[@type='ListSwitch']" mode="widget_desc">
<xsl:value-of select="@type"/>
@@ -7867,6 +7890,14 @@
+ <xsl:template match="widget[@type='TextList']" mode="widget_class"> + <xsl:text>class </xsl:text> + <xsl:text>TextListWidget</xsl:text> + <xsl:text> extends Widget{ <xsl:template match="widget[@type='TextStyleList']" mode="widget_desc">
<xsl:value-of select="@type"/>
@@ -7908,6 +7939,14 @@
+ <xsl:template match="widget[@type='TextStyleList']" mode="widget_class"> + <xsl:text>class </xsl:text> + <xsl:text>TextStyleListWidget</xsl:text> + <xsl:text> extends Widget{ <xsl:template match="widget[@type='ToggleButton']" mode="widget_desc">
<xsl:value-of select="@type"/>
@@ -10499,40 +10538,92 @@
<xsl:text>function animate() {
- <xsl:text> // Do the page swith if any one pending
- <xsl:text> if(current_subscribed_page != current_visible_page){
- <xsl:text> switch_visible_page(current_subscribed_page);
- <xsl:text> while(widget = need_cache_apply.pop()){
- <xsl:text> widget.apply_cache();
- <xsl:text> if(jumps_need_update) update_jumps();
- <xsl:text> apply_updates();
- <xsl:text> pending_widget_animates.forEach(widget => widget._animate());
- <xsl:text> pending_widget_animates = [];
+ <xsl:text> let rearm = true; + <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ + <xsl:text> if(page_fading == "pending") + <xsl:text> svg_root.classList.add("fade-out-page"); + <xsl:text> page_fading = "in_progress"; + <xsl:text> if(page_fading_args.length) + <xsl:text> setTimeout(function(){ + <xsl:text> switch_page(...page_fading_args); + <xsl:text> // Do the page swith if pending + <xsl:text> if(page_switch_in_progress){ + <xsl:text> if(current_subscribed_page != current_visible_page){ + <xsl:text> switch_visible_page(current_subscribed_page); + <xsl:text> page_switch_in_progress = false; + <xsl:text> if(page_fading == "in_progress"){ + <xsl:text> svg_root.classList.remove("fade-out-page"); + <xsl:text> page_fading = "off"; + <xsl:text> while(widget = need_cache_apply.pop()){ + <xsl:text> widget.apply_cache(); + <xsl:text> if(jumps_need_update) update_jumps(); + <xsl:text> apply_updates(); + <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); + <xsl:text> pending_widget_animates = []; + <xsl:text> rearm = false; <xsl:text> requestAnimationFrameID = null;
+ <xsl:text> if(rearm) requestHMIAnimation(); @@ -10787,21 +10878,27 @@
- <xsl:text>var page_fading_in_progress = false;
+ <xsl:text>var page_fading = "off"; + <xsl:text>var page_fading_args = "off"; <xsl:text>function fading_page_switch(...args){
- <xsl:text> svg_root.classList.add("fade-out-page");
- <xsl:text> page_fading_in_progress = true;
- <xsl:text> setTimeout(function(){
- <xsl:text> switch_page(...args);
+ <xsl:text> if(page_fading == "in_progress") + <xsl:text> page_fading = "forced"; + <xsl:text> page_fading = "pending"; + <xsl:text> page_fading_args = args; + <xsl:text> requestHMIAnimation(); @@ -11163,6 +11260,8 @@
<xsl:text>var page_node_local_index = hmi_local_index("page_node");
+ <xsl:text>var page_switch_in_progress = false; <xsl:text>function toggleFullscreen() {
@@ -11217,7 +11316,7 @@
<xsl:text>function switch_page(page_name, page_index) {
- <xsl:text> if(current_subscribed_page != current_visible_page){
+ <xsl:text> if(page_switch_in_progress){ <xsl:text> /* page switch already going */
@@ -11227,6 +11326,8 @@
+ <xsl:text> page_switch_in_progress = true; <xsl:text> if(page_name == undefined)
@@ -11409,12 +11510,6 @@
<xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
- <xsl:text> if(page_fading_in_progress)
- <xsl:text> svg_root.classList.remove("fade-out-page");
- <xsl:text> page_fading_in_progress = false;
<xsl:text> current_visible_page = page_name;
@@ -11539,6 +11634,24 @@
+// Declarations from SVG scripts (inkscape document properties) + <xsl:for-each select="/svg:svg/svg:script"> + <xsl:text>/* </xsl:text> + <xsl:value-of select="@id"/> + <xsl:value-of select="text()"/> --- a/svghmi/svghmi.js Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/svghmi.js Sun Jul 17 22:53:35 2022 +0200
@@ -59,32 +59,49 @@
// Called on requestAnimationFrame, modifies DOM
var requestAnimationFrameID = null;
- // Do the page swith if any one pending
- if(page_switch_in_progress){
- if(current_subscribed_page != current_visible_page){
- switch_visible_page(current_subscribed_page);
+ if(page_fading == "pending" || page_fading == "forced"){ + if(page_fading == "pending") + svg_root.classList.add("fade-out-page"); + page_fading = "in_progress"; + if(page_fading_args.length) + switch_page(...page_fading_args); - page_switch_in_progress = false;
+ // Do the page swith if pending + if(page_switch_in_progress){ + if(current_subscribed_page != current_visible_page){ + switch_visible_page(current_subscribed_page); - if(page_fading_in_progress){
- svg_root.classList.remove("fade-out-page");
- page_fading_in_progress = false;
+ page_switch_in_progress = false; - while(widget = need_cache_apply.pop()){
+ if(page_fading == "in_progress"){ + svg_root.classList.remove("fade-out-page"); - if(jumps_need_update) update_jumps();
+ while(widget = need_cache_apply.pop()){ + if(jumps_need_update) update_jumps();
- pending_widget_animates.forEach(widget => widget._animate());
- pending_widget_animates = [];
+ pending_widget_animates.forEach(widget => widget._animate()); + pending_widget_animates = []; requestAnimationFrameID = null;
+ if(rearm) requestHMIAnimation(); function requestHMIAnimation() {
@@ -212,14 +229,17 @@
-var page_fading_in_progress = false;
+var page_fading = "off"; +var page_fading_args = "off"; function fading_page_switch(...args){
- svg_root.classList.add("fade-out-page");
- page_fading_in_progress = true;
+ if(page_fading == "in_progress") + page_fading = "forced"; + page_fading = "pending"; + page_fading_args = args;
document.body.style.backgroundColor = "black";
--- a/svghmi/widget_textlist.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widget_textlist.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -26,3 +26,5 @@
// could find a proper way in xpath to reverse()
+widget_class("TextList"); --- a/svghmi/widgets_common.ysl2 Sun Jul 17 22:46:26 2022 +0200
+++ b/svghmi/widgets_common.ysl2 Sun Jul 17 22:53:35 2022 +0200
@@ -477,14 +477,17 @@
generate-id() = generate-id(key('TypesKey', @type)) and
not(@type = $excluded_types)]""";
apply "$used_widget_types", mode="widget_class";
-template "widget", mode="widget_class"
-class «@type»Widget extends Widget{
- /* empty class, as «@type» widget didn't provide any */
+template "widget", mode="widget_class" { + class «@type»Widget extends Widget{ + /* empty class, as «@type» widget didn't provide any */ + warning > «@type» widget is used in SVG but widget type is not declared
const "included_ids","$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id";
const "hmi_widgets","$hmi_elements[@id = $included_ids]";
@@ -494,6 +497,7 @@
apply "$hmi_widgets", mode="hmi_widgets";
function "defs_by_labels" {
--- a/tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:46:26 2022 +0200
+++ b/tests/projects/svghmi/svghmi_0@svghmi/svghmi.svg Sun Jul 17 22:53:35 2022 +0200
@@ -18,6 +18,14 @@
+hmi_widgets["g443-3"].off_action = function(){ + console.log("Hello from Inkscape"); + PushButtonWidget.prototype.off_action.call(this); + console.log("Bye from Inkscape"); @@ -125,12 +133,12 @@
inkscape:document-units="px"
- inkscape:current-layer="g5053"
+ inkscape:current-layer="hmi0" - inkscape:zoom="0.80184804"
- inkscape:cx="784.66046"
- inkscape:cy="-449.34319"
+ inkscape:zoom="0.14174805" + inkscape:cx="-1530.0784" + inkscape:cy="-1404.9832" inkscape:window-width="1600"
inkscape:window-height="836"
@@ -8399,4 +8407,47 @@
sodipodi:role="line">Freq4</tspan></text>
+ transform="matrix(0.57180538,0,0,0.57180538,-77.992226,121.53383)" + inkscape:label="HMI:PushButton@/SELECTION" + style="stroke-width:1"> + 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:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;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="inactive" + inkscape:label="active" + 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:#fdfdfd;fill-opacity:1;fill-rule:nonzero;stroke:#ffd0b2;stroke-width:28.60938263;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" /> + style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;display:inline;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:label="setting_jmp"><tspan + style="text-align:center;text-anchor:middle;fill:#ff6600;stroke-width:0.99999994px">up</tspan></text>