From 4f1f20a52a5c01b3046a56f37b6c7c029d3e10f0 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 13 Mar 2019 15:17:53 -0400
Subject: fixed a variety of things related to selection, dragging, marqueeing

---
 .../views/collections/CollectionFreeFormView.tsx   | 215 +++++++--------------
 1 file changed, 69 insertions(+), 146 deletions(-)

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

diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index ab41d1378..71f429e80 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { FieldWaiting } from "../../../fields/Field";
@@ -12,39 +12,68 @@ import { undoBatch } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionSchemaView } from "../collections/CollectionSchemaView";
+import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { InkingCanvas } from "../InkingCanvas";
+import { AudioBox } from "../nodes/AudioBox";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FormattedTextBox } from "../nodes/FormattedTextBox";
 import { ImageBox } from "../nodes/ImageBox";
 import { KeyValueBox } from "../nodes/KeyValueBox";
 import { PDFBox } from "../nodes/PDFBox";
+import { VideoBox } from "../nodes/VideoBox";
 import { WebBox } from "../nodes/WebBox";
 import "./CollectionFreeFormView.scss";
 import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
+import { MarqueeView } from "./MarqueeView";
+import { PreviewCursor } from "./PreviewCursor";
 import React = require("react");
-import { SelectionManager } from "../../util/SelectionManager";
 const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
 
 @observer
 export class CollectionFreeFormView extends CollectionViewBase {
-    private _canvasRef = React.createRef<HTMLDivElement>();
-    @observable
-    private _lastX: number = 0;
-    @observable
-    private _lastY: number = 0;
+    public _canvasRef = React.createRef<HTMLDivElement>();
     private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
 
-    @observable
-    private _downX: number = 0;
-    @observable
-    private _downY: number = 0;
+    public addLiveTextBox = (newBox: Document) => {
+        // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
+        this._selectOnLoaded = newBox.Id;
+        //set text to be the typed key and get focus on text box
+        this.props.addDocument(newBox);
+        //remove cursor from screen
+        this.PreviewCursorVisible = false;
+    }
+
+    public selectDocuments = (docs: Document[]) => {
+        this.props.CollectionView.SelectedDocs.length = 0;
+        docs.map(d => this.props.CollectionView.SelectedDocs.push(d.Id));
+    }
+
+    public getActiveDocuments = () => {
+        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
+        const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
+        let active: Document[] = [];
+        if (lvalue && lvalue != FieldWaiting) {
+            lvalue.Data.map(doc => {
+                var page = doc.GetNumber(KeyStore.Page, 0);
+                if (page == curPage || page == 0) {
+                    active.push(doc);
+                }
+            })
+        }
+
+        return active;
+    }
 
     //determines whether the blinking cursor for indicating whether a text will be made on key down is visible
-    @observable
-    private _previewCursorVisible: boolean = false;
+    @observable public PreviewCursorVisible: boolean = false;
+    @observable public MarqueeVisible = false;
+    @observable public DownX: number = 0;
+    @observable public DownY: number = 0;
+    @observable private _lastX: number = 0;
+    @observable private _lastY: number = 0;
 
     @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
     @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
@@ -72,40 +101,34 @@ export class CollectionFreeFormView extends CollectionViewBase {
         }
     }
 
-    @observable
-    _marquee = false;
+
+    @action
+    cleanupInteractions = () => {
+        document.removeEventListener("pointermove", this.onPointerMove);
+        document.removeEventListener("pointerup", this.onPointerUp);
+        this.MarqueeVisible = false;
+    }
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
+        this.PreviewCursorVisible = false;
         if ((e.button === 2 && this.props.active() && (!this.isAnnotationOverlay || this.zoomScaling != 1)) || e.button == 0) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointerup", this.onPointerUp);
