From 223262242f7db021c8449e920645892cc5ed2820 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 17 Oct 2020 17:45:10 -0400
Subject: major rewrite of native width/height/aspect.  Fixed scaling of text
 note sidebars.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 25 +++++++++++-----------
 1 file changed, 12 insertions(+), 13 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 b32a3bd52..c863addd2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -105,11 +105,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable canPanY: boolean = true;
 
     @computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; }
-    @computed get fitToContent() { return this.props.fitToBox || (this.Document._fitToBox && !this.isAnnotationOverlay); }
+    @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
     @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent ? this.props.ContentScaling() : 1; }
     @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); }
-    @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth)); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight)); }
+    @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document)); }
+    @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document)); }
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
@@ -120,7 +120,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (this.fitToContent) {
             const zs = !this.childDocs.length ? 1 :
                 Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x));
-            return mult * (this.props.isAnnotationOverlay ? Math.min(zs, 1) : zs);
+            return mult * zs;
         }
         return mult * NumCast(this.Document[this.scaleFieldKey], 1);
     }
@@ -219,7 +219,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 d.x = x + NumCast(d.x) - dropPos[0];
                 d.y = y + NumCast(d.y) - dropPos[1];
             }
-            const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
+            const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
             !d._isBackground && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
@@ -925,8 +925,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
 
             willZoom && this.setScaleToZoom(layoutdoc, scale);
-            const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeWidth)) / 2 / this.zoomScaling() : 0);
-            const newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeHeight)) / 2 / this.zoomScaling() : 0);
+            const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeWidth(this.props.Document)) / 2 / this.zoomScaling() : 0);
+            const newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeHeight(this.props.Document)) / 2 / this.zoomScaling() : 0);
             const newState = HistoryUtil.getState();
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
@@ -954,8 +954,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
-        const pw = this.isAnnotationOverlay ? NumCast(this.props.Document._nativeWidth) : this.props.PanelWidth();
-        const ph = this.isAnnotationOverlay ? NumCast(this.props.Document._nativeHeight) : this.props.PanelHeight();
+        const pw = this.isAnnotationOverlay ? Doc.NativeWidth(this.props.Document) : this.props.PanelWidth();
+        const ph = this.isAnnotationOverlay ? Doc.NativeHeight(this.props.Document) : this.props.PanelHeight();
         pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height)));
     }
 
@@ -979,7 +979,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate,
             LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString,
             FreezeDimensions: this.props.freezeChildDimensions,
-            layoutKey: StrCast(this.props.Document.childLayoutKey),
             setupDragLines: this.setupDragLines,
             dontRegisterView: this.props.dontRegisterView,
             rootSelected: childData ? this.rootSelected : returnFalse,
@@ -1297,7 +1296,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
         this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
         if (!Doc.UserDoc().noviceMode) {
-            optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
+            optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
             optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" });
 
         }
@@ -1454,8 +1453,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @computed get contentScaling() {
         if (this.props.annotationsKey && !this.props.forceScaling) return 0;
-        const nw = returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth));
-        const nh = returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight));
+        const nw = returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document));
+        const nh = returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document));
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
-- 
cgit v1.2.3-70-g09d2


From 37fc6beb4662be195b8aa551cc1042b04f2c24a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 17 Oct 2020 22:01:53 -0400
Subject: fixed rubberbadning ink shapes not to get cutoff. fixed animating
 annotations.   fixed ink masks. fixed warnings.

---
 src/client/views/InkingStroke.tsx                            |  4 +---
 src/client/views/collections/CollectionMenu.tsx              |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  6 ++++--
 src/client/views/nodes/PresBox.tsx                           | 12 ++++++------
 src/client/views/pdf/PDFViewer.tsx                           |  6 +++++-
 src/server/websocket.ts                                      |  4 ++--
 6 files changed, 19 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 186406424..7e424d8f0 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -38,7 +38,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         this.props.Document._backgroundColor = "rgba(0,0,0,0.7)";
         this.props.Document.mixBlendMode = "hard-light";
         this.props.Document.color = "#9b9b9bff";
-        this.props.Document._stayInCollection = true;
+        //this.props.Document._stayInCollection = true;
         this.props.Document.isInkMask = true;
     }
 
@@ -190,8 +190,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
 
         return (
             <svg className="inkingStroke"
-                width={Math.max(width, height)}
-                height={Math.max(width, height)}
                 style={{
                     pointerEvents: this.props.Document.isInkMask ? "all" : "none",
                     transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index d08b31340..a4f5118d6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -533,7 +533,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
     }
     get document() { return this.props.docView.props.Document; }
     @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey];
+        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
     }
     @computed get childDocs() {
         return DocListCast(this.dataField);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c863addd2..56624f42d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -202,13 +202,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
         if (!super.onInternalDrop(e, de)) return false;
+        const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y);
-        const z = NumCast(docDragData.droppedDocuments[0].z);
+        const z = NumCast(refDoc.z);
         const x = (z ? xpo : xp) - docDragData.offset[0];
         const y = (z ? ypo : yp) - docDragData.offset[1];
         const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
         zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
-        const dropPos = [NumCast(docDragData.droppedDocuments[0].x), NumCast(docDragData.droppedDocuments[0].y)];
+        const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
+        const dropPos = this.Document._currentFrame !== undefined ? [dvals.x, dvals.y] : [NumCast(refDoc.x), NumCast(refDoc.y)];
         for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
             const d = docDragData.droppedDocuments[i];
             const layoutDoc = Doc.Layout(d);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 3b7794815..01a7bed8c 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -288,7 +288,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const willZoom = false;
         const openInTab = () => {
             collectionDocView ? collectionDocView.props.addDocTab(activeItem, "replace") : this.props.addDocTab(activeItem, "replace:left");
-        }
+        };
         // If openDocument is selected then it should open the document for the user
         if (activeItem.openDocument) {
             openInTab();
@@ -299,10 +299,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             } else if (docToJump === curDoc) {
                 //checking if curDoc has navigation open
                 if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
-                    await DocumentManager.Instance.jumpToDocument(targetDoc, false, () => { openInTab() }, srcContext); // documents open in new tab instead of on right
+                    await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext); // documents open in new tab instead of on right
                 } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
                     //awaiting jump so that new scale can be found, since jumping is async
-                    await DocumentManager.Instance.jumpToDocument(targetDoc, true, () => { openInTab() }, srcContext); // documents open in new tab instead of on right
+                    await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext); // documents open in new tab instead of on right
                 }
             } else {
                 //awaiting jump so that new scale can be found, since jumping is async
@@ -442,7 +442,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 }
             }
         };
-        this.layoutDoc.presStatus = PresStatus.Auto;
+        this.layoutDoc.presStatus = PresStatus.Autoplay;
         this.startPresentation(startSlide);
         this.gotoDocument(startSlide, this.itemIndex);
         load();
@@ -450,7 +450,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
 
     @action
     pauseAutoPres = () => {
-        if (this.layoutDoc.presStatus === PresStatus.Auto) {
+        if (this.layoutDoc.presStatus === PresStatus.Autoplay) {
             if (this._presTimer) clearTimeout(this._presTimer);
             this.layoutDoc.presStatus = PresStatus.Manual;
             this.layoutDoc.presLoop = false;
@@ -611,7 +611,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         return true;
     }
     childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
-    removeDocument = (doc: Doc) => { return Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); };
+    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
     active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d278612b1..76b218972 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -730,7 +730,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     @computed get overlayLayer() {
         return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
-            style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, transform: `scale(${this._zoomed})` }}>
+            style={{
+                pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined,
+                mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
+                transform: `scale(${this._zoomed})`
+            }}>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                 LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
                 annotationsKey={this.annotationKey}
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 490760441..6ceb9e29f 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -278,8 +278,8 @@ export namespace WebSocket {
         diff.diff.$set = diff.diff.$addToSet; delete diff.diff.$addToSet;// convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
         const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
         const newListItems = diff.diff.$set[updatefield].fields;
-        const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || [];
-        diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
+        const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((item: any) => item !== undefined) || [];
+        diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => newItem && !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
         const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
         delete diff.diff.length;
         Database.Instance.update(diff.id, diff.diff,
-- 
cgit v1.2.3-70-g09d2


From 6cfd2a37494bf531c88e26ea89b5b776805d31a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 20 Oct 2020 23:36:45 -0400
Subject: fixes for overlayDocs for undo/redo in presBox - undo/redo of
 minimize/show sidebar.

---
 src/client/util/CurrentUserUtils.ts                   |  1 +
 src/client/views/OverlayView.tsx                      |  9 ++-------
 .../collectionFreeForm/CollectionFreeFormView.tsx     |  5 +++--
 src/client/views/nodes/DocumentView.tsx               |  8 ++++----
 src/client/views/nodes/PresBox.tsx                    | 19 +++++++++----------
 src/fields/documentSchemas.ts                         |  1 -
 6 files changed, 19 insertions(+), 24 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index f43b6df44..38bfe3fb6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1190,6 +1190,7 @@ export class CurrentUserUtils {
     public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
     public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
     public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
+    public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); }
 }
 
 Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index b40c9edfb..7d47abdce 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -14,6 +14,7 @@ import { Scripting } from "../util/Scripting";
 import { ScriptingRepl } from './ScriptingRepl';
 import { DragManager } from "../util/DragManager";
 import { List } from "../../fields/List";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 
 export type OverlayDisposer = () => void;
 
@@ -146,12 +147,7 @@ export class OverlayView extends React.Component {
 
 
     @computed get overlayDocs() {
-        const userDocOverlays = Doc.UserDoc().myOverlayDocs;
-        if (!userDocOverlays) {
-            return null;
-        }
-        return userDocOverlays instanceof Doc && DocListCast(userDocOverlays.data).map(d => {
-            setTimeout(() => d.inOverlay = true, 0);
+        return CurrentUserUtils.OverlayDocs?.map(d => {
             let offsetx = 0, offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
@@ -161,7 +157,6 @@ export class OverlayView extends React.Component {
                 }
                 if (e.metaKey) {
                     const dragData = new DragManager.DocumentDragData([d]);
-                    d.removeDropProperties = new List<string>(["inOverlay"]);
                     dragData.offset = [-offsetx, -offsety];
                     dragData.dropAction = "move";
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 56624f42d..fdcfb00d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -46,6 +46,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -803,7 +804,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay || this.props.Document.treeViewOutlineMode) return;
+        if (this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -839,7 +840,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || this.Document.inOverlay) {
+        if (!this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.Document)) {
             this.Document._viewTransition = panType;
             const scale = this.getLocalTransform().inverse().Scale;
             const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d227d3a8e..08a4a7e23 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -423,7 +423,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) e.stopPropagation();
+                if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -438,7 +438,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (e.cancelBubble && this.active) {
             this.removeMoveListeners();
         }
-        else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
+        else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
 
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
@@ -554,7 +554,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             if ((this.active || this.layoutDoc.onDragStart) &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !this.layoutDoc.inOverlay) {
+                !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
                 e.stopPropagation();
                 if (SelectionManager.IsSelected(this, true) && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
             }
@@ -573,7 +573,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (e.cancelBubble && this.active) {
             document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView)
         }
-        else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
+        else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
                     document.removeEventListener("pointermove", this.onPointerMove);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 53b2433e8..9723937a7 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -520,11 +520,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     @action
     updateMinimize = () => {
         const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
-        if (this.layoutDoc.inOverlay) {
+        if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
             Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
-            this.layoutDoc.inOverlay = false;
         } else if (this.layoutDoc.context && docView) {
             this.layoutDoc.presStatus = PresStatus.Edit;
             clearTimeout(this._presTimer);
@@ -716,7 +715,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const anchorNode = document.activeElement as HTMLDivElement;
         if (anchorNode && anchorNode.className?.includes("lm_title")) return;
         if (e.keyCode === 27) { // Escape key
-            if (this.layoutDoc.inOverlay) { this.updateMinimize(); }
+            if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
             else if (this.layoutDoc.presStatus === "edit") { this._selectedArray = []; this._eleArray = []; this._dragArray = []; }
             else this.layoutDoc.presStatus = "edit";
             if (this._presTimer) clearTimeout(this._presTimer);
@@ -1360,10 +1359,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     @computed get presentDropdown() {
         return (
             <div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
-                <div className="dropdown-play-button" onClick={(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}>
+                <div className="dropdown-play-button" onClick={(undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); })))}>
                     Minimize
                 </div>
-                <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}>
+                <div className="dropdown-play-button" onClick={(undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); })))}>
                     Sidebar view
                 </div>
             </div>
@@ -1949,8 +1948,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 {this.playButtonFrames}
             </div>
             <div className="presPanel-divider"></div>
-            {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div>
-                : <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}>
+            {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }))}>EXIT</div>
+                : <div className="presPanel-button" onClick={undoBatch(action(() => this.layoutDoc.presStatus = "edit"))}>
                     <FontAwesomeIcon icon={"times"} />
                 </div>}
         </div>);
@@ -1968,7 +1967,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        return this.layoutDoc.inOverlay ?
+        return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
             <div className="miniPres">
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
@@ -1982,11 +1981,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                         {this.playButtonFrames}
                     </div>
                     <div className="presPanel-divider"></div>
