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. svghmi
authorEdouard Tisserant
Tue, 10 Mar 2020 13:57:29 +0100
branchsvghmi
changeset 2864 36f78f6cfabd
parent 2863 e120d6985a2f
child 2865 545902730141
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.
svghmi/gen_index_xhtml.xslt
svghmi/svghmi.js
--- 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>
--- 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;
 };