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.

---
 src/client/views/nodes/DocumentView.tsx | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e980322d5..d227d3a8e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -129,8 +129,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get topMost() { return this.props.renderDepth === 0; }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
-    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
-    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
     @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
     @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@@ -494,8 +494,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 const doc = Document(this.props.Document);
                 const layoutDoc = Document(Doc.Layout(this.props.Document));
-                let nwidth = layoutDoc._nativeWidth || 0;
-                let nheight = layoutDoc._nativeHeight || 0;
+                let nwidth = Doc.NativeWidth(layoutDoc);
+                let nheight = Doc.NativeHeight(layoutDoc);
                 const width = (layoutDoc._width || 0);
                 const height = (layoutDoc._height || (nheight / nwidth * width));
                 const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling();
@@ -505,13 +505,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = e.ctrlKey || (nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
-                    layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
-                    layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
+                    Doc.SetNativeWidth(layoutDoc, nwidth = layoutDoc._width || 0);
+                    Doc.SetNativeHeight(layoutDoc, nheight = layoutDoc._height || 0);
                 }
                 if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
-                            layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0);
+                            Doc.SetNativeWidth(layoutDoc, actualdW / (layoutDoc._width || 1) * Doc.NativeWidth(layoutDoc));
                         }
                         layoutDoc._width = actualdW;
                         if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width;
@@ -519,7 +519,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     }
                     else {
                         if (!fixedAspect) {
-                            layoutDoc._nativeHeight = actualdH / (layoutDoc._height || 1) * (doc._nativeHeight || 0);
+                            Doc.SetNativeHeight(layoutDoc, actualdH / (layoutDoc._height || 1) * Doc.NativeHeight(doc));
                         }
                         layoutDoc._height = actualdH;
                         if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height;
@@ -754,8 +754,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this.props.bringToFront(this.props.Document, true);
             const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
             const hgt = this.Document[HeightSym]();
-            this.props.Document[DataSym][this.LayoutFieldKey + "-nativeWidth"] = wid;
-            this.props.Document[DataSym][this.LayoutFieldKey + "-nativeHeight"] = hgt;
+            Doc.SetNativeWidth(this.props.Document[DataSym], wid);
+            Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
         }
     }
 
@@ -907,7 +907,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const excluded = ["PresBox", /* "FormattedTextBox", */ "FontIconBox"]; // bcz: shifting the title for texst causes problems with collaborative use when some people see titles, and others don't
         return !excluded.includes(StrCast(this.layoutDoc.layout));
     }
-    chromeHeight = () => this.showOverlappingTitle ? 1 : 25;
+    chromeHeight = () => this.showOverlappingTitle ? 0 : 25;
 
     @computed get finalLayoutKey() {
         if (typeof this.props.layoutKey === "string") {
-- 
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/nodes/DocumentView.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 7096b3a4c888816db9196f56a7bc01b89558d0ce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 23 Oct 2020 15:04:25 -0400
Subject: improved selection/play/context menu behavior of youtube videos. 
 fixed youtube videos to pinwithview better by capturing timecode after a seek
 drag event so that you can pin with view.

---
 src/client/views/collections/CollectionMenu.tsx | 2 +-
 src/client/views/nodes/DocumentView.tsx         | 2 +-
 src/client/views/nodes/VideoBox.tsx             | 4 ++++
 3 files changed, 6 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index abad23e0d..34a1c0697 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -400,7 +400,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     pinWithView = (targetDoc: Opt<Doc>) => {
         if (targetDoc) {
             TabDocView.PinDoc(targetDoc, false);
-            const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
+            const activeDoc = PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.length - 1];
             if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
                 const scroll = targetDoc._scrollTop;
                 activeDoc.presPinView = true;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 08a4a7e23..96ae1dc2b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -889,7 +889,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 e?.stopPropagation(); // DocumentViews should stop propagation of this event
             }
             cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
-            !this.isSelected(true) && SelectionManager.SelectDoc(this, false);
+            !this.isSelected(true) && setTimeout(() => SelectionManager.SelectDoc(this, false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
         });
     }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 30e8db645..250595db5 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -20,6 +20,7 @@ import "./VideoBox.scss";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Networking } from "../../Network";
 import { SnappingManager } from "../../util/SnappingManager";