-                    <div className="presPanel-button-text" onClick={() => { this.updateMinimize(); this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div>
+                    <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.updateMinimize(); this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }))}>EXIT</div>
                 </div>
             </div>
             :
-            <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
+            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.OverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 71294c59c..311aaa769 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -19,7 +19,6 @@ export const documentSchema = createSchema({
     activeFrame: "number",      // the active frame of a frame based animated document 
     _currentTimecode: "number", // current play back time of a temporal document (video / audio)
     displayTimecode: "number",  // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
-    inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
     isLabel: "boolean",         // whether the document is a label or not (video / audio)
     audioStart: "number",       // the time frame where the audio should begin playing
     audioEnd: "number",         // the time frame where the audio should stop playing
-- 
cgit v1.2.3-70-g09d2


From 0ddd75da0f0b42cf3e1692e1cb1761c0e70ed551 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Nov 2020 14:11:25 -0500
Subject: fixed up dropping documents into a collection that doesn't have an
 activeFrame set so that they can be resized

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx    | 9 +++++++++
 1 file changed, 9 insertions(+)

(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 fdcfb00d8..58a9c4974 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -169,14 +169,23 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 if (newBox.activeFrame !== undefined) {
                     const x = newBox.x;
                     const y = newBox.y;
+                    const w = newBox._width;
+                    const h = newBox._height;
                     delete newBox["x-indexed"];
                     delete newBox["y-indexed"];
+                    delete newBox["w-indexed"];
+                    delete newBox["h-indexed"];
                     delete newBox["opacity-indexed"];
+                    delete newBox._width;
+                    delete newBox._height;
                     delete newBox.x;
                     delete newBox.y;
+                    delete newBox.opacity;
                     delete newBox.activeFrame;
                     newBox.x = x;
                     newBox.y = y;
+                    newBox._width = w;
+                    newBox._height = h;
                 }
             }
             if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
-- 
cgit v1.2.3-70-g09d2


From eacb4733e85ada91520fe401f5c1351f7bbc38d4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 3 Nov 2020 14:26:19 -0500
Subject: added undo's for icon/deiconifying pile docs and icon docs. added a
 zoom hack to focus on stacking documents. stopped autoscroll on nested
 collections.

---
 src/client/views/collections/CollectionPileView.tsx        | 14 ++++++++------
 src/client/views/collections/CollectionStackingView.tsx    |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx          |  2 +-
 src/client/views/nodes/DocumentView.tsx                    |  2 +-
 4 files changed, 11 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 5b4730848..2636b98e5 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -6,7 +6,7 @@ import { emptyFunction, setupMoveUpEvents } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
-import { UndoManager } from "../../util/UndoManager";
+import { UndoManager, undoBatch } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import "./CollectionPileView.scss";
 import { CollectionSubView } from "./CollectionSubView";
@@ -34,14 +34,14 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         const draggingSelf = this.props.isSelected();
         return <div className="collectionPileView-innards" style={{ pointerEvents: this.layoutEngine() === "starburst" || (SnappingManager.GetIsDragging() && !draggingSelf) ? undefined : "none", zIndex: this.layoutEngine() === "starburst" && !SnappingManager.GetIsDragging() ? -10 : "auto" }} >
             <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine}
-                addDocument={(doc: Doc | Doc[]) => {
+                addDocument={undoBatch((doc: Doc | Doc[]) => {
                     (doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d));
                     return this.props.addDocument(doc);
-                }}
-                moveDocument={(doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-                    (doc instanceof Doc ? [doc] : doc).map((d) => Doc.deiconifyView(d));
+                })}
+                moveDocument={undoBatch((doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
+                    (doc instanceof Doc ? [doc] : doc).map(undoBatch((d) => Doc.deiconifyView(d)));
                     return this.props.moveDocument(doc, targetCollection, addDoc);
-                }} />
+                })} />
         </div>;
     }
 
@@ -107,6 +107,8 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
     }
 
     // onClick for toggling the pileup view
+    @undoBatch
+    @action
     onClick = (e: React.MouseEvent) => {
         if (e.button === 0) {
             SelectionManager.DeselectAll();
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4880d342c..7a3257525 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -172,7 +172,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
 
     focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => {
         Doc.BrushDoc(doc);
-        this.props.focus(doc);
+        this.props.focus(this.props.Document, true);  // bcz: HACK ARgh.. need to update all focus() functions to take parameters about how to focus.  in this case, we want to zoom so we pass true and hope an ancestor is a collection view
         Doc.linkFollowHighlight(doc);
 
         const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName("documentView-node")).find((node: any) => node.id === doc[Id]);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 58a9c4974..703776e72 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1218,7 +1218,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
         (e as any).handlePan = true;
 
-        if (!this.props.Document._noAutoscroll && this._marqueeRef?.current) {
+        if (!this.props.Document._noAutoscroll && !this.props.renderDepth && this._marqueeRef?.current) {
             const dragX = e.detail.clientX;
             const dragY = e.detail.clientY;
             const bounds = this._marqueeRef.current?.getBoundingClientRect();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5fb2a8db9..f6a147c50 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -325,7 +325,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                             thisContainer: this.props.ContainingCollectionDoc,
                             shiftKey: e.shiftKey
                         }, console.log);
-                        func();
+                        undoBatch(func)();
                     } else if (!Doc.IsSystem(this.props.Document)) {
                         if (this.props.Document.type === DocumentType.INK) {
                             InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true);
-- 
cgit v1.2.3-70-g09d2


From aba91357df33b49e9c09208a53b757ef8f8dc724 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 8 Nov 2020 21:35:16 -0500
Subject: added an optional background grid for collections.

---
 src/client/views/MainView.tsx                      |  4 +--
 src/client/views/collections/CollectionMenu.tsx    | 13 ++++++--
 .../collectionFreeForm/CollectionFreeFormView.scss |  7 ++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 37 +++++++++++++++++++++-
 src/client/views/nodes/PresBox.tsx                 |  4 +--
 5 files changed, 57 insertions(+), 8 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3a3dbc68f..452ce61ff 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -133,7 +133,7 @@ export class MainView extends React.Component {
         }
 
         library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
-            fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
+            fa.faSquare, far.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
             fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
             fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
             fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
@@ -151,7 +151,7 @@ export class MainView extends React.Component {
             fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
             fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
             fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
-            fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload,
+            fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
             fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines);
         this.initAuthenticationRouters();
     }
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index cf94a93d3..b2b23115f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -489,13 +489,19 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     @computed get lightboxButton() {
         const targetDoc = this.selectedDoc;
         return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Lightbox of Images"}</div>} placement="top">
-            <button className="antimodeMenu-button" onPointerDown={action(() => targetDoc._isLightboxOpen = true)} onClick={this.onAlias}>
+            <button className="antimodeMenu-button" onPointerDown={action(() => targetDoc._isLightboxOpen = true)}>
                 <FontAwesomeIcon className="documentdecorations-icon" icon="desktop" size="lg" />
             </button>
         </Tooltip>;
     }
-
-
+    @computed get gridbackgroundButton() {
+        const targetDoc = this.selectedDoc;
+        return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Toggle background grid"}</div>} placement="top">
+            <button className="antimodeMenu-button" onPointerDown={action(() => targetDoc["_backgroundGrid-show"] = !targetDoc["_backgroundGrid-show"])}>
+                <FontAwesomeIcon className="documentdecorations-icon" icon={targetDoc["_backgroundGrid-show"] ? "border-all" : ["far", "square"]} size="lg" />
+            </button>
+        </Tooltip>;
+    }
 
     render() {
         return (
@@ -513,6 +519,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
                                 </button>
                             </Tooltip>}
                         {this.notACollection ? (null) : this.lightboxButton}
+                        {this.notACollection ? (null) : this.gridbackgroundButton}
                         {this.aliasButton}
                         {/* {this.pinButton} */}
                         {this.pinWithViewButton}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index e92100c50..a50b41198 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -12,6 +12,13 @@
     border-radius: inherit;
 }
 
+.collectionfreeformview-grid {
+    transform-origin: top left;
+    position: absolute;
+    top: 0;
+    left: 0;
+}
+
 .collectionfreeformview-viewdef {
     >.collectionFreeFormDocumentView-container {
         pointer-events: none;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 703776e72..8a4ce826f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1434,6 +1434,38 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         return false;
     });
+    @computed get grid() {
+        const gridSpace = NumCast(this.layoutDoc["_backgroundGrid-space"], 50);
+        const shiftX = this.props.isAnnotationOverlay ? 0 : -(this.panX()) % gridSpace - gridSpace;
+        const shiftY = this.props.isAnnotationOverlay ? 0 : -(this.panY()) % gridSpace - gridSpace;
+        const cx = this.centeringShiftX();
+        const cy = this.centeringShiftY();
+        const w = this.props.PanelWidth() / this.zoomScaling() + 3 * gridSpace;
+        const h = this.props.PanelHeight() / this.zoomScaling() + 3 * gridSpace;
+        return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `scale(${this.zoomScaling()}) translate(${shiftX}px, ${shiftY}px)` }}
+            ref={(el) => {
+                const ctx = el?.getContext('2d');
+                if (ctx) {
+                    const Cx = (cx / this.zoomScaling()) % gridSpace - 0;
+                    const Cy = (cy / this.zoomScaling()) % gridSpace - 0;
+                    ctx.clearRect(0, 0, w, h);
+                    if (ctx) {
+                        ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
+                        ctx.lineWidth = Math.min(1, Math.max(0.5, 0.5 / this.zoomScaling()));
+                        ctx.beginPath();
+                        for (let x = Cx; x <= -Cx + w; x += gridSpace) {
+                            ctx.moveTo(x, Cy - h);
+                            ctx.lineTo(x, Cy + h);
+                        }
+                        for (let y = Cy; y <= -Cy + h; y += gridSpace) {
+                            ctx.moveTo(Cx - w, y);
+                            ctx.lineTo(Cx + w, y);
+                        }
+                        ctx.stroke();
+                    }
+                }
+            }} />;
+    }
     @computed get marqueeView() {
         return <MarqueeView
             {...this.props}
@@ -1447,6 +1479,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             getTransform={this.getTransform}
             isAnnotationOverlay={this.isAnnotationOverlay}>
             <div ref={this._marqueeRef}>
+                {this.layoutDoc["_backgroundGrid-show"] ? this.grid : (null)}
                 <CollectionFreeFormViewPannableContents
                     centeringShiftX={this.centeringShiftX}
                     centeringShiftY={this.centeringShiftY}
@@ -1463,6 +1496,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         </MarqueeView>;
     }
 
+
     @computed get contentScaling() {
         if (this.props.annotationsKey && !this.props.forceScaling) return 0;
         const nw = returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document));
@@ -1635,7 +1669,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
     }
 
     @computed get zoomProgressivize() {
-        return PresBox.Instance && PresBox.Instance.activeItem && PresBox.Instance.activeItem.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
+        return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
     }
 
     @computed get progressivize() {
@@ -1666,6 +1700,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
         </>;
     }
 
