From eb220da697e2383b1e368dee743613158994746e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 31 Mar 2019 17:58:11 -0400
Subject: restructured marquee and preview cursor to not need reactions. fixed
 some inking bugs.

---
 .../collections/collectionFreeForm/MarqueeView.tsx | 169 ++++++++++++---------
 1 file changed, 97 insertions(+), 72 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 2692690dd..2b0e7d228 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,62 +1,58 @@
-import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../../fields/Document";
-import { FieldWaiting, Opt } from "../../../../fields/Field";
+import { FieldWaiting } from "../../../../fields/Field";
+import { InkField, StrokeData } from "../../../../fields/InkField";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { Documents } from "../../../documents/Documents";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { Transform } from "../../../util/Transform";
+import { InkingCanvas } from "../../InkingCanvas";
 import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
+import { PreviewCursor } from "./PreviewCursor";
 import React = require("react");
-import { InkField, StrokeData } from "../../../../fields/InkField";
-import { Utils } from "../../../../Utils";
-import { InkingCanvas } from "../../InkingCanvas";
 
 interface MarqueeViewProps {
-    getMarqueeTransform: () => Transform;
+    getContainerTransform: () => Transform;
     getTransform: () => Transform;
     container: CollectionFreeFormView;
     addDocument: (doc: Document, allowDuplicates: false) => boolean;
     activeDocuments: () => Document[];
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
+    addLiveTextDocument: (doc: Document) => void;
 }
 
 @observer
 export class MarqueeView extends React.Component<MarqueeViewProps>
 {
-    private _reactionDisposer: Opt<IReactionDisposer>;
-
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
     @observable _downX: number = 0;
     @observable _downY: number = 0;
-
-    componentDidMount() {
-        this._reactionDisposer = reaction(
-            () => this.props.container.MarqueeVisible,
-            (visible: boolean) => this.onPointerDown(visible, this.props.container.DownX, this.props.container.DownY))
-    }
-    componentWillUnmount() {
-        if (this._reactionDisposer) {
-            this._reactionDisposer();
-        }
-        this.cleanupInteractions();
-    }
+    @observable _used: boolean = false;
+    @observable _visible: boolean = false;
+    static DRAG_THRESHOLD = 4;
 
     @action
-    cleanupInteractions = () => {
-        document.removeEventListener("pointermove", this.onPointerMove, true)
-        document.removeEventListener("pointerup", this.onPointerUp, true);
+    cleanupInteractions = (all: boolean = false) => {
+        if (all) {
+            document.removeEventListener("pointermove", this.onPointerMove, true)
+            document.removeEventListener("pointerup", this.onPointerUp, true);
+        } else {
+            this._used = true;
+        }
         document.removeEventListener("keydown", this.marqueeCommand, true);
+        this._visible = false;
     }
 
     @action
-    onPointerDown = (visible: boolean, downX: number, downY: number): void => {
-        if (visible) {
-            this._downX = this._lastX = downX;
-            this._downY = this._lastY = downY;
+    onPointerDown = (e: React.PointerEvent): void => {
+        if (e.buttons == 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
+            this._downX = this._lastX = e.pageX;
+            this._downY = this._lastY = e.pageY;
+            this._used = false;
             document.addEventListener("pointermove", this.onPointerMove, true)
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
@@ -67,11 +63,20 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     onPointerMove = (e: PointerEvent): void => {
         this._lastX = e.pageX;
         this._lastY = e.pageY;
+        if (!e.cancelBubble) {
+            if (!this._used && e.buttons == 1 && !e.altKey && !e.metaKey &&
+                (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
+                this._visible = true;
+            }
+            e.stopPropagation();
+            e.preventDefault();
+        }
     }
 
     @action
     onPointerUp = (e: PointerEvent): void => {
-        this.cleanupInteractions();
+        this.cleanupInteractions(true);
+        this._visible = false;
         if (!e.shiftKey) {
             SelectionManager.DeselectAll();
         }
@@ -83,6 +88,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top);
     }
 
+    @computed
     get Bounds() {
         let left = this._downX < this._lastX ? this._downX : this._lastX;
         let top = this._downY < this._lastY ? this._downY : this._lastY;
@@ -95,7 +101,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     marqueeCommand = (e: KeyboardEvent) => {
         if (e.key == "Backspace" || e.key == "Delete") {
             this.marqueeSelect().map(d => this.props.removeDocument(d));
-            this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
+            let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
+            if (ink && ink != FieldWaiting && ink.Data) {
+                this.marqueeInkDelete(ink.Data);
+            }
             this.cleanupInteractions();
         }
         if (e.key == "c") {
@@ -104,53 +113,61 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 this.props.removeDocument(d);
                 d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2);
                 d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2);
-                d.SetNumber(KeyStore.Page, 0);
+                d.SetNumber(KeyStore.Page, -1);
                 d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0));
                 return d;
             });
-            let liftedInk = this.marqueeInkSelect(true);
-            this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
-            //setTimeout(() => {
-            let newCollection = Documents.FreeformDocument(selected, {
-                x: bounds.left,
-                y: bounds.top,
-                panx: 0,
-                pany: 0,
-                width: bounds.width,
-                height: bounds.height,
-                backgroundColor: "Transparent",
-                ink: liftedInk,
-                title: "a nested collection"
-            });
-            this.props.addDocument(newCollection, false);
+            let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
+            if (ink && ink != FieldWaiting && ink.Data) {
+                //setTimeout(() => {
+                let newCollection = Documents.FreeformDocument(selected, {
+                    x: bounds.left,
+                    y: bounds.top,
+                    panx: 0,
+                    pany: 0,
+                    width: bounds.width,
+                    height: bounds.height,
+                    backgroundColor: "Transparent",
+                    ink: this.marqueeInkSelect(ink.Data),
+                    title: "a nested collection"
+                });
+                this.props.addDocument(newCollection, false);
+                this.marqueeInkDelete(ink.Data);
+            }
             // }, 100);
             this.cleanupInteractions();
         }
     }
-    marqueeInkSelect(select: boolean) {
-        let selRect = this.Bounds;
-        let centerShiftX = 0 - (selRect.left + selRect.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
-        let centerShiftY = 0 - (selRect.top + selRect.height / 2);
-        let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
-        if (ink && ink != FieldWaiting && ink.Data) {
-            let idata = new Map();
-            ink.Data.forEach((value: StrokeData, key: string, map: any) => {
-                let inside = InkingCanvas.IntersectStrokeRect(value, selRect);
-                if (inside && select) {
-                    idata.set(key,
-                        {
-                            pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
-                            color: value.color,
-                            width: value.width,
-                            tool: value.tool,
-                            page: -1
-                        });
-                } else if (!inside && !select) {
-                    idata.set(key, value);
-                }
-            })
-            return idata;
-        }
+    @action
+    marqueeInkSelect(ink: Map<any, any>) {
+        let idata = new Map();
+        let centerShiftX = 0 - (this.Bounds.left + this.Bounds.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
+        let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
+        ink.forEach((value: StrokeData, key: string, map: any) => {
+            if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
+                idata.set(key,
+                    {
+                        pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
+                        color: value.color,
+                        width: value.width,
+                        tool: value.tool,
+                        page: -1
+                    });
+            }
+        });
+        return idata;
+    }
+
+    @action
+    marqueeInkDelete(ink: Map<any, any>, ) {
+        // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
+        // ink.forEach((value: StrokeData, key: string, map: any) =>
+        //     InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
+
+        let idata = new Map();
+        ink.forEach((value: StrokeData, key: string, map: any) =>
+            !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
+        this.props.container.props.Document.SetDataOnPrototype(KeyStore.Ink, idata, InkField);
     }
 
     marqueeSelect() {
@@ -168,8 +185,16 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
 
     render() {
-        let p = this.props.getMarqueeTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
-        let v = this.props.getMarqueeTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        return (!this.props.container.MarqueeVisible ? (null) : <div className="marqueeView" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />);
+        let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
+        let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+        return <div className="marqueeView" onPointerDown={this.onPointerDown}>
+            <PreviewCursor container={this.props.container} addLiveTextDocument={this.props.addLiveTextDocument}
+                getContainerTransform={this.props.getContainerTransform} getTransform={this.props.getTransform} >
+                {this.props.children}
+                {!this._visible ? (null) :
+                    <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />}
+
+            </PreviewCursor>
+        </div>;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