+import { SelectionManager } from "../../util/SelectionManager";
 const path = require('path');
 
 export const timeSchema = createSchema({
@@ -75,6 +76,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         update && this.player?.pause();
         update && this._youtubePlayer?.pauseVideo();
         this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
+        this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
+        this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
         this._playTimer = undefined;
         this.updateTimecode();
     }
@@ -346,6 +349,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
         const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
+            onPointerLeave={this.updateTimecode}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
             src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
     }
-- 
cgit v1.2.3-70-g09d2


From f4ab30f9a939587ddebfb50f55d999b458954bfb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 25 Oct 2020 23:36:24 -0400
Subject: fixed placement of link button for scaled text views.  fixed document
 decorations for ContentFittingDocViews & fitWidth items.  Fixed
 documentDeocrations for CollectionMulticolumn view.

---
 .../collectionMulticolumn/CollectionMulticolumnView.tsx           | 5 +++--
 src/client/views/nodes/ContentFittingDocumentView.tsx             | 8 +++++++-
 src/client/views/nodes/DocumentLinksButton.scss                   | 6 ++++++
 src/client/views/nodes/DocumentLinksButton.tsx                    | 4 ++--
 src/client/views/nodes/DocumentView.tsx                           | 2 +-
 5 files changed, 19 insertions(+), 6 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b99bef15e..6e16137b5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -213,10 +213,9 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         return this.props.addDocTab(doc, where);
     }
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
-        const layoutTemp = this.props.DataDoc ? true : undefined;
         return <ContentFittingDocumentView
             Document={layout}
-            DataDoc={layout.resolvedDataDoc as Doc || (layoutTemp ? layout : undefined)}
+            DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
             LayoutTemplate={this.props.ChildLayoutTemplate}
             LayoutTemplateString={this.props.ChildLayoutString}
@@ -290,6 +289,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         return (
             <div className={"collectionMulticolumnView_contents"}
                 style={{
+                    width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
+                    height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
                     marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
                     marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
                 }} ref={this.createDashEventsTarget}>
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index bc3926ff4..bc3ad5bce 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -38,7 +38,13 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     private PanelHeight = () => this.panelHeight;
 
     @computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
-    @computed get panelHeight() { return this.nativeHeight() && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
+    @computed get panelHeight() {
+        if (this.nativeHeight()) {
+            if (!this.props.Document._fitWidth) return this.nativeHeight() * this.contentScaling()
+            else return this.panelWidth / Doc.NativeAspect(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 1;
+        }
+        return this.props.PanelHeight();
+    }
 
     @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); }  // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply.
     private getTransform = () => this.props.dontCenter ?
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 9328fb96b..735aa669f 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,6 +1,11 @@
 @import "../globalCssVariables.scss";
 
 
+.documentLinksButton-cont {
+    min-width: 20;
+    min-height: 20;
+    position: absolute;
+}
 .documentLinksButton,
 .documentLinksButton-endLink,
 .documentLinksButton-startLink {
@@ -29,6 +34,7 @@
 
 .documentLinksButton {
     background-color: black;
+    font-weight: bold;
 
     &:hover {
         background: $main-accent;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index ddfb3cc34..a167f2a23 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -243,13 +243,13 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             src={`/assets/${"link.png"}`} />;
 
         const linkButton = <div className="documentLinksButton-cont" ref={this._linkButton} style={{
-            minWidth: 20, minHeight: 20, position: "absolute",
             left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3]
         }}>
             <div className={"documentLinksButton"} style={{
                 backgroundColor: this.props.InMenu ? "" : "#add8e6",
                 color: this.props.InMenu ? "white" : "black",
-                width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px", fontWeight: "bold"
+                width: this.props.InMenu ? "20px" : "30px",
+                height: this.props.InMenu ? "20px" : "30px",
             }}
                 onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick}
             // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 96ae1dc2b..0b27cd03c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -920,7 +920,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     }
     childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
-    @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, undefined]; }
+    @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, -20]; }
     @computed get contents() {
         TraceMobx();
         return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}>
-- 
cgit v1.2.3-70-g09d2


From e1e0d9cf9b66f91534a3afd73bc9668afa8169c9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 30 Oct 2020 15:32:34 -0400
Subject: fixed warning about onClick... being adding multipl times.

---
 src/client/views/nodes/DocumentView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0b27cd03c..a4da00578 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -839,8 +839,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
                 onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
                 onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
+                !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
             }
-            !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
         }
 
         const funcs: ContextMenuProps[] = [];
-- 
cgit v1.2.3-70-g09d2


From a957215f38affed8b4372977cc3850f7ea7d6677 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Nov 2020 14:48:26 -0500
Subject: fixed highlighting rounded documents to not be jumpy

---
 src/client/views/nodes/DocumentView.tsx | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a4da00578..01aad2e8e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1148,11 +1148,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 pointerEvents: this.ignorePointerEvents ? "none" : undefined,
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
-                border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
-                boxShadow: this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
-                    StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") :
-                    this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
-                        undefined,
+                boxShadow: highlighting && borderRounding ? `0 0 0 ${localScale}px ${highlightColors[fullDegree]}` :
+                    this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
+                        StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") :
+                        this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
+                            undefined,
                 background: finalColor,
                 opacity: finalOpacity,
                 fontFamily: StrCast(this.Document._fontFamily, "inherit"),
-- 
cgit v1.2.3-70-g09d2


From e45b1d51636e4ac46ec315b8ac007dcefca9e0e4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Nov 2020 15:08:46 -0500
Subject: better fix for hiighlighting rounded border documents.

---
 src/client/views/nodes/DocumentView.tsx | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 01aad2e8e..5fb2a8db9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1144,13 +1144,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             style={{
                 transformOrigin: this._animateScalingTo ? "center center" : undefined,
                 transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
-                transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
+                transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
                 pointerEvents: this.ignorePointerEvents ? "none" : undefined,
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
-                boxShadow: highlighting && borderRounding ? `0 0 0 ${localScale}px ${highlightColors[fullDegree]}` :
+                border: highlighting && borderRounding && highlightStyles[fullDegree] === "dashed" ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
+                boxShadow: highlighting && borderRounding && highlightStyles[fullDegree] !== "dashed" ? `0 0 0 ${localScale}px ${highlightColors[fullDegree]}` :
                     this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
-                        StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") :
+                        StrCast(this.layoutDoc._linkButtonShadow, "lightblue 0em 0em 1em") :
                         this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
                             undefined,
                 background: finalColor,
-- 
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/nodes/DocumentView.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 f23e2f2bcb6ab7fb1895a87a34f8eafcbab90a8d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 3 Nov 2020 21:40:53 -0500
Subject: fixed LinkManager to cache links correctly. fixes issue with
 linkButton not showing the correct number.

---
 src/client/util/LinkManager.ts                 |  28 +++----
 src/client/views/nodes/DocumentLinksButton.tsx | 109 ++++++++++---------------
 src/client/views/nodes/DocumentView.tsx        |   2 +-
 3 files changed, 57 insertions(+), 82 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index c3ab7c6e7..3a0c0e3eb 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -22,47 +22,41 @@ import { computedFn } from "mobx-utils";
 export class LinkManager {
 
     private static _instance: LinkManager;
-
     public static currentLink: Opt<Doc>;
-
     public static get Instance(): LinkManager { return this._instance || (this._instance = new this()); }
 
     public addLink(linkDoc: Doc) { return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc); }
-
     public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
-
-    public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.getAllRelatedLinks(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
+    public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
 
     public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
-
     public getAllDirectLinks(anchor: Doc): Doc[] { return this.directLinker(anchor); }  // finds all links that contain the given anchor
-
     public getAllLinks(): Doc[] { return this.allLinks(); }
 
-    allLinks = computedFn(function allLinks(this: any) {
+    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)));
         return Array.from(lset);
     }, true);
 