+
     render() {
         // trace();
         const freeformclass = "collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : "-none");
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 52e09a469..683cb938a 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -426,7 +426,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             if (tagDoc) tagDoc.opacity = 1;
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
             const curInd: number = itemIndexes.indexOf(index);
-            if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1 }
+            if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; }
             else {
                 if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { }
                 else if (curDoc.presHideBefore) {
@@ -1210,7 +1210,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const targetDoc: Doc = this.targetDoc;
         this.updateMovement(activeItem.presMovement, true);
         this.updateEffect(targetDoc.presEffect, true);
-        this.updateEffectDirection(targetDoc.presEffectDirection, true)
+        this.updateEffectDirection(targetDoc.presEffectDirection, true);
         array.forEach((doc) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
-- 
cgit v1.2.3-70-g09d2


From ce5512a3fdd451ad47263f896a9e855229133eaf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 9 Nov 2020 15:04:28 -0500
Subject: added fullScreen alias option for opening documents so that opened
 document can have different scale than original. fixed pushpins to not toggle
 target if something must be panned/zoomed to show the target.  fixed drag
 drop to ignore the document being dragged properly when dropping (which
 prevented a document over a collection from being dropped on the colelction
 when move slightly).

---
 src/client/util/DocumentManager.ts                 | 10 ++---
 src/client/util/DragManager.ts                     | 18 +++++----
 src/client/views/DocumentDecorations.tsx           | 10 ++---
 .../views/collections/CollectionStackingView.tsx   |  5 ++-
 src/client/views/collections/TabDocView.tsx        |  6 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 45 ++++++++++++++--------
 src/client/views/nodes/DocumentView.tsx            | 13 +++----
 src/client/views/nodes/FieldView.tsx               |  3 +-
 src/fields/documentSchemas.ts                      |  1 +
 9 files changed, 62 insertions(+), 49 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 14aaeaec0..a6816c7f9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -163,21 +163,19 @@ export class DocumentManager {
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
             const sameContext = annotatedDoc && annotatedDoc === originatingDoc?.context;
             if (originatingDoc?.isPushpin) {
-                const hide = !docView.props.Document.hidden;
-                docView.props.focus(docView.props.Document, willZoom, undefined, (notfocused: boolean) => { // bcz: Argh! TODO: Need to add a notFocused argument to the after finish callback function that indicates whether the window had to scroll to show the target  
-                    if (notfocused || docView.props.Document.hidden) {
+                docView.props.focus(docView.props.Document, willZoom, undefined, (didFocus: boolean) => {
+                    if (!didFocus || docView.props.Document.hidden) {
                         docView.props.Document.hidden = !docView.props.Document.hidden;
                     }
                     return focusAndFinish();
-                    // @ts-ignore   bcz: Argh TODO: Need to add a parameter to focus() everywhere for whether focus should center the target's container in the view or not. // here we don't want to focus the container if the source and target are in the same container
-                }, sameContext);
+                }, sameContext, false);// don't want to focus the container if the source and target are in the same container, so pass 'sameContext' for dontCenter parameter
                 //finished?.();
             }
             else {
                 docView.select(false);
                 docView.props.Document.hidden && (docView.props.Document.hidden = undefined);
                 // @ts-ignore
-                docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, sameContext);
+                docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, sameContext, false);
             }
             highlight();
         } else {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9e91b4f55..86e2d339e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -416,7 +416,13 @@ export namespace DragManager {
         });
 
         const hideSource = options?.hideSource ? true : false;
-        eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
+        eles.forEach(ele => {
+            if (ele.parentElement && ele.parentElement?.className === dragData.dragDivName) {
+                ele.parentElement.hidden = hideSource;
+            } else {
+                ele.hidden = hideSource;
+            }
+        });
 
         SnappingManager.SetIsDragging(true);
         let lastX = downX;
@@ -514,27 +520,25 @@ export namespace DragManager {
         const hideDragShowOriginalElements = () => {
             dragLabel.style.display = "none";
             dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
-            eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = false) : (ele.hidden = false));
+            eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.hidden = ele.parentElement.hidden = false) : (ele.hidden = false));
         };
         const endDrag = action(() => {
+            hideDragShowOriginalElements();
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
+            SnappingManager.SetIsDragging(false);
             SnappingManager.clearSnapLines();
             batch.end();
         });
 
         AbortDrag = () => {
-            hideDragShowOriginalElements();
-            SnappingManager.SetIsDragging(false);
             options?.dragComplete?.(new DragCompleteEvent(true, dragData));
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
-            hideDragShowOriginalElements();
             dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
-            SnappingManager.SetIsDragging(false);
-            endDrag();
             options?.dragComplete?.(new DragCompleteEvent(false, dragData));
+            endDrag();
         };
         document.addEventListener("pointermove", moveHandler, true);
         document.addEventListener("pointerup", upHandler);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4dcae8e60..63b99cd85 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -177,19 +177,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             const selectedDocs = SelectionManager.SelectedDocuments();
             if (selectedDocs.length) {
                 if (e.ctrlKey) {    // open an alias in a new tab with Ctrl Key
-                    const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                    alias.context = undefined;
-                    //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
-                    CollectionDockingView.AddSplit(alias, "right");
+                    selectedDocs[0].props.Document._fullScreenView = Doc.MakeAlias(selectedDocs[0].props.Document);
+                    (selectedDocs[0].props.Document._fullScreenView as Doc).context = undefined;
+                    CollectionDockingView.AddSplit(selectedDocs[0].props.Document._fullScreenView as Doc, "right");
                 } else if (e.shiftKey) {   // open centered in a new workspace with Shift Key
                     const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
                     alias.context = undefined;
                     alias.x = -alias[WidthSym]() / 2;
                     alias.y = -alias[HeightSym]() / 2;
-                    //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
                     CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
                 } else {    // open same document in new tab
-                    CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
+                    CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right");
                 }
             }
         }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 2285bcb8f..62f733058 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -28,6 +28,7 @@ import { CollectionViewType } from "./CollectionView";
 import { SnappingManager } from "../../util/SnappingManager";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocUtils } from "../../documents/Documents";
+import { DocAfterFocusFunc } from "../nodes/DocumentView";
 const _global = (window /* browser */ || global /* node */) as any;
 
 type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -170,9 +171,9 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     }
 
 
-    focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => {
+    focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, didFocus?: boolean) => {
         Doc.BrushDoc(doc);
-        this.props.focus(this.props.Document, true);  // bcz: HACK ARgh.. need to update all focus() functions to take parameters about how to focus.  in this case, we want to zoom so we pass true and hope an ancestor is a collection view
+        this.props.focus(this.props.Document, true);  // bcz: want our containing collection to zoom
         Doc.linkFollowHighlight(doc);
 
         const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName("documentView-node")).find((node: any) => node.id === doc[Id]);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 38b9b399d..530595cd0 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -21,7 +21,7 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { DocumentView } from "../nodes/DocumentView";
+import { DocumentView, DocAfterFocusFunc } from "../nodes/DocumentView";
 import { PresBox, PresMovement } from '../nodes/PresBox';
 import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
@@ -343,11 +343,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             </Tooltip>
         </>;
     }
-    focusFunc = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => void) => {
+    focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, notFocused?: boolean) => {
         if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
             this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
         }
-        afterFocus?.();
+        afterFocus?.(false);
     }
     setView = action((view: DocumentView) => this._view = view);
     active = () => this._isActive;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8a4ce826f..4541716f3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -33,7 +33,7 @@ import { ContextMenu } from "../../ContextMenu";
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentViewProps, DocAfterFocusFunc } from "../../nodes/DocumentView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { pageSchema } from "../../nodes/ImageBox";
 import { PresBox } from "../../nodes/PresBox";
@@ -887,7 +887,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1];
     }
 
-    focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean, dontCenter?: boolean) => {
+    focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, didFocus?: boolean) => {
         const state = HistoryUtil.getState();
 
         // TODO This technically isn't correct if type !== "doc", as
@@ -927,7 +927,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 } else {
                     !dontCenter && delay && this.props.focus(this.props.Document);
                     // @ts-ignore
-                    afterFocus(true);  // bcz: TODO Aragh -- need to add a parameter to afterFocus() functions to indicate whether the focus function didn't need to scroll
+                    afterFocus(true);
 
                 }
             }
@@ -948,19 +948,23 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
             }
             Doc.BrushDoc(this.props.Document);
-            this.props.focus(this.props.Document);
-            Doc.linkFollowHighlight(doc);
 
