From 0178de4ab9ffd11630b700f9c02468b74beabd14 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 17 Aug 2022 12:31:26 -0400
Subject: fixed dragging docs on web and pdf to work better by temporarily
 adding transparent docs to the opaque layer so they can get drop events.  
 cleaned eraser and pen interaction code and made erasing strokes work faster
 and avoid hanging by not intersecting strokes that are already partially
 deleted.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 140 ++++++---------------
 1 file changed, 36 insertions(+), 104 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 f3074543b..45a5e30ff 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -109,8 +109,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable _hLines: number[] | undefined;
     @observable _vLines: number[] | undefined;
     @observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown.
-    @observable _pullCoords: number[] = [0, 0];
-    @observable _pullDirection: string = '';
     @observable _showAnimTimeline = false;
     @observable _clusterSets: Doc[][] = [];
     @observable _deleteList: DocumentView[] = [];
@@ -465,20 +463,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
         let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props'  used to be renderDepth + 1
-        if (property !== StyleProp.BackgroundColor) return styleProp;
-        const cluster = NumCast(doc?.cluster);
-        if (this.Document._useClusters) {
-            if (this._clusterSets.length <= cluster) {
-                setTimeout(() => doc && this.updateCluster(doc));
-            } else {
-                // choose a cluster color from a palette
-                const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
-                styleProp = colors[cluster % colors.length];
-                const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
-                // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set?.map(s => (styleProp = StrCast(s.backgroundColor)));
-            }
-        } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
+        switch (property) {
+            case StyleProp.BackgroundColor:
+                const cluster = NumCast(doc?.cluster);
+                if (this.Document._useClusters) {
+                    if (this._clusterSets.length <= cluster) {
+                        setTimeout(() => doc && this.updateCluster(doc));
+                    } else {
+                        // choose a cluster color from a palette
+                        const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
+                        styleProp = colors[cluster % colors.length];
+                        const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
+                        // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
+                        set?.map(s => (styleProp = StrCast(s.backgroundColor)));
+                    }
+                }
+                break;
+        }
         return styleProp;
     };
 
@@ -523,17 +524,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     case InkTool.Pen:
                         break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
                     case InkTool.Eraser:
-                        document.addEventListener('pointermove', this.onEraserMove);
-                        document.addEventListener('pointerup', this.onEraserUp);
                         this._batch = UndoManager.StartBatch('collectionErase');
-                        e.stopPropagation();
-                        e.preventDefault();
+                        setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
                         break;
                     case InkTool.None:
                         if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
                             this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
-                            document.addEventListener('pointermove', this.onPointerMove);
-                            document.addEventListener('pointerup', this.onPointerUp);
+                            setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
                         }
                         break;
                 }
@@ -698,23 +695,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @action
     onEraserUp = (e: PointerEvent): void => {
-        if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            document.removeEventListener('pointermove', this.onEraserMove);
-            document.removeEventListener('pointerup', this.onEraserUp);
-            this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
-            this._deleteList = [];
-            this._batch?.end();
-        }
-    };
-
-    @action
-    onPointerUp = (e: PointerEvent): void => {
-        if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            document.removeEventListener('pointermove', this.onPointerMove);
-            document.removeEventListener('pointerup', this.onPointerUp);
-            this.removeMoveListeners();
-            this.removeEndListeners();
-        }
+        this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
+        this._deleteList = [];
+        this._batch?.end();
     };
 
     onClick = (e: React.MouseEvent) => {
@@ -752,46 +735,42 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
      *  However, if Shift is held, then no segmentation is done -- instead any intersected stroke is deleted in its entirety.
      */
     @action
-    onEraserMove = (e: PointerEvent) => {
+    onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const currPoint = { X: e.clientX, Y: e.clientY };
-        this.getEraserIntersections({ X: this._lastX, Y: this._lastY }, currPoint).forEach(intersect => {
+        this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
             if (!this._deleteList.includes(intersect.inkView)) {
                 this._deleteList.push(intersect.inkView);
                 SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1');
                 SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
-                !e.shiftKey &&
+                if (!e.shiftKey) {
                     this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
                         GestureOverlay.Instance.dispatchGesture(
                             GestureUtils.Gestures.Stroke,
                             segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
                         )
                     );
+                }
                 // Lower ink opacity to give the user a visual indicator of deletion.
                 intersect.inkView.layoutDoc.opacity = 0.5;
+                intersect.inkView.layoutDoc.dontIntersect = true;
             }
         });
-        this._lastX = currPoint.X;
-        this._lastY = currPoint.Y;
-
-        e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-        e.preventDefault();
+        return false;
     };
 
     @action