-    directLinker = computedFn(function directLinker(this: any, anchor: Doc) {
-        return LinkManager.Instance.getAllLinks().filter(link => {
+    directLinker = computedFn(function directLinker(this: any, anchor: Doc): Doc[] {
+        return LinkManager.Instance.allLinks().filter(link => {
             const a1 = Cast(link?.anchor1, Doc, null);
             const a2 = Cast(link?.anchor2, Doc, null);
             return link && ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (Doc.AreProtosEqual(anchor, a1) || Doc.AreProtosEqual(anchor, a2) || Doc.AreProtosEqual(link, anchor));
         });
     }, true);
 
-    relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc) {
-        const related = LinkManager.Instance.directLinker(anchor);
-        DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => related.push(...LinkManager.Instance.getAllRelatedLinks(anno)));
-        return related;
+    relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
+        return DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).reduce((list, anno) =>
+            [...list, ...LinkManager.Instance.relatedLinker(anno)],
+            LinkManager.Instance.directLinker(anchor).slice());
     }, true);
 
     // returns map of group type to anchor's links in that group type
     public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
         const anchorGroups = new Map<string, Array<Doc>>();
-        this.getAllRelatedLinks(anchor).forEach(link => {
+        this.relatedLinker(anchor).forEach(link => {
             if (!link.linkRelationship || link?.linkRelationship !== "-ungrouped-") {
                 const group = anchorGroups.get(StrCast(link.linkRelationship));
                 anchorGroups.set(StrCast(link.linkRelationship), group ? [...group, link] : [link]);
@@ -78,13 +72,13 @@ export class LinkManager {
     // returns a list of all metadata docs associated with the given group type
     public getAllMetadataDocsInGroup(groupType: string): Array<Doc> {
         const md: Doc[] = [];
-        LinkManager.Instance.getAllLinks().forEach(linkDoc => StrCast(linkDoc.linkRelationship).toUpperCase() === groupType.toUpperCase() && md.push(linkDoc));
+        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.getAllLinks().findIndex(linkDoc =>
+        return -1 !== LinkManager.Instance.allLinks().findIndex(linkDoc =>
             (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) ||
             (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1)));
     }
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index a167f2a23..95c007175 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -3,7 +3,6 @@ import { Tooltip } from "@material-ui/core";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { DocumentType } from "../../documents/DocumentTypes";
 import { emptyFunction, setupMoveUpEvents, returnFalse, Utils, emptyPath } from "../../../Utils";
 import { TraceMobx } from "../../../fields/util";
 import { DocUtils, Docs } from "../../documents/Documents";
@@ -39,6 +38,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     @observable public static StartLinkView: DocumentView | undefined;
     @observable public static AnnotationId: string | undefined;
     @observable public static AnnotationUri: string | undefined;
+    @observable public static EditLink: DocumentView | undefined;
 
     @observable public static invisibleWebDoc: Opt<Doc>;
     public static invisibleWebRef = React.createRef<HTMLDivElement>();
@@ -209,94 +209,75 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         }
     }));
 
-    @observable
-    public static EditLink: DocumentView | undefined;
 
     @action clearLinks() {
         DocumentLinksButton.StartLink = undefined;
         DocumentLinksButton.StartLinkView = undefined;
     }
 
-    @computed
-    get linkButton() {
-        TraceMobx();
-        const links = DocUtils.FilterDocs(Array.from(new Set<Doc>(this.props.links)), this.props.View.props.docFilters(), []);
-
-        const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
-        const buttonTitle = "Tap to view links";
-        const title = this.props.InMenu ? menuTitle : buttonTitle;
-
-
-        const startLink = <img
-            style={{ width: "11px", height: "11px" }}
-            id={"startLink-icon"}
-            src={`/assets/${"startLink.png"}`} />;
-
-        const endLink = <img
-            style={{ width: "14px", height: "9px" }}
-            id={"endLink-icon"}
-            src={`/assets/${"endLink.png"}`} />;
+    @computed get filteredLinks() {
+        return DocUtils.FilterDocs(Array.from(new Set<Doc>(this.props.links)), this.props.View.props.docFilters(), []);
+    }
 
-        const link = <img
-            style={{ width: "22px", height: "16px" }}
-            id={"link-icon"}
-            src={`/assets/${"link.png"}`} />;
+    @computed get linkButtonInner() {
+        const btnDim = this.props.InMenu ? "20px" : "30px";
+        const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />;
 
-        const linkButton = <div className="documentLinksButton-cont" ref={this._linkButton} style={{
-            left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3]
-        }}>
-            <div className={"documentLinksButton"} style={{
-                backgroundColor: this.props.InMenu ? "" : "#add8e6",
-                color: this.props.InMenu ? "white" : "black",
-                width: this.props.InMenu ? "20px" : "30px",
-                height: this.props.InMenu ? "20px" : "30px",
-            }}
+        return <div className="documentLinksButton-cont" ref={this._linkButton}
+            style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
+        >
+            <div className={"documentLinksButton"}
                 onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick}
-            // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
-            // onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
-            //     addDocTab: this.props.View.props.addDocTab,
-            //     linkSrc: this.props.View.props.Document,
-            //     linkDoc: links[0],
-            //     Location: [e.clientX, e.clientY + 20]
-            // }))} 
-            >
-
-                {/* {this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
-                    <FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length} */}
-
-                {this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
-                    link : Array.from(links).length}
-
+                style={{
+                    backgroundColor: this.props.InMenu ? "" : "#add8e6",
+                    color: this.props.InMenu ? "white" : "black",
+                    width: btnDim,
+                    height: btnDim,
+                }} >
+                {this.props.InMenu ?
+                    this.props.StartLink ?
+                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />
+                        : link
+                    : Array.from(this.filteredLinks).length}
             </div>
             {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ?
                 <div className={"documentLinksButton-endLink"}
                     style={{
-                        width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px",
+                        width: btnDim, height: btnDim,
                         backgroundColor: DocumentLinksButton.StartLink ? "" : "grey",
                         opacity: DocumentLinksButton.StartLink ? "" : "50%",
                         border: DocumentLinksButton.StartLink ? "" : "none",
                         cursor: DocumentLinksButton.StartLink ? "pointer" : "default"
                     }}
-                    onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
-                    onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
+                    onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
+                    onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)} />
+                : (null)
             }