-            const notFocused = newPanX === savedState.px && newPanY === savedState.py;
-            afterFocus && setTimeout(() => {
-                // @ts-ignore
-                if (afterFocus?.(notFocused)) { // bcz: TODO Aragh -- need to add a parameter to afterFocus() functions to indicate whether the focus function didn't need to scroll
-                    this.Document._panX = savedState.px;
-                    this.Document._panY = savedState.py;
-                    this.Document[this.scaleFieldKey] = savedState.s;
-                    this.Document._viewTransition = savedState.pt;
-                }
-            }, notFocused ? 0 : 500);
+            const newDidFocus = didFocus || (newPanX !== savedState.px || newPanY !== savedState.py);
+
+            const newAfterFocus = (didFocus: boolean) => {
+                afterFocus && setTimeout(() => {
+                    // @ts-ignore
+                    if (afterFocus?.(didFocus || (newPanX !== savedState.px || newPanY !== savedState.py))) {
+                        this.Document._panX = savedState.px;
+                        this.Document._panY = savedState.py;
+                        this.Document[this.scaleFieldKey] = savedState.s;
+                        this.Document._viewTransition = savedState.pt;
+                    }
+                }, newPanX !== savedState.px || newPanY !== savedState.py ? 500 : 0);
+                return false;
+            };
+            this.props.focus(this.props.Document, undefined, undefined, newAfterFocus, undefined, newDidFocus);
+            Doc.linkFollowHighlight(doc);
         }
 
     }
@@ -1434,8 +1438,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         return false;
     });
+
+    chooseGridSpace = (start: number): number => {
+        const w = this.props.PanelWidth() / this.zoomScaling() + 3 * start;
+        if (w / start > 60) return this.chooseGridSpace(start * 10);
+        return start;
+    }
+
     @computed get grid() {
-        const gridSpace = NumCast(this.layoutDoc["_backgroundGrid-space"], 50);
+        const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-space"], 50));
         const shiftX = this.props.isAnnotationOverlay ? 0 : -(this.panX()) % gridSpace - gridSpace;
         const shiftY = this.props.isAnnotationOverlay ? 0 : -(this.panY()) % gridSpace - gridSpace;
         const cx = this.centeringShiftX();
@@ -1448,6 +1459,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 if (ctx) {
                     const Cx = (cx / this.zoomScaling()) % gridSpace - 0;
                     const Cy = (cy / this.zoomScaling()) % gridSpace - 0;
+                    ctx.lineWidth = Math.min(1, Math.max(0.5, 0.5 / this.zoomScaling())) * (gridSpace > 50 ? 3 : 1);
+                    ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
                     ctx.clearRect(0, 0, w, h);
                     if (ctx) {
                         ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4efa5d67f..77f63b457 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -42,7 +42,8 @@ import { RadialMenu } from './RadialMenu';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import React = require("react");
 
-export type DocFocusFunc = () => boolean;
+export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
+export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
@@ -82,7 +83,7 @@ export interface DocumentViewProps {
     PanelHeight: () => number;
     pointerEvents?: string;
     contentsPointerEvents?: string;
-    focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
+    focus: DocFocusFunc;
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
@@ -331,11 +332,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                             InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true);
                         } else {
                             UndoManager.RunInBatch(() => {
-                                let fullScreenDoc = this.props.Document;
-                                if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
-                                    fullScreenDoc = Doc.MakeAlias(this.props.Document);
-                                    fullScreenDoc.layoutKey = "layout_fullScreen";
-                                }
+                                const fullScreenDoc = Cast(this.props.Document._fullScreenView, Doc, null) || this.props.Document;
                                 this.props.addDocTab(fullScreenDoc, "add");
                             }, "double tap");
                             SelectionManager.DeselectAll();
@@ -385,7 +382,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
     // depending on the followLinkLocation property of the source (or the link itself as a fallback);
     public static followLinkClick = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docView: {
-        focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void,
+        focus: DocFocusFunc,
         addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean,
         ContainingCollectionDoc?: Doc
     }, shiftKey: boolean, altKey: boolean) => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 3a5b27b21..79947c023 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -11,6 +11,7 @@ import { CollectionView } from "../collections/CollectionView";
 import { AudioBox } from "./AudioBox";
 import { VideoBox } from "./VideoBox";
 import { dropActionType } from "../../util/DragManager";
+import { DocAfterFocusFunc, DocFocusFunc } from "./DocumentView";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -47,7 +48,7 @@ export interface FieldViewProps {
     whenActiveChanged: (isActive: boolean) => void;
     LayoutTemplateString?: string;
     dontRegisterView?: boolean;
-    focus: (doc: Doc) => void;
+    focus: DocFocusFunc;
     presMultiSelect?: (doc: Doc) => void; //added for selecting multiple documents in a presentation
     ignoreAutoHeight?: boolean;
     PanelWidth: () => number;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 311aaa769..6e58f7f13 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -15,6 +15,7 @@ export const documentSchema = createSchema({
     // "Location" properties in a very general sense
     _curPage: "number",         // current page of a page based document
     _currentFrame: "number",    // current frame of a frame based collection (e.g., a progressive slide)
+    _fullScreenView: Doc,       // alias to display when double-clicking to open document in a full-screen view
     lastFrame: "number",        // last frame of a frame based collection (e.g., a progressive slide)
     activeFrame: "number",      // the active frame of a frame based animated document 
     _currentTimecode: "number", // current play back time of a temporal document (video / audio)
-- 
cgit v1.2.3-70-g09d2


From 83d3756537cfcd8a2248c4b8a2a6f75f77154602 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 9 Nov 2020 21:40:59 -0500
Subject: fixed autoHeight for formattedTextSidebars when the text document has
 been scaled.  cleaned up link groups.  fixed pushpins that don't focus on
 target (e.g., a si m ple pushpin on a PDF).

---
 src/client/util/LinkManager.ts                     |  9 +----
 .../views/collections/CollectionStackingView.tsx   | 10 +++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 src/client/views/linking/LinkMenu.tsx              | 29 ++++++---------
 src/client/views/linking/LinkMenuGroup.tsx         | 43 +++-------------------
 src/client/views/nodes/LinkDocPreview.tsx          | 17 ++++-----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 20 +++++-----
 7 files changed, 42 insertions(+), 89 deletions(-)

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

diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3a0c0e3eb..802b8ae7b 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -35,7 +35,7 @@ export class LinkManager {
 
     allLinks = computedFn(function allLinks(this: any): Doc[] {
         const lset = new Set<Doc>(DocListCast(Doc.LinkDBDoc().data));
-        SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).map(doc => lset.add(doc)));
+        SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).forEach(doc => lset.add(doc)));
         return Array.from(lset);
     }, true);
 
@@ -69,13 +69,6 @@ export class LinkManager {
         return anchorGroups;
     }
 
-    // returns a list of all metadata docs associated with the given group type
-    public getAllMetadataDocsInGroup(groupType: string): Array<Doc> {
-        const md: Doc[] = [];
-        LinkManager.Instance.allLinks().forEach(linkDoc => StrCast(linkDoc.linkRelationship).toUpperCase() === groupType.toUpperCase() && md.push(linkDoc));
-        return md;
-    }
-
     // checks if a link with the given anchors exists
     public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean {
         return -1 !== LinkManager.Instance.allLinks().findIndex(linkDoc =>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 62f733058..97eacaeab 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -352,8 +352,12 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
                         if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
-                            const height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
-                            Doc.Layout(doc)._height = Math.max(height, NumCast(doc[this.props.fieldKey + "-height"]));
+                            const height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => NumCast(Doc.Layout(doc)._viewScale, 1) * Number(getComputedStyle(r).height.replace("px", "")))));
+                            if (this.props.annotationsKey) {
+                                doc[this.props.annotationsKey + "-height"] = height;
+                            } else {
+                                Doc.Layout(doc)._height = height * NumCast(Doc.Layout(doc)._viewScale, 1);
+                            }
                         }
                     }));
                     this.observer.observe(ref);
