From 47ecf8d30f4aa5e25a659fc7f3c0c1487420150e Mon Sep 17 00:00:00 2001
From: Sam Wilkins <abdullah_ahmed@brown.edu>
Date: Thu, 9 May 2019 20:59:10 -0400
Subject: merge with master, but haven't reconciled internal and external
 linking

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 242 +++++++++++----------
 1 file changed, 129 insertions(+), 113 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 228fc937e..6861ce58b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,8 +1,5 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, computed, trace } from "mobx";
 import { observer } from "mobx-react";
-import Measure from "react-measure";
-import { Document } from "../../../../fields/Document";
-import { KeyStore } from "../../../../fields/KeyStore";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
@@ -11,10 +8,9 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
 import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
 import { InkingCanvas } from "../../InkingCanvas";
-import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView";
 import { CollectionSubView } from "../CollectionSubView";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
@@ -22,72 +18,86 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
+import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc";
+import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
+import { pageSchema } from "../../nodes/ImageBox";
+import { Id } from "../../../../new_fields/RefField";
+
+export const panZoomSchema = createSchema({
+    panX: "number",
+    panY: "number",
+    scale: "number"
+});
+
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
+const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema);
 
 @observer
-export class CollectionFreeFormView extends CollectionSubView {
+export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
+    public static RIGHT_BTN_DRAG = false;
     private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
     private _lastX: number = 0;
     private _lastY: number = 0;
     private get _pwidth() { return this.props.PanelWidth(); }
     private get _pheight() { return this.props.PanelHeight(); }
 
-    @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
-    @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+    @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+    @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
-    private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
-    private childViews = () => this.views;
-    private panX = () => this.props.Document.GetNumber(KeyStore.PanX, 0);
-    private panY = () => this.props.Document.GetNumber(KeyStore.PanY, 0);
-    private zoomScaling = () => this.props.Document.GetNumber(KeyStore.Scale, 1);
+    private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
+    private panX = () => FieldValue(this.Document.panX, 0);
+    private panY = () => FieldValue(this.Document.panY, 0);
+    private zoomScaling = () => FieldValue(this.Document.scale, 1);
     private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0;  // shift so pan position is at center of window for non-overlay collections
     private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
     private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
     private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
-    private addLiveTextBox = (newBox: Document) => {
-        this._selectOnLoaded = newBox.Id;// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+    private addLiveTextBox = (newBox: Doc) => {
+        this._selectOnLoaded = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
         this.addDocument(newBox, false);
     }
-    private addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        if (this.isAnnotationOverlay) {
-            newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
-        }
-        return this.props.addDocument(this.bringToFront(newBox), false);
+    private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
+        this.props.addDocument(newBox, false);
+        this.bringToFront(newBox);
+        return true;
     }
-    private selectDocuments = (docs: Document[]) => {
+    private selectDocuments = (docs: Doc[]) => {
         SelectionManager.DeselectAll;
         docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).filter(dv => dv).map(dv =>
             SelectionManager.SelectDoc(dv!, true));
     }
     public getActiveDocuments = () => {
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
-        return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).reduce((active, doc) => {
-            var page = doc.GetNumber(KeyStore.Page, -1);
-            if (page === curPage || page === -1) {
-                active.push(doc);
-            }
-            return active;
-        }, [] as Document[]);
+        const curPage = FieldValue(this.Document.curPage, -1);
+        return this.children.filter(doc => {
+            var page = NumCast(doc.page, -1);
+            return page === curPage || page === -1;
+        });
     }
 
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
-            const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
-                let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
-                let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
+                let dragDoc = de.data.droppedDocuments[0];
+                let zoom = NumCast(dragDoc.zoomBasis, 1);
+                let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+                let x = xp - de.data.xOffset / zoom;
+                let y = yp - de.data.yOffset / zoom;
+                let dropX = NumCast(de.data.droppedDocuments[0].x);
+                let dropY = NumCast(de.data.droppedDocuments[0].y);
                 de.data.droppedDocuments.map(d => {
-                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
-                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
-                    if (!d.GetNumber(KeyStore.Width, 0)) {
-                        d.SetNumber(KeyStore.Width, 300);
+                    d.x = x + NumCast(d.x) - dropX;
+                    d.y = y + NumCast(d.y) - dropY;
+                    if (!NumCast(d.width)) {
+                        d.width = 300;
                     }
-                    if (!d.GetNumber(KeyStore.Height, 0)) {
-                        let nw = d.GetNumber(KeyStore.NativeWidth, 0);
-                        let nh = d.GetNumber(KeyStore.NativeHeight, 0);
-                        d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
+                    if (!NumCast(d.height)) {
+                        let nw = NumCast(d.nativeWidth);
+                        let nh = NumCast(d.nativeHeight);
+                        d.height = nw && nh ? nh / nw * NumCast(d.width) : 300;
                     }
                     this.bringToFront(d);
                 });
@@ -98,52 +108,44 @@ export class CollectionFreeFormView extends CollectionSubView {
         return false;
     }
 
-    @action
-    cleanupInteractions = () => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
-
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
-            var dv = DocumentManager.Instance.getDocumentView(doc);
-            return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
-        }, false);
-        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
+        if ((CollectionFreeFormView.RIGHT_BTN_DRAG &&
+            (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) ||
+                (e.button === 0 && e.altKey)) && this.props.active())) ||
+            (!CollectionFreeFormView.RIGHT_BTN_DRAG &&
+                ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && this.props.active()))) {
             document.removeEventListener("pointermove", this.onPointerMove);
-            document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
+            document.addEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointerup", this.onPointerUp);
             this._lastX = e.pageX;
             this._lastY = e.pageY;
         }
     }
 
-    @action
     onPointerUp = (e: PointerEvent): void => {
-        e.stopPropagation();
-
-        this.cleanupInteractions();
+        document.removeEventListener("pointermove", this.onPointerMove);
+        document.removeEventListener("pointerup", this.onPointerUp);
     }
 
     @action
     onPointerMove = (e: PointerEvent): void => {
         if (!e.cancelBubble) {
-            let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
-            let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
-            let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+            let x = Cast(this.props.Document.panX, "number", 0);
+            let y = Cast(this.props.Document.panY, "number", 0);
+            let docs = this.children || [];
             let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
             if (!this.isAnnotationOverlay) {
-                let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
-                let maxx = docs.length ? docs[0].Width() + minx : minx;
-                let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
-                let maxy = docs.length ? docs[0].Height() + miny : miny;
+                let minx = docs.length ? Cast(docs[0].x, "number", 0) : 0;
+                let maxx = docs.length ? Cast(docs[0].width, "number", 0) + minx : minx;
+                let miny = docs.length ? Cast(docs[0].y, "number", 0) : 0;
+                let maxy = docs.length ? Cast(docs[0].height, "number", 0) + miny : miny;
                 let ranges = docs.filter(doc => doc).reduce((range, doc) => {
-                    let x = doc.GetNumber(KeyStore.X, 0);
-                    let xe = x + doc.GetNumber(KeyStore.Width, 0);
-                    let y = doc.GetNumber(KeyStore.Y, 0);
-                    let ye = y + doc.GetNumber(KeyStore.Height, 0);
+                    let x = Cast(doc.x, "number", 0);
+                    let xe = x + Cast(doc.width, "number", 0);
+                    let y = Cast(doc.y, "number", 0);
+                    let ye = y + Cast(doc.height, "number", 0);
                     return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
                     [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
                 }, [[minx, maxx], [miny, maxy]]);
@@ -157,7 +159,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             this.setPan(x - dx, y - dy);
             this._lastX = e.pageX;
             this._lastY = e.pageY;
-            e.stopPropagation();
+            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();
         }
     }
@@ -167,10 +169,10 @@ export class CollectionFreeFormView extends CollectionSubView {
         // if (!this.props.active()) {
         //     return;
         // }
-        let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+        let childSelected = this.children.some(doc => {
             var dv = DocumentManager.Instance.getDocumentView(doc);
-            return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
-        }, false);
+            return dv && SelectionManager.IsSelected(dv) ? true : false;
+        });
         if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) {
             return;
         }
@@ -179,8 +181,8 @@ export class CollectionFreeFormView extends CollectionSubView {
 
         if (e.ctrlKey) {
             let deltaScale = (1 - (e.deltaY / coefficient));
-            this.props.Document.SetNumber(KeyStore.NativeWidth, this.nativeWidth * deltaScale);
-            this.props.Document.SetNumber(KeyStore.NativeHeight, this.nativeHeight * deltaScale);
+            this.props.Document.nativeWidth = this.nativeWidth * deltaScale;
+            this.props.Document.nativeHeight = this.nativeHeight * deltaScale;
             e.stopPropagation();
             e.preventDefault();
         } else {
@@ -190,23 +192,24 @@ export class CollectionFreeFormView extends CollectionSubView {
             if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
                 deltaScale = 1 / this.zoomScaling();
             }
+            if (deltaScale < 0) deltaScale = -deltaScale;
             let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
             let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-            this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
-            this.setPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+            let safeScale = Math.abs(localTransform.Scale);
+            this.props.Document.scale = Math.abs(safeScale);
+            this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
             e.stopPropagation();
         }
     }
 
     @action
     setPan(panX: number, panY: number) {
-        MainOverlayTextBox.Instance.SetTextDoc();
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
-        this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
-        this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
+        this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+        this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
     }
 
     @action
@@ -218,49 +221,61 @@ export class CollectionFreeFormView extends CollectionSubView {
     onDragOver = (): void => {
     }
 
-    @action
-    bringToFront(doc: Document) {
-        this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice().sort((doc1, doc2) => {
+    bringToFront = (doc: Doc) => {
+        const docs = this.children;
+        docs.slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
             if (doc2 === doc) return -1;
-            return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
-        }).map((doc, index) => doc.SetNumber(KeyStore.ZIndex, index + 1));
-        return doc;
+            return NumCast(doc1.zIndex) - NumCast(doc2.zIndex);
+        }).forEach((doc, index) => doc.zIndex = index + 1);
+        doc.zIndex = docs.length + 1;
     }
 
-    focusDocument = (doc: Document) => {
+    focusDocument = (doc: Doc) => {
+        SelectionManager.DeselectAll();
+        this.props.Document.panTransformType = "Ease";
         this.setPan(
-            doc.GetNumber(KeyStore.X, 0) + doc.Width() / 2,
-            doc.GetNumber(KeyStore.Y, 0) + doc.Height() / 2);
+            NumCast(doc.x) + NumCast(doc.width) / 2,
+            NumCast(doc.y) + NumCast(doc.height) / 2);
         this.props.focus(this.props.Document);
+        if (this.props.Document.panTransformType === "Ease") {
+            setTimeout(() => this.props.Document.panTransformType = "None", 2000);  // wait 3 seconds, then reset to false
+        }
     }
 
-    getDocumentViewProps(document: Document): DocumentViewProps {
+
+    getDocumentViewProps(document: Doc): DocumentViewProps {
         return {
             Document: document,
+            toggleMinimized: emptyFunction,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
-            selectOnLoad: document.Id === this._selectOnLoaded,
-            PanelWidth: document.Width,
-            PanelHeight: document.Height,
+            selectOnLoad: document[Id] === this._selectOnLoaded,
+            PanelWidth: document[WidthSym],
+            PanelHeight: document[HeightSym],
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
             parentActive: this.props.active,
-            whenActiveChanged: this.props.active,
+            whenActiveChanged: this.props.whenActiveChanged,
+            bringToFront: this.bringToFront,
         };
     }
 
-    @computed
+    @computed.struct
     get views() {
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
-        let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
-            var page = doc.GetNumber(KeyStore.Page, -1);
+        let curPage = FieldValue(this.Document.curPage, -1);
+        let docviews = this.children.reduce((prev, doc) => {
+            if (!(doc instanceof Doc)) return prev;
+            var page = NumCast(doc.page, -1);
             if (page === curPage || page === -1) {
-                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+                let minim = Cast(doc.isMinimized, "boolean");
+                if (minim === undefined || !minim) {
+                    prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
+                }
             }
             return prev;
         }, [] as JSX.Element[]);
@@ -275,17 +290,20 @@ export class CollectionFreeFormView extends CollectionSubView {
         super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
     }
 
+    private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
+        const easing = () => this.props.Document.panTransformType === "Ease";
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
+                style={{ borderRadius: "inherit" }}
                 onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
-                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
                     <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
-                        zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
-                        <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} />
+                        easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
+
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
                                 {this.childViews}
@@ -293,20 +311,18 @@ export class CollectionFreeFormView extends CollectionSubView {
                         </CollectionFreeFormLinksView>
                         <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
                     </CollectionFreeFormViewPannableContents>
-                    <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
                 </MarqueeView>
+                <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} {...this.props} />
             </div>
         );
     }
 }
 
 @observer
-class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
+class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
     @computed get overlayView() {
-        let overlayLayout = this.props.Document.GetText(KeyStore.OverlayLayout, "");
-        return !overlayLayout ? (null) :
-            (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout}
-                isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
+        return (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
+            isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
         return this.overlayView;
@@ -314,12 +330,10 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
 }
 
 @observer
-class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> {
+class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
     @computed get backgroundView() {
-        let backgroundLayout = this.props.Document.GetText(KeyStore.BackgroundLayout, "");
-        return !backgroundLayout ? (null) :
-            (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout}
-                isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
+        return (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
+            isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
         return this.backgroundView;
@@ -332,17 +346,19 @@ interface CollectionFreeFormViewPannableContentsProps {
     panX: () => number;
     panY: () => number;
     zoomScaling: () => number;
+    easing: () => boolean;
 }
 
 @observer
 class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
     render() {
+        let freeformclass = "collectionfreeformview" + (this.props.easing() ? "-ease" : "-none");
         const cenx = this.props.centeringShiftX();
         const ceny = this.props.centeringShiftY();
         const panx = -this.props.panX();
         const pany = -this.props.panY();
         const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire
-        return <div className="collectionfreeformview" style={{ transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+        return <div className={freeformclass} style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
             {this.props.children}
         </div>;
     }
-- 
cgit v1.2.3-70-g09d2