-            this._lastX = e.pageX;
-            this._lastY = e.pageY;
-            this._downX = e.pageX;
-            this._downY = e.pageY;
+            this._lastX = this.DownX = e.pageX;
+            this._lastY = this.DownY = e.pageY;
         }
     }
 
     @action
     onPointerUp = (e: PointerEvent): void => {
-        if (this._marquee) {
-            document.removeEventListener("keydown", this.marqueeCommand);
-        }
         e.stopPropagation();
 
-        if (this._marquee) {
-            if (!e.shiftKey) {
-                SelectionManager.DeselectAll();
-            }
-            var selectedDocs = this.marqueeSelect();
-            selectedDocs.map(s => this.props.CollectionView.SelectedDocs.push(s.Id));
-        }
-        else if (!this._marquee && Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) {
+        if (!this.MarqueeVisible && Math.abs(this.DownX - e.clientX) < 3 && Math.abs(this.DownY - e.clientY) < 3) {
             //show preview text cursor on tap
-            this._previewCursorVisible = true;
+            this.PreviewCursorVisible = true;
             //select is not already selected
             if (!this.props.isSelected()) {
                 this.props.select(false);
@@ -114,99 +137,29 @@ export class CollectionFreeFormView extends CollectionViewBase {
         this.cleanupInteractions();
     }
 
-    @action
-    cleanupInteractions = () => {
-        document.removeEventListener("keydown", this.marqueeCommand);
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-        this._marquee = false;
-    }
-
-    intersectRect(r1: { left: number, right: number, top: number, bottom: number },
-        r2: { left: number, right: number, top: number, bottom: number }) {
-        return !(r2.left > r1.right ||
-            r2.right < r1.left ||
-            r2.top > r1.bottom ||
-            r2.bottom < r1.top);
-    }
-
-    marqueeSelect() {
-        this.props.CollectionView.SelectedDocs.length = 0;
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
-        let p = this.getTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
-        let v = this.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        let selRect = { left: p[0], top: p[1], right: p[0] + Math.abs(v[0]), bottom: p[1] + Math.abs(v[1]) }
-
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
-        const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
-        let selection: Document[] = [];
-        if (lvalue && lvalue != FieldWaiting) {
-            lvalue.Data.map(doc => {
-                var page = doc.GetNumber(KeyStore.Page, 0);
-                if (page == curPage || page == 0) {
-                    var x = doc.GetNumber(KeyStore.X, 0);
-                    var y = doc.GetNumber(KeyStore.Y, 0);
-                    var w = doc.GetNumber(KeyStore.Width, 0);
-                    var h = doc.GetNumber(KeyStore.Height, 0);
-                    if (this.intersectRect({ left: x, top: y, right: x + w, bottom: y + h }, selRect))
-                        selection.push(doc)
-                }
-            })
-        }
-        return selection;
-    }
-
     @action
     onPointerMove = (e: PointerEvent): void => {
         if (!e.cancelBubble && this.props.active()) {
-            let wasMarquee = this._marquee;
-            this._marquee = e.buttons != 2 && !e.altKey && !e.metaKey;
-            if (this._marquee && !wasMarquee) {
-                this._previewCursorVisible = false;
-                document.addEventListener("keydown", this.marqueeCommand);
+            if (e.buttons != 2 && !e.altKey && !e.metaKey && !this.MarqueeVisible) {
+                this.MarqueeVisible = true;
+                this.PreviewCursorVisible = false;
             }
-            if (this._marquee) {
+            if (this.MarqueeVisible) {
                 e.stopPropagation();
                 e.preventDefault();
             }
-
-            if (!this._marquee && (!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
+            else if ((!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
                 let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
                 let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
                 let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
-                this._previewCursorVisible = false;
+                this.PreviewCursorVisible = false;
                 this.SetPan(x - dx, y - dy);
+                this._lastX = e.pageX;
+                this._lastY = e.pageY;
                 e.stopPropagation();
                 e.preventDefault();
             }
         }
-        this._lastX = e.pageX;
-        this._lastY = e.pageY;
-    }
-
-    @action
-    marqueeCommand = (e: KeyboardEvent) => {
-        if (e.key == "Backspace") {
-            this.marqueeSelect().map(d => this.props.removeDocument(d));
-            this.cleanupInteractions();
-        }
-        if (e.key == "c") {
-            let p = this.getTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
-            let v = this.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-
-            let selected = this.marqueeSelect().map(m => m);
-            this.marqueeSelect().map(d => this.props.removeDocument(d));
-            //setTimeout(() => {
-            this.props.CollectionView.addDocument(Documents.FreeformDocument(selected.map(d => {
-                d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - p[0] - v[0] / 2);
-                d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - p[1] - v[1] / 2);
-                d.SetNumber(KeyStore.Page, this.props.Document.GetNumber(KeyStore.Page, 0));
-                d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0));
-                return d;
-            }), { x: p[0], y: p[1], panx: 0, pany: 0, width: v[0], height: v[1], title: "a nested collection" }));
-            // }, 100);
-            this.cleanupInteractions();
-        }
     }
 
     @action
@@ -263,24 +216,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
     onDragOver = (): void => {
     }
 
-    @action
-    onKeyDown = (e: React.KeyboardEvent<Element>) => {
-        //if not these keys, make a textbox if preview cursor is active!
-        if (!e.ctrlKey && !e.altKey) {
-            if (this._previewCursorVisible) {
-                //make textbox and add it to this collection
-                let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY);
-                let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" });
-                // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
-                this._selectOnLoaded = newBox.Id;
-                //set text to be the typed key and get focus on text box
-                this.props.CollectionView.addDocument(newBox);
-                //remove cursor from screen
-                this._previewCursorVisible = false;
-            }
-        }
-    }
-
     @action
     bringToFront(doc: Document) {
         const { fieldKey: fieldKey, Document: Document } = this.props;
@@ -349,7 +284,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     get backgroundView() {
         return !this.backgroundLayout ? (null) :
             (<JsxParser
-                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
+                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox }}
                 bindings={this.props.bindings}
                 jsx={this.backgroundLayout}
                 showWarnings={true}
@@ -360,7 +295,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
     get overlayView() {
         return !this.overlayLayout ? (null) :
             (<JsxParser
-                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
+                components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox }}
                 bindings={this.props.bindings}
                 jsx={this.overlayLayout}
                 showWarnings={true}
@@ -369,28 +304,17 @@ export class CollectionFreeFormView extends CollectionViewBase {
     }
 
     getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
+    getMarqueeTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH)
     getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY);
     noScaling = () => 1;
 
     //when focus is lost, this will remove the preview cursor
     @action
     onBlur = (e: React.FocusEvent<HTMLDivElement>): void => {
-        this._previewCursorVisible = false;
+        this.PreviewCursorVisible = false;
     }
 
     render() {
-        //determines whether preview text cursor should be visible (ie when user taps this collection it should)
-        let cursor = null;
-        if (this._previewCursorVisible) {
-            //get local position and place cursor there!
-            let [x, y] = this.getTransform().transformPoint(this._downX, this._downY);
-            cursor = <div id="prevCursor" onKeyPress={this.onKeyDown} style={{ color: "black", position: "absolute", transformOrigin: "left top", transform: `translate(${x}px, ${y}px)` }}>I</div>
-        }
-
-        let p = this.getTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
-        let v = this.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        var marquee = this._marquee ? <div className="collectionfreeformview-marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }}></div> : (null);
-
         let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
 
         const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
@@ -399,7 +323,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
         return (
             <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
                 onPointerDown={this.onPointerDown}
-                onKeyPress={this.onKeyDown}
                 onWheel={this.onPointerWheel}
                 onDrop={this.onDrop.bind(this)}
                 onDragOver={this.onDragOver}
@@ -412,12 +335,12 @@ export class CollectionFreeFormView extends CollectionViewBase {
                     ref={this._canvasRef}>
                     {this.backgroundView}
                     <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} />
-                    {cursor}
+                    <PreviewCursor container={this} addLiveTextDocuemnt={this.addLiveTextBox} getTransform={this.getTransform} />
                     {this.views}
-                    {marquee}
                 </div>
+                <MarqueeView container={this} activeDocuemnts={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} getMarqueeTransform={this.getMarqueeTransform} getTransform={this.getTransform} />
                 {this.overlayView}
             </div>
         );
     }
-}
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