@@ -406,7 +410,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
                         if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
-                            Doc.Layout(doc)._height = Math.max(height, NumCast(doc[this.props.fieldKey + "-height"]));
+                            Doc.Layout(doc)._height = Math.max(height * NumCast(doc[this.props.fieldKey + "-height"]), NumCast(doc[this.props.fieldKey + "-height"]));
                         }
                     }));
                     this.observer.observe(ref);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4541716f3..21e653e02 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -926,8 +926,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     afterFocus && setTimeout(afterFocus, delay);
                 } else {
                     !dontCenter && delay && this.props.focus(this.props.Document);
-                    // @ts-ignore
-                    afterFocus(true);
+                    afterFocus?.(!dontCenter && delay ? true : false);
 
                 }
             }
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 3b4912d3d..54b597f59 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -59,24 +59,17 @@ export class LinkMenu extends React.Component<Props> {
     }
 
     renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => {
-        const linkItems: Array<JSX.Element> = [];
-        groups.forEach((group, groupType) => {
-            linkItems.push(
-                <LinkMenuGroup
-                    key={groupType}
-                    docView={this.props.docView}
-                    sourceDoc={this.props.docView.props.Document}
-                    group={group}
-                    groupType={groupType}
-                    showEditor={action((linkDoc: Doc) => this._editingLink = linkDoc)}
-                    addDocTab={this.props.addDocTab} />
-            );
-        });
-
-        // if source doc has no links push message
-        if (linkItems.length === 0) linkItems.push(<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>);
-
-        return linkItems;
+        const linkItems = Array.from(groups.entries()).map(group =>
+            <LinkMenuGroup
+                key={group[0]}
+                docView={this.props.docView}
+                sourceDoc={this.props.docView.props.Document}
+                group={group[1]}
+                groupType={group[0]}
+                showEditor={action(linkDoc => this._editingLink = linkDoc)}
+                addDocTab={this.props.addDocTab} />);
+
+        return linkItems.length ? linkItems : [<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
     }
 
     @computed
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index e76227ccf..e53655fd3 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,17 +1,12 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
-import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { Docs } from "../../documents/Documents";
-import { DragManager, SetupDrag } from "../../util/DragManager";
+import { Cast } from "../../../fields/Types";
 import { LinkManager } from "../../util/LinkManager";
 import { DocumentView } from "../nodes/DocumentView";
 import './LinkMenu.scss';
-import { LinkMenuItem, StartLinkTargetsDrag } from "./LinkMenuItem";
+import { LinkMenuItem } from "./LinkMenuItem";
 import React = require("react");
-import { Cast } from "../../../fields/Types";
 
 interface LinkMenuGroupProps {
     sourceDoc: Doc;
@@ -26,34 +21,8 @@ interface LinkMenuGroupProps {
 export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
 
     private _drag = React.createRef<HTMLDivElement>();
-    private _table = React.createRef<HTMLDivElement>();
     private _menuRef = React.createRef<HTMLDivElement>();
 
-    onLinkButtonDown = (e: React.PointerEvent): void => {
-        e.stopPropagation();
-        document.removeEventListener("pointermove", this.onLinkButtonMoved);
-        document.addEventListener("pointermove", this.onLinkButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkButtonUp);
-        document.addEventListener("pointerup", this.onLinkButtonUp);
-    }
-
-    onLinkButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onLinkButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkButtonUp);
-        e.stopPropagation();
-    }
-
-    onLinkButtonMoved = async (e: PointerEvent) => {
-        if (this._drag.current && (e.movementX > 1 || e.movementY > 1)) {
-            document.removeEventListener("pointermove", this.onLinkButtonMoved);
-            document.removeEventListener("pointerup", this.onLinkButtonUp);
-
-            const targets = this.props.group.map(l => LinkManager.getOppositeAnchor(l, this.props.sourceDoc)).filter(d => d) as Doc[];
-            StartLinkTargetsDrag(this._drag.current, this.props.docView, e.x, e.y, this.props.sourceDoc, targets);
-        }
-        e.stopPropagation();
-    }
-
     render() {
         const set = new Set<Doc>(this.props.group);
         const groupItems = Array.from(set.keys()).map(linkDoc => {
@@ -75,11 +44,9 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
         return (
             <div className="linkMenu-group" ref={this._menuRef}>
 
-                {/* <div className="linkMenu-group-name">
-                    <p ref={this._drag} onPointerDown={this.onLinkButtonDown}
-                        className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p>
-                    {this.props.groupType === "*" || this.props.groupType === "" ? <></> : this.viewGroupAsTable(this.props.groupType)}
-                </div> */}
+                <div className="linkMenu-group-name">
+                    <p ref={this._drag} className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p>
+                </div>
 
                 <div className="linkMenu-group-wrapper">
                     {groupItems}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 867be9735..d47942bd9 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -77,19 +77,16 @@ export class LinkDocPreview extends React.Component<Props> {
             this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { _fitWidth: true, title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
         }
     }
-    width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)) - 16;
-    height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)) - 16;
+    width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
+    height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
     @computed get targetDocView() {
         return !this._targetDoc ?
-            <div style={{
-                pointerEvents: "all", maxWidth: 225, maxHeight: 225, width: "100%", height: "100%",
-                overflow: "hidden"
-            }}>
+            <div style={{ pointerEvents: "all", maxWidth: 225, maxHeight: 225, width: "100%", height: "100%", overflow: "hidden" }}>
                 <div style={{ width: "100%", height: "100%", textOverflow: "ellipsis", }} onPointerDown={this.pointerDown}>
                     {this._toolTipText}
                 </div>
-            </div> :
-
+            </div>
+            :
             <ContentFittingDocumentView
                 Document={this._targetDoc}
                 LibraryPath={emptyPath}
@@ -109,8 +106,8 @@ export class LinkDocPreview extends React.Component<Props> {
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 renderDepth={-1}
-                PanelWidth={this.width} //Math.min(350, NumCast(target._width, 350))}
-                PanelHeight={this.height} //Math.min(250, NumCast(target._height, 250))}
+                PanelWidth={this.width}
+                PanelHeight={this.height}
                 focus={emptyFunction}
                 whenActiveChanged={returnFalse}
                 bringToFront={returnFalse}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index bc9e8e99d..fe38939c5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -812,6 +812,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     componentDidMount() {
         this._cachedLinks = DocListCast(this.Document.links);
+        this._disposers.sidebarheight = reaction(
+            () => ({ annoHeight: NumCast(this.rootDoc[this.annotationKey + "-height"]), textHeight: NumCast(this.rootDoc[this.fieldKey + "-height"]) }),
+            ({ annoHeight, textHeight }) => {
+                this.layoutDoc._autoHeight && (this.rootDoc._height = Math.max(annoHeight, textHeight));
+            });
         this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
             newLinks => {
                 this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
@@ -1540,18 +1545,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }, 10);
             } else {
                 try {
-                    const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+                    const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", "")) * NumCast(this.Document._viewScale, 1);
                     const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
-                    const finalHeight = newHeight + Math.max(0, outer);
-                    const maxsidebar = !this.sidebarWidth() ? 0 : Array.from(this._boxRef.current!.getElementsByClassName("collectionStackingViewFieldColumn")).reduce((prev, ele) => Math.max(prev, Number(getComputedStyle(ele).height.replace("px", ""))), 0);
-                    if (this.rootDoc._height !== finalHeight && finalHeight > maxsidebar) {
-                        this.rootDoc._height = finalHeight;
-                        this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
-                    }
-                    this.rootDoc[this.fieldKey + "-height"] = finalHeight;
+                    this.rootDoc[this.fieldKey + "-height"] = newHeight + Math.max(0, outer);
                 } catch (e) { console.log("Error in tryUpdateHeight"); }
             }
-        } else this.rootDoc[this.fieldKey + "-height"] = 0;
+        } //else this.rootDoc[this.fieldKey + "-height"] = 0;
     }
 
     @computed get audioHandle() {
@@ -1609,7 +1608,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
     sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
-    sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
+    sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0).scale(1 / NumCast(this.layoutDoc._viewScale, 1));
     @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
     render() {
         TraceMobx();
@@ -1631,6 +1630,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     transformOrigin: "top left",
                     width: `${100 / scale}%`,
                     height: `calc(${100 / scale}% - ${this.props.ChromeHeight?.() || 0}px)`,
+                    overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
                     ...this.styleFromLayoutString(scale)   // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
                 }}>
                 <div className={`formattedTextBox-cont`} ref={this._ref}
-- 
cgit v1.2.3-70-g09d2


From 28dccafaa4aa446dd88c1b6f4218a0d7f79fa1bb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 10 Nov 2020 12:51:48 -0500
Subject: fixed performance issues with background grid by avoiding making
 really big canvases.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 33 ++++++++++------------
 src/fields/documentSchemas.ts                      |  1 +
 2 files changed, 16 insertions(+), 18 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 21e653e02..1033050b9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1438,38 +1438,35 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return false;
     });
 
-    chooseGridSpace = (start: number): number => {
-        const w = this.props.PanelWidth() / this.zoomScaling() + 3 * start;
-        if (w / start > 60) return this.chooseGridSpace(start * 10);
-        return start;
+    chooseGridSpace = (gridSpace: number): number => {
+        const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3;
+        return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
     }
 
     @computed get grid() {
-        const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-space"], 50));
-        const shiftX = this.props.isAnnotationOverlay ? 0 : -(this.panX()) % gridSpace - gridSpace;
-        const shiftY = this.props.isAnnotationOverlay ? 0 : -(this.panY()) % gridSpace - gridSpace;
-        const cx = this.centeringShiftX();
-        const cy = this.centeringShiftY();
-        const w = this.props.PanelWidth() / this.zoomScaling() + 3 * gridSpace;
-        const h = this.props.PanelHeight() / this.zoomScaling() + 3 * gridSpace;
-        return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `scale(${this.zoomScaling()}) translate(${shiftX}px, ${shiftY}px)` }}
+        const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-spacing"], 50));
+        const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.panX() % gridSpace - gridSpace) * this.zoomScaling();
+        const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.panY() % gridSpace - gridSpace) * this.zoomScaling();
+        const renderGridSpace = gridSpace * this.zoomScaling();
+        const w = this.props.PanelWidth() + 2 * renderGridSpace;
+        const h = this.props.PanelHeight() + 2 * renderGridSpace;
+        return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
             ref={(el) => {
                 const ctx = el?.getContext('2d');
                 if (ctx) {
-                    const Cx = (cx / this.zoomScaling()) % gridSpace - 0;
-                    const Cy = (cy / this.zoomScaling()) % gridSpace - 0;
-                    ctx.lineWidth = Math.min(1, Math.max(0.5, 0.5 / this.zoomScaling())) * (gridSpace > 50 ? 3 : 1);
+                    const Cx = this.centeringShiftX() % renderGridSpace;
+                    const Cy = this.centeringShiftY() % renderGridSpace;
+                    ctx.lineWidth = Math.min(1, Math.max(0.5, this.zoomScaling()));
                     ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
                     ctx.clearRect(0, 0, w, h);
                     if (ctx) {
                         ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
-                        ctx.lineWidth = Math.min(1, Math.max(0.5, 0.5 / this.zoomScaling()));
                         ctx.beginPath();
-                        for (let x = Cx; x <= -Cx + w; x += gridSpace) {
+                        for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
                             ctx.moveTo(x, Cy - h);
                             ctx.lineTo(x, Cy + h);
                         }
-                        for (let y = Cy; y <= -Cy + h; y += gridSpace) {
+                        for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
                             ctx.moveTo(Cx - w, y);
                             ctx.lineTo(Cx + w, y);
                         }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 6e58f7f13..e0404d9d3 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -34,6 +34,7 @@ export const documentSchema = createSchema({
     _scrollLeft: "number",        // scroll position of a scrollable document (pdf, text, web)
 
     // appearance properties on the layout
+    "_backgroundGrid-spacing": "number", // the size of the grid for collection views
     _autoHeight: "boolean",     // whether the height of the document should be computed automatically based on its contents
     _nativeWidth: "number",     // native width of document which determines how much document contents are scaled when the document's width is set
     _nativeHeight: "number",    // "
-- 
cgit v1.2.3-70-g09d2