-            {
-                DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
-                    style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }}
-                    onPointerDown={this.clearLinks} onClick={this.clearLinks}
-                /> : (null)
+            {DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ?
+                <div className={"documentLinksButton-startLink"} onPointerDown={this.clearLinks} onClick={this.clearLinks} style={{ width: btnDim, height: btnDim }} />
+                : (null)
             }
         </div >;
+    }
+
+    @computed get linkButton() {
+        TraceMobx();
+
+        const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
+        const buttonTitle = "Tap to view links";
+        const title = this.props.InMenu ? menuTitle : buttonTitle;
 
-        return (!Array.from(links).length) && !this.props.AlwaysOn ? (null) :
+        return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) :
             this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
                 <Tooltip title={<><div className="dash-tooltip">{title}</div></>}>
-                    {linkButton}
-                </Tooltip> : !!!DocumentLinksButton.EditLink && !this.props.InMenu ?
+                    {this.linkButtonInner}
+                </Tooltip>
+                :
+                !DocumentLinksButton.EditLink && !this.props.InMenu ?
                     <Tooltip title={<><div className="dash-tooltip">{title}</div></>}>
-                        {linkButton}
-                    </Tooltip> :
-                    linkButton;
+                        {this.linkButtonInner}
+                    </Tooltip>
+                    : this.linkButtonInner;
     }
 
     render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f6a147c50..3ebaad0a3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -990,7 +990,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
 
     @computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
-    @computed get allLinks() { TraceMobx(); return DocListCast(this.Document.links); }
+    @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
     @computed get allAnchors() {
         TraceMobx();
         if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null;
-- 
cgit v1.2.3-70-g09d2


From 4becefec2200632b482b7660bd3bb0792a4ba1d8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 4 Nov 2020 15:41:12 -0500
Subject: made font size of tree view ignore font size of document

---
 src/client/views/nodes/DocumentView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3ebaad0a3..4efa5d67f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1157,7 +1157,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 background: finalColor,
                 opacity: finalOpacity,
                 fontFamily: StrCast(this.Document._fontFamily, "inherit"),
-                fontSize: Cast(this.Document._fontSize, "string", null),
+                fontSize: !this.props.treeViewDoc ? Cast(this.Document._fontSize, "string", null) : undefined,
             }}>
             {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <>
                 {this.innards}
-- 
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/nodes/DocumentView.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

