From eec81f7e0b53395e3e2ea25663a9ea06ec83085d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Nov 2023 19:30:19 -0400
Subject: performance fixes - don't invalidate as much by using reactions in
 place of computd values;  don't make things active when things are dragged
 unless CanEmbed; fix for linkBox to use reaction.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 107 +++++++++++++--------
 1 file changed, 66 insertions(+), 41 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d80ea2cb2..2897cac0e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1280,12 +1280,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @computed get childPointerEvents() {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents = DocumentView.Interacting
+        const pointerevents = DocumentView.Interacting
             ? 'none'
-            : this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
-        return pointerEvents;
+            : this.props.childPointerEvents?.() ??
+              (this.props.viewDefDivClick || //
+              (engine === computePassLayout.name && !this.props.isSelected(true)) ||
+              this.isContentActive() === false
+                  ? 'none'
+                  : this.props.pointerEvents?.());
+        console.log(`${this.rootDoc.title} pe = ` + pointerevents);
+        return pointerevents;
     }
-    childPointerEventsFunc = () => this.childPointerEvents;
+
+    @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined;
+    childPointerEventsFunc = () => this._childPointerEvents;
     childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
     getChildDocView(entry: PoolData) {
         const childLayout = entry.pair.layout;
@@ -1372,9 +1380,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     });
     @observable _lightboxDoc: Opt<Doc>;
 
-    getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
+    getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
         const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
-        const childDoc = params.pair.layout;
+        const childDoc = pair.layout;
         const childDocLayout = Doc.Layout(childDoc);
         const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
         const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
@@ -1400,7 +1408,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             width: _width,
             height: _height,
             transition: StrCast(childDocLayout.dataTransition),
-            pair: params.pair,
+            pair,
             pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
             replica: '',
         };
@@ -1480,7 +1488,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     doFreeformLayout(poolData: Map<string, PoolData>) {
-        this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document })));
+        this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions(pair)));
         return [] as ViewDefResult[];
     }
 
@@ -1502,38 +1510,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @observable _numLoaded = 1;
     _lastPoolSize = 0;
-    get doLayoutComputation() {
-        const { newPool, computedElementData } = this.doInternalLayoutComputation;
+    @action
+    doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
         const array = Array.from(newPool.entries());
-        let somethingChanged = array.length !== this._lastPoolSize;
         this._lastPoolSize = array.length;
-        runInAction(() => {
-            for (const entry of array) {
-                const lastPos = this._cachedPool.get(entry[0]); // last computed pos
-                const newPos = entry[1];
-                if (
-                    !lastPos ||
-                    newPos.color !== lastPos.color ||
-                    newPos.backgroundColor !== lastPos.backgroundColor ||
-                    newPos.opacity !== lastPos.opacity ||
-                    newPos.x !== lastPos.x ||
-                    newPos.y !== lastPos.y ||
-                    newPos.z !== lastPos.z ||
-                    newPos.rotation !== lastPos.rotation ||
-                    newPos.zIndex !== lastPos.zIndex ||
-                    newPos.transition !== lastPos.transition ||
-                    newPos.pointerEvents !== lastPos.pointerEvents
-                ) {
-                    this._layoutPoolData.set(entry[0], newPos);
-                    somethingChanged = true;
-                }
-                if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
-                    this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
-                    somethingChanged = true;
-                }
+        for (const entry of array) {
+            const lastPos = this._cachedPool.get(entry[0]); // last computed pos
+            const newPos = entry[1];
+            if (
+                !lastPos ||
+                newPos.color !== lastPos.color ||
+                newPos.backgroundColor !== lastPos.backgroundColor ||
+                newPos.opacity !== lastPos.opacity ||
+                newPos.x !== lastPos.x ||
+                newPos.y !== lastPos.y ||
+                newPos.z !== lastPos.z ||
+                newPos.rotation !== lastPos.rotation ||
+                newPos.zIndex !== lastPos.zIndex ||
+                newPos.transition !== lastPos.transition ||
+                newPos.pointerEvents !== lastPos.pointerEvents
+            ) {
+                this._layoutPoolData.set(entry[0], newPos);
             }
-        });
-        if (!somethingChanged) return undefined;
+            if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
+                this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
+            }
+        }
+        // by returning undefined, we prevent an edit being made to layoutElements when nothing has happened
+        // this short circuit, prevents lots of downstream mobx invalidations which would have no effect but cause
+        // a distinct lag at the start of dragging.
+        // The reason we're here in the first place without a change is that when dragging a document,
+        // filters are changed on the annotation layers (eg. WebBox) which invalidate the childDoc list
+        // for the overlay views -- however, in many cases, this filter change doesn't actually affect anything
+        // (e.g, no annotations, or only opaque annotations).
         this._cachedPool.clear();
         Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
         const elements = computedElementData.slice();
@@ -1551,7 +1560,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
         return elements;
-    }
+    };
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
@@ -1605,10 +1614,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     { fireImmediately: true }
                 );
 
+                this._disposers.pointerevents = reaction(
+                    () => {
+                        const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
+                        return DocumentView.Interacting
+                            ? 'none'
+                            : this.props.childPointerEvents?.() ??
+                                  (this.props.viewDefDivClick || //
+                                  (engine === computePassLayout.name && !this.props.isSelected(true)) ||
+                                  this.isContentActive() === false
+                                      ? 'none'
+                                      : this.props.pointerEvents?.());
+                    },
+                    pointerevents => (this._childPointerEvents = pointerevents as any),
+                    { fireImmediately: true }
+                );
+
                 this._disposers.layoutComputation = reaction(
-                    () => this.doLayoutComputation,
-                    elements => elements !== undefined && (this._layoutElements = elements || []),
-                    { fireImmediately: true, name: 'doLayout' }
+                    () => this.doInternalLayoutComputation,
+                    ({ newPool, computedElementData }) => (this._layoutElements = this.doLayoutComputation(newPool, computedElementData)),
+                    { fireImmediately: true, name: 'layoutComputationReaction' }
                 );
 
                 this._disposers.active = reaction(
-- 
cgit v1.2.3-70-g09d2