-    onPointerMove = (e: PointerEvent): void => {
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
+    onPointerMove = (e: PointerEvent): boolean => {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             Doc.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
-                document.removeEventListener('pointermove', this.onPointerMove);
-                document.removeEventListener('pointerup', this.onPointerUp);
+                return true;
             } else this.pan(e);
-            e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-            e.preventDefault();
         }
+        return false;
     };
 
     /**
@@ -801,6 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => {
         const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) };
         const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
+
         return this.childDocs
             .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView))
             .filter(inkView => inkView?.ComponentView instanceof InkingStroke)
@@ -888,7 +868,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const tVals: number[] = [];
         // Iterating through all ink strokes in the current freeform collection.
         this.childDocs
-            .filter(doc => doc.type === DocumentType.INK)
+            .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect)
             .forEach(doc => {
                 const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke;
                 const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] };
@@ -919,7 +899,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                         e.preventDefault();
                         document.removeEventListener('pointermove', this.onPointerMove);
-                        document.removeEventListener('pointerup', this.onPointerUp);
                         return;
                     }
                     // TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
@@ -967,13 +946,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
                         // const transformed = this.getTransform().inverse().transformPoint(centerX, centerY);
 
-                        if (!this._pullDirection) {
-                            // if we are not bezel movement
-                            this.pan({ clientX: centerX, clientY: centerY });
-                        } else {
-                            this._pullCoords = [centerX, centerY];
-                        }
-
                         this._lastX = centerX;
                         this._lastY = centerY;
                     }
@@ -998,24 +970,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
                 this._lastX = centerX;
                 this._lastY = centerY;
-                const screenBox = this._mainCont?.getBoundingClientRect();
-
-                // determine if we are using a bezel movement
-                if (screenBox) {
-                    if (screenBox.right - centerX < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'right';
-                    } else if (centerX - screenBox.left < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'left';
-                    } else if (screenBox.bottom - centerY < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'bottom';
-                    } else if (centerY - screenBox.top < 100) {
-                        this._pullCoords = [centerX, centerY];
-                        this._pullDirection = 'top';
-                    }
-                }
 
                 this.removeMoveListeners();
                 this.addMoveListeners();
@@ -1027,19 +981,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
 
     cleanUpInteractions = () => {
-        switch (this._pullDirection) {
-            case 'left':
-            case 'right':
-            case 'top':
-            case 'bottom':
-                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: 'New Collection' }), this._pullDirection);
-        }
-
-        this._pullDirection = '';
-        this._pullCoords = [0, 0];
-
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.removeEventListener('pointerup', this.onPointerUp);
         this.removeMoveListeners();
         this.removeEndListeners();
     };
@@ -1380,7 +1321,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const { z, color, zIndex } = params.pair.layout;
         const { x, y, opacity } =
             this.Document._currentFrame === undefined
-                ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
+                ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.childOpacity ? this.props.childOpacity() : this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
                 : CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
         return {
             x: NumCast(x),
@@ -1915,7 +1856,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 getContainerTransform={this.getContainerTransform}
                 getTransform={this.getTransform}
                 isAnnotationOverlay={this.isAnnotationOverlay}>
-                <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
+                <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
                     {this.layoutDoc._backgroundGridShow ? (
                         <div>
                             <CollectionFreeFormBackgroundGrid // bcz : UGHH  don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
@@ -1996,15 +1937,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 {this._firstRender ? this.placeholder : this.marqueeView}
                 {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
 
-                <div
-                    className={'pullpane-indicator'}
-                    style={{
-                        display: this._pullDirection ? 'block' : 'none',
-                        top: clientRect ? (this._pullDirection === 'bottom' ? this._pullCoords[1] - clientRect.y : 0) : 'auto',
-                        left: clientRect ? (this._pullDirection === 'right' ? this._pullCoords[0] - clientRect.x : 0) : 'auto',
-                        width: clientRect ? (this._pullDirection === 'left' ? this._pullCoords[0] - clientRect.left : this._pullDirection === 'right' ? clientRect.right - this._pullCoords[0] : clientRect.width) : 0,
-                        height: clientRect ? (this._pullDirection === 'top' ? this._pullCoords[1] - clientRect.top : this._pullDirection === 'bottom' ? clientRect.bottom - this._pullCoords[1] : clientRect.height) : 0,
-                    }}></div>
                 {
                     // uncomment to show snap lines
                     <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
-- 
cgit v1.2.3-70-g09d2

