# HG changeset patch
# User Edouard Tisserant
# Date 1583845049 -3600
# Node ID 36f78f6cfabd2973a5579cd43c78e276a28eba0e
# Parent  e120d6985a2f905233a7e6891b6ab2c62d65f26d
SVGHMI: split page switch into switching subscription and switching elements in the DOM, to ensure that subscriptions have been send before changing DOM, and avoid some flicker.

diff -r e120d6985a2f -r 36f78f6cfabd svghmi/gen_index_xhtml.xslt
--- a/svghmi/gen_index_xhtml.xslt	Mon Mar 09 13:43:34 2020 +0100
+++ b/svghmi/gen_index_xhtml.xslt	Tue Mar 10 13:57:29 2020 +0100
@@ -625,8 +625,6 @@
 </xsl:text>
     <xsl:text>var updates = {};
 </xsl:text>
-    <xsl:text>var page_switch = null;
-</xsl:text>
     <xsl:text>
 </xsl:text>
     <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) {
@@ -761,11 +759,9 @@
 </xsl:text>
     <xsl:text>function animate() {
 </xsl:text>
-    <xsl:text>    if(page_switch != null){
-</xsl:text>
-    <xsl:text>        do_switch_page(page_switch);
-</xsl:text>
-    <xsl:text>        page_switch=null;
+    <xsl:text>    if(current_subscribed_page != current_visible_page){
+</xsl:text>
+    <xsl:text>        switch_visible_page(current_subscribed_page);
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
@@ -783,6 +779,8 @@
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
+    <xsl:text>    requestAnimationFrameID = null;
+</xsl:text>
     <xsl:text>}
 </xsl:text>
     <xsl:text>
@@ -791,15 +789,11 @@
 </xsl:text>
     <xsl:text>function requestHMIAnimation() {
 </xsl:text>
-    <xsl:text>    if(requestAnimationFrameID != null){
-</xsl:text>
-    <xsl:text>        window.cancelAnimationFrame(requestAnimationFrameID);
-</xsl:text>
-    <xsl:text>        requestAnimationFrameID = null;
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    requestAnimationFrameID = window.requestAnimationFrame(animate);
+    <xsl:text>    if(requestAnimationFrameID == null){
+</xsl:text>
+    <xsl:text>        requestAnimationFrameID = window.requestAnimationFrame(animate);
+</xsl:text>
+    <xsl:text>    }
 </xsl:text>
     <xsl:text>}
 </xsl:text>
@@ -865,7 +859,7 @@
 </xsl:text>
     <xsl:text>        // register for rendering on next frame, since there are updates
 </xsl:text>
-    <xsl:text>        window.requestAnimationFrame(animate);
+    <xsl:text>        requestHMIAnimation();
 </xsl:text>
     <xsl:text>    } catch(err) {
 </xsl:text>
@@ -1115,7 +1109,9 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>var current_page;
+    <xsl:text>var current_visible_page;
+</xsl:text>
+    <xsl:text>var current_subscribed_page;
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -1135,77 +1131,153 @@
 </xsl:text>
     <xsl:text>function switch_page(page_name) {
 </xsl:text>
-    <xsl:text>    page_switch = page_name;
-</xsl:text>
-    <xsl:text>    window.requestAnimationFrame(animate);
+    <xsl:text>    if(current_subscribed_page != current_visible_page){
+</xsl:text>
+    <xsl:text>        /* page switch already going */
+</xsl:text>
+    <xsl:text>        /* TODO LOG ERROR */
+</xsl:text>
+    <xsl:text>        return;
+</xsl:text>
+    <xsl:text>    } else if(page_name == current_visible_page){
+</xsl:text>
+    <xsl:text>        /* already in that page */
+</xsl:text>
+    <xsl:text>        /* TODO LOG ERROR */
+</xsl:text>
+    <xsl:text>        return;
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>    switch_subscribed_page(page_name);
+</xsl:text>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>function switch_subscribed_page(page_name) {
+</xsl:text>
+    <xsl:text>    let old_desc = page_desc[current_subscribed_page];
+</xsl:text>
+    <xsl:text>    let new_desc = page_desc[page_name];
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    if(new_desc == undefined){
+</xsl:text>
+    <xsl:text>        /* TODO LOG ERROR */
+</xsl:text>
+    <xsl:text>        return;
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    if(old_desc){
+</xsl:text>
+    <xsl:text>        for(let widget of old_desc.widgets){
+</xsl:text>
+    <xsl:text>            /* remove subsribers */
+</xsl:text>
+    <xsl:text>            for(let index of widget.indexes){
+</xsl:text>
+    <xsl:text>                subscribers[index].delete(widget);
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>    for(let widget of new_desc.widgets){
+</xsl:text>
+    <xsl:text>        /* add widget's subsribers */
+</xsl:text>
+    <xsl:text>        for(let index of widget.indexes){
+</xsl:text>
+    <xsl:text>            subscribers[index].add(widget);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    update_subscriptions();
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    current_subscribed_page = page_name;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    requestHMIAnimation();
 </xsl:text>
     <xsl:text>}
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>function do_switch_page(page_name) {
-</xsl:text>
-    <xsl:text>    let old_desc = page_desc[current_page];
+    <xsl:text>function switch_visible_page(page_name) {
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    let old_desc = page_desc[current_visible_page];
 </xsl:text>
     <xsl:text>    let new_desc = page_desc[page_name];
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    if(new_desc == undefined){
-</xsl:text>
-    <xsl:text>        /* TODO LOG ERROR */
-</xsl:text>
-    <xsl:text>        return;
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
     <xsl:text>    if(old_desc){
 </xsl:text>
-    <xsl:text>        for(let widget of old_desc.widgets){
-</xsl:text>
-    <xsl:text>            /* remove subsribers */
-</xsl:text>
-    <xsl:text>            for(let index of widget.indexes){
-</xsl:text>
-    <xsl:text>                subscribers[index].delete(widget);
+    <xsl:text>        for(let eltid in old_desc.required_detachables){
+</xsl:text>
+    <xsl:text>            if(!(eltid in new_desc.required_detachables)){
+</xsl:text>
+    <xsl:text>                let [element, parent] = old_desc.required_detachables[eltid];
+</xsl:text>
+    <xsl:text>                parent.removeChild(element);
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>        for(let eltid in old_desc.required_detachables){
-</xsl:text>
-    <xsl:text>            if(!(eltid in new_desc.required_detachables)){
-</xsl:text>
-    <xsl:text>                let [element, parent] = old_desc.required_detachables[eltid];
-</xsl:text>
-    <xsl:text>                parent.removeChild(element);
+    <xsl:text>        for(let eltid in new_desc.required_detachables){
+</xsl:text>
+    <xsl:text>            if(!(eltid in old_desc.required_detachables)){
+</xsl:text>
+    <xsl:text>                let [element, parent] = new_desc.required_detachables[eltid];
+</xsl:text>
+    <xsl:text>                parent.appendChild(element);
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
+    <xsl:text>    }else{
+</xsl:text>
     <xsl:text>        for(let eltid in new_desc.required_detachables){
 </xsl:text>
-    <xsl:text>            if(!(eltid in old_desc.required_detachables)){
-</xsl:text>
-    <xsl:text>                let [element, parent] = new_desc.required_detachables[eltid];
-</xsl:text>
-    <xsl:text>                parent.appendChild(element);
-</xsl:text>
-    <xsl:text>            }
+    <xsl:text>            let [element, parent] = new_desc.required_detachables[eltid];
+</xsl:text>
+    <xsl:text>            parent.appendChild(element);
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>    }else{
-</xsl:text>
-    <xsl:text>        for(let eltid in new_desc.required_detachables){
-</xsl:text>
-    <xsl:text>            let [element, parent] = new_desc.required_detachables[eltid];
-</xsl:text>
-    <xsl:text>            parent.appendChild(element);
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    for(let widget of new_desc.widgets){
+</xsl:text>
+    <xsl:text>        for(let index of widget.indexes){
+</xsl:text>
+    <xsl:text>            /* dispatch current cache in newly opened page widgets */
+</xsl:text>
+    <xsl:text>            let cached_val = cache[index];
+</xsl:text>
+    <xsl:text>            if(cached_val != undefined)
+</xsl:text>
+    <xsl:text>                dispatch_value_to_widget(widget, index, cached_val, cached_val);
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
@@ -1213,35 +1285,9 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>    for(let widget of new_desc.widgets){
-</xsl:text>
-    <xsl:text>        /* add widget's subsribers */
-</xsl:text>
-    <xsl:text>        for(let index of widget.indexes){
-</xsl:text>
-    <xsl:text>            subscribers[index].add(widget);
-</xsl:text>
-    <xsl:text>            /* dispatch current cache in newly opened page widgets */
-</xsl:text>
-    <xsl:text>            let cached_val = cache[index];
-</xsl:text>
-    <xsl:text>            if(cached_val != undefined)
-</xsl:text>
-    <xsl:text>                dispatch_value_to_widget(widget, index, cached_val, cached_val);
-</xsl:text>
-    <xsl:text>        }
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
     <xsl:text>    svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
 </xsl:text>
-    <xsl:text>    current_page = page_name;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    window.setTimeout(update_subscriptions,0);
+    <xsl:text>    current_visible_page = page_name;
 </xsl:text>
     <xsl:text>};
 </xsl:text>
diff -r e120d6985a2f -r 36f78f6cfabd svghmi/svghmi.js
--- a/svghmi/svghmi.js	Mon Mar 09 13:43:34 2020 +0100
+++ b/svghmi/svghmi.js	Tue Mar 10 13:57:29 2020 +0100
@@ -2,7 +2,6 @@
 
 var cache = hmitree_types.map(_ignored => undefined);
 var updates = {};
-var page_switch = null;
 
 function dispatch_value_to_widget(widget, index, value, oldval) {
     try {
@@ -70,9 +69,8 @@
 // Do the page swith if any one pending
 // Called on requestAnimationFrame, modifies DOM
 function animate() {
-    if(page_switch != null){
-        do_switch_page(page_switch);
-        page_switch=null;
+    if(current_subscribed_page != current_visible_page){
+        switch_visible_page(current_subscribed_page);
     }
 
     for(let index in updates){
@@ -81,15 +79,14 @@
         dispatch_value(Number(index), updates[index]);
         delete updates[index];
     }
+    requestAnimationFrameID = null;
 }
 
 var requestAnimationFrameID = null;
 function requestHMIAnimation() {
-    if(requestAnimationFrameID != null){
-        window.cancelAnimationFrame(requestAnimationFrameID);
-        requestAnimationFrameID = null;
-    }
-    requestAnimationFrameID = window.requestAnimationFrame(animate);
+    if(requestAnimationFrameID == null){
+        requestAnimationFrameID = window.requestAnimationFrame(animate);
+    }
 }
 
 // Message reception handler
@@ -122,7 +119,7 @@
             }
         };
         // register for rendering on next frame, since there are updates
-        window.requestAnimationFrame(animate);
+        requestHMIAnimation();
     } catch(err) {
         // 1003 is for "Unsupported Data"
         // ws.close(1003, err.message);
@@ -247,7 +244,8 @@
     return new_val;
 }
 
-var current_page;
+var current_visible_page;
+var current_subscribed_page;
 
 function prepare_svg() {
     for(let eltid in detachable_elements){
@@ -257,12 +255,20 @@
 };
 
 function switch_page(page_name) {
-    page_switch = page_name;
-    window.requestAnimationFrame(animate);
-}
-
-function do_switch_page(page_name) {
-    let old_desc = page_desc[current_page];
+    if(current_subscribed_page != current_visible_page){
+        /* page switch already going */
+        /* TODO LOG ERROR */
+        return;
+    } else if(page_name == current_visible_page){
+        /* already in that page */
+        /* TODO LOG ERROR */
+        return;
+    }
+    switch_subscribed_page(page_name);
+};
+
+function switch_subscribed_page(page_name) {
+    let old_desc = page_desc[current_subscribed_page];
     let new_desc = page_desc[page_name];
 
     if(new_desc == undefined){
@@ -277,6 +283,27 @@
                 subscribers[index].delete(widget);
             }
         }
+    }
+    for(let widget of new_desc.widgets){
+        /* add widget's subsribers */
+        for(let index of widget.indexes){
+            subscribers[index].add(widget);
+        }
+    }
+
+    update_subscriptions();
+
+    current_subscribed_page = page_name;
+
+    requestHMIAnimation();
+}
+
+function switch_visible_page(page_name) {
+
+    let old_desc = page_desc[current_visible_page];
+    let new_desc = page_desc[page_name];
+
+    if(old_desc){
         for(let eltid in old_desc.required_detachables){
             if(!(eltid in new_desc.required_detachables)){
                 let [element, parent] = old_desc.required_detachables[eltid];
@@ -297,9 +324,7 @@
     }
 
     for(let widget of new_desc.widgets){
-        /* add widget's subsribers */
         for(let index of widget.indexes){
-            subscribers[index].add(widget);
             /* dispatch current cache in newly opened page widgets */
             let cached_val = cache[index];
             if(cached_val != undefined)
@@ -308,9 +333,7 @@
     }
 
     svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
-    current_page = page_name;
-
-    window.setTimeout(update_subscriptions,0);
+    current_visible_page = page_name;
 };