From 0f4b9e541e4e1332bbed95f4a99113162ffef806 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 30 Apr 2020 11:10:27 -0400
Subject: added double click script support

---
 .../views/collections/collectionMulticolumn/CollectionMultirowView.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index af0cc3b5c..615efdb39 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -203,7 +203,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
 
 
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
-
+    @computed get onChildDoubleClickHandler() { return ScriptCast(this.Document.onChildDoubleClick); }
 
     addDocTab = (doc: Doc, where: string) => {
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
@@ -229,6 +229,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
+            onDoubleClick={this.onChildDoubleClickHandler}
             getTransform={dxf}
             focus={this.props.focus}
             CollectionDoc={this.props.CollectionView?.props.Document}
-- 
cgit v1.2.3-70-g09d2


From ff7c7d40b1fcdf74b539c7d97f36707ff1521d2e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 1 May 2020 01:46:07 -0400
Subject: fixed presentations to allow drag and drop.  fixed pres box to use
 RenderData instead of modifying presentation elements with unnecessary info
 like their containing PresBox and their presentation index position. 
 COnverted COntentFIttingDocumentView to use DocumentView's props

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/views/MainView.tsx                      |  4 +-
 src/client/views/SearchDocBox.tsx                  | 10 ++++-
 .../views/collections/CollectionCarouselView.tsx   |  8 +++-
 .../views/collections/CollectionSchemaView.tsx     | 14 ++++---
 .../views/collections/CollectionStackingView.tsx   | 15 ++++---
 .../views/collections/CollectionTreeView.tsx       | 20 +++++----
 src/client/views/collections/CollectionView.tsx    | 14 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 .../CollectionMulticolumnView.tsx                  | 14 ++++---
 .../CollectionMultirowView.tsx                     | 14 ++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  8 ++--
 .../views/nodes/ContentFittingDocumentView.tsx     | 49 ++++------------------
 src/client/views/nodes/DocumentBox.tsx             | 15 ++++---
 src/client/views/nodes/DocumentView.tsx            | 20 ++++-----
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/PresBox.tsx                 | 21 ++++------
 .../formattedText/FormattedTextBoxComment.tsx      | 12 ++++--
 .../views/nodes/formattedText/RichTextMenu.scss    |  3 ++
 .../views/presentationview/PresElementBox.tsx      | 25 +++++++----
 src/client/views/search/SearchItem.tsx             | 12 ++++--
 src/new_fields/documentSchemas.ts                  |  2 +-
 .../authentication/models/current_user_utils.ts    |  2 +-
 23 files changed, 150 insertions(+), 137 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2e81d5fa6..228a6af97 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -81,6 +81,7 @@ export interface DocumentOptions {
     author?: string;
     dropAction?: dropActionType;
     childDropAction?: dropActionType;
+    targetDropAction?: dropActionType;
     layoutKey?: string;
     type?: string;
     title?: string;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 65e4eb036..a29a6baac 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -597,8 +597,8 @@ export class MainView extends React.Component {
             {// TO VIEW SNAP LINES
                 <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
                     <svg style={{ width: "100%", height: "100%" }}>
-                        {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                        {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {this._hLines?.map((l: any) => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {this._vLines?.map((l: any) => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                     </svg>
                 </div>}
             <TimelineMenu />
diff --git a/src/client/views/SearchDocBox.tsx b/src/client/views/SearchDocBox.tsx
index 799fa9d85..7bd689b19 100644
--- a/src/client/views/SearchDocBox.tsx
+++ b/src/client/views/SearchDocBox.tsx
@@ -6,7 +6,7 @@ import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
 import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
-import { returnFalse } from "../../Utils";
+import { returnFalse, returnZero } from "../../Utils";
 import { Docs } from "../documents/Documents";
 import { SearchUtil } from "../util/SearchUtil";
 import { EditableView } from "./EditableView";
@@ -399,7 +399,13 @@ export class SearchDocBox extends React.Component<FieldViewProps> {
                 <ContentFittingDocumentView {...this.props}
                     Document={this.content}
                     rootSelected={returnFalse}
-                    getTransform={this.props.ScreenToLocalTransform}>
+                    bringToFront={returnFalse}
+                    ContainingCollectionDoc={undefined}
+                    ContainingCollectionView={undefined}
+                    NativeWidth={returnZero}
+                    NativeHeight={returnZero}
+                    parentActive={this.props.active}
+                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}>
                 </ContentFittingDocumentView>
                 <div
                     style={{
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index eda8e5684..bfb7134b2 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -14,6 +14,7 @@ import { Doc } from '../../../new_fields/Doc';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { ContextMenu } from '../ContextMenu';
 import { ObjectField } from '../../../new_fields/ObjectField';
+import { returnFalse } from '../../../Utils';
 
 type CarouselDocument = makeInterface<[typeof documentSchema,]>;
 const CarouselDocument = makeInterface(documentSchema);
@@ -49,9 +50,12 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                     <ContentFittingDocumentView {...this.props}
                         renderDepth={this.props.renderDepth + 1}
                         Document={this.childLayoutPairs[index].layout}
-                        DataDocument={this.childLayoutPairs[index].data}
+                        DataDoc={this.childLayoutPairs[index].data}
                         PanelHeight={this.panelHeight}
-                        getTransform={this.props.ScreenToLocalTransform} />
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        bringToFront={returnFalse}
+                        parentActive={this.props.active}
+                    />
                 </div>
                 <div className="collectionCarouselView-caption" key="caption"
                     style={{
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 380d91d2f..c0024293f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -27,7 +27,7 @@ import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionView } from "./CollectionView";
 import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { setupMoveUpEvents, emptyFunction, returnZero, returnOne } from "../../../Utils";
+import { setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils";
 import { DocumentView } from "../nodes/DocumentView";
 
 library.add(faCog, faPlus, faSortUp, faSortDown);
@@ -121,7 +121,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             {!this.previewDocument ? (null) :
                 <ContentFittingDocumentView
                     Document={this.previewDocument}
-                    DataDocument={undefined}
+                    DataDoc={undefined}
                     NativeHeight={returnZero}
                     NativeWidth={returnZero}
                     fitToBox={true}
@@ -132,16 +132,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     rootSelected={this.rootSelected}
                     PanelWidth={this.previewWidth}
                     PanelHeight={this.previewHeight}
-                    getTransform={this.getPreviewTransform}
-                    CollectionDoc={this.props.CollectionView?.props.Document}
-                    CollectionView={this.props.CollectionView}
+                    ScreenToLocalTransform={this.getPreviewTransform}
+                    ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                    ContainingCollectionView={this.props.CollectionView}
                     moveDocument={this.props.moveDocument}
                     addDocument={this.props.addDocument}
                     removeDocument={this.props.removeDocument}
-                    active={this.props.active}
+                    parentActive={this.props.active}
                     whenActiveChanged={this.props.whenActiveChanged}
                     addDocTab={this.props.addDocTab}
                     pinToPres={this.props.pinToPres}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
                 />}
         </div>;
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 556d7df5c..6c230d5b1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne } from "../../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -165,12 +165,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         const height = () => this.getDocHeight(doc);
         return <ContentFittingDocumentView
             Document={doc}
-            DataDocument={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
+            DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
+            RenderData={this.props.RenderData}
             PanelWidth={width}
             PanelHeight={height}
             NativeHeight={returnZero}
@@ -180,16 +181,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView?.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.addDocTab}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
             pinToPres={this.props.pinToPres}
         />;
     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d938bd7ad..71358a8ec 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -353,27 +353,31 @@ class TreeView extends React.Component<TreeViewProps> {
             return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.props.document[Id] + this.props.document.title}>
                 <ContentFittingDocumentView
                     Document={layoutDoc}
-                    DataDocument={this.templateDataDoc}
+                    DataDoc={this.templateDataDoc}
                     LibraryPath={emptyPath}
                     renderDepth={this.props.renderDepth + 1}
                     rootSelected={returnTrue}
                     backgroundColor={this.props.backgroundColor}
                     fitToBox={this.boundsOfCollectionDocument !== undefined}
                     FreezeDimensions={true}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : returnZero}
+                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : returnZero}
                     PanelWidth={panelWidth}
                     PanelHeight={panelHeight}
-                    getTransform={this.docTransform}
-                    CollectionDoc={this.props.containingCollection}
-                    CollectionView={undefined}
+                    focus={returnFalse}
+                    ScreenToLocalTransform={this.docTransform}
+                    ContainingCollectionDoc={this.props.containingCollection}
+                    ContainingCollectionView={undefined}
                     addDocument={returnFalse}
                     moveDocument={this.props.moveDocument}
                     removeDocument={returnFalse}
-                    active={this.props.active}
+                    parentActive={this.props.active}
                     whenActiveChanged={emptyFunction}
                     addDocTab={this.props.addDocTab}
-                    pinToPres={this.props.pinToPres} />
+                    pinToPres={this.props.pinToPres}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
+                />
             </div>;
         }
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8d8c321e8..561226de5 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -113,12 +113,16 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action.bound
     addDocument(doc: Doc): boolean {
-        const targetDataDoc = this.props.Document[DataSym];
-        const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
-        !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, doc]));  // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
-        // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+        if (this.props.addDocument) {
+            this.props.addDocument(doc);
+        } else {
+            const targetDataDoc = this.props.Document[DataSym];
+            const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
+            !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, doc]));  // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
+            // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+            targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+        }
         doc.context = this.props.Document;
-        targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
         Doc.GetProto(doc).lastOpened = new DateField;
         return true;
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 763a6c605..b4eb22444 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -877,6 +877,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             onDoubleClick: this.onChildDoubleClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
+            RenderData: this.props.RenderData,
             PanelWidth: childLayout[WidthSym],
             PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
@@ -1176,7 +1177,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
     onPointerOver = (e: React.PointerEvent) => {
         if (SelectionManager.GetIsDragging()) {
-            this.setupDragLines(e);
+            this.setupDragLines();
         }
         e.stopPropagation();
     }
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 66d441115..b3a6a9deb 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -14,7 +14,7 @@ import "./collectionMulticolumnView.scss";
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 import { List } from '../../../../new_fields/List';
-import { returnZero } from '../../../../Utils';
+import { returnZero, returnFalse, returnOne } from '../../../../Utils';
 
 type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
 const MulticolumnDocument = makeInterface(documentSchema);
@@ -216,7 +216,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <ContentFittingDocumentView
             Document={layout}
-            DataDocument={layout.resolvedDataDoc as Doc}
+            DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
@@ -231,17 +231,19 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView?.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.addDocTab}
             pinToPres={this.props.pinToPres}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
         />;
     }
     /**
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 615efdb39..0fb29ca61 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -6,7 +6,7 @@ import * as React from "react";
 import { Doc } from '../../../../new_fields/Doc';
 import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types';
 import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
-import { Utils, returnZero } from '../../../../Utils';
+import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils';
 import "./collectionMultirowView.scss";
 import { computed, trace, observable, action } from 'mobx';
 import { Transform } from '../../../util/Transform';
@@ -215,7 +215,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <ContentFittingDocumentView
             Document={layout}
-            DataDocument={layout.resolvedDataDoc as Doc}
+            DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
@@ -230,17 +230,19 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView?.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.addDocTab}
             pinToPres={this.props.pinToPres}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
         />;
     }
     /**
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 1c7d116c5..24468dcc1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -115,13 +115,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     PanelWidth={this.panelWidth}
                     PanelHeight={this.panelHeight} />
                 : <ContentFittingDocumentView {...this.props}
-                    CollectionDoc={this.props.ContainingCollectionDoc}
-                    DataDocument={this.props.DataDoc}
-                    getTransform={this.getTransform}
+                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                    DataDoc={this.props.DataDoc}
+                    ScreenToLocalTransform={this.getTransform}
                     NativeHeight={this.NativeHeight}
                     NativeWidth={this.NativeWidth}
-                    active={this.props.parentActive}
-                    focus={this.focusDoc}
                     PanelWidth={this.panelWidth}
                     PanelHeight={this.panelHeight}
                 />}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index d0b0c8ee6..637fd5acc 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -3,51 +3,16 @@ import { computed } from "mobx";
 import { observer } from "mobx-react";
 import "react-table/react-table.css";
 import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { ScriptField } from "../../../new_fields/ScriptField";
 import { NumCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
 import { emptyFunction, returnOne } from "../../../Utils";
-import { Transform } from "../../util/Transform";
-import { CollectionView } from "../collections/CollectionView";
 import '../DocumentDecorations.scss';
-import { DocumentView } from "../nodes/DocumentView";
+import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import "./ContentFittingDocumentView.scss";
-import { dropActionType } from "../../util/DragManager";
 
-interface ContentFittingDocumentViewProps {
-    Document: Doc;
-    DataDocument?: Doc;
-    LayoutDoc?: () => Opt<Doc>;
-    NativeWidth?: () => number;
-    NativeHeight?: () => number;
-    FreezeDimensions?: boolean;
-    LibraryPath: Doc[];
-    renderDepth: number;
-    fitToBox?: boolean;
-    layoutKey?: string;
-    dropAction?: dropActionType;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    focus?: (doc: Doc) => void;
-    CollectionView?: CollectionView;
-    CollectionDoc?: Doc;
-    onClick?: ScriptField;
-    onDoubleClick?: ScriptField;
-    backgroundColor?: (doc: Doc) => string | undefined;
-    getTransform: () => Transform;
-    addDocument?: (document: Doc) => boolean;
-    moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
-    removeDocument?: (document: Doc) => boolean;
-    active: (outsideReaction: boolean) => boolean;
-    whenActiveChanged: (isActive: boolean) => void;
-    addDocTab: (document: Doc, where: string) => boolean;
-    pinToPres: (document: Doc) => void;
-    dontRegisterView?: boolean;
-    rootSelected: (outsideReaction?: boolean) => boolean;
-}
 
 @observer
-export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
+export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
     private get layoutDoc() { return this.props.LayoutDoc?.() || Doc.Layout(this.props.Document); }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
@@ -68,7 +33,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     @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(); }
 
-    private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
+    private getTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
     private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
 
@@ -90,7 +55,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                     }}>
                     <DocumentView {...this.props}
                         Document={this.props.Document}
-                        DataDoc={this.props.DataDocument}
+                        DataDoc={this.props.DataDoc}
                         LayoutDoc={this.props.LayoutDoc}
                         LibraryPath={this.props.LibraryPath}
                         NativeWidth={this.nativeWidth}
@@ -107,11 +72,11 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         removeDocument={this.props.removeDocument}
                         moveDocument={this.props.moveDocument}
                         whenActiveChanged={this.props.whenActiveChanged}
-                        ContainingCollectionView={this.props.CollectionView}
-                        ContainingCollectionDoc={this.props.CollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                         addDocTab={this.props.addDocTab}
                         pinToPres={this.props.pinToPres}
-                        parentActive={this.props.active}
+                        parentActive={this.props.parentActive}
                         ScreenToLocalTransform={this.getTransform}
                         renderDepth={this.props.renderDepth}
                         focus={this.props.focus || emptyFunction}
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index d4d997120..88eeb1137 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -6,7 +6,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyPath } from "../../../Utils";
+import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
@@ -114,9 +114,10 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         }
         const contents = !(containedDoc instanceof Doc) ? (null) : <ContentFittingDocumentView
             Document={containedDoc}
-            DataDocument={undefined}
+            DataDoc={undefined}
             LibraryPath={emptyPath}
-            CollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+            ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+            ContainingCollectionDoc={undefined}
             fitToBox={true}
             layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
             rootSelected={this.props.isSelected}
@@ -125,14 +126,18 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
             removeDocument={this.props.removeDocument}
             addDocTab={this.props.addDocTab}
             pinToPres={this.props.pinToPres}
-            getTransform={this.getTransform}
+            ScreenToLocalTransform={this.getTransform}
             renderDepth={this.props.renderDepth + 1}
+            NativeHeight={returnZero}
+            NativeWidth={returnZero}
             PanelWidth={this.pwidth}
             PanelHeight={this.pheight}
             focus={this.props.focus}
-            active={this.props.active}
+            parentActive={this.props.active}
             dontRegisterView={!this.isSelectionLocked()}
             whenActiveChanged={this.props.whenActiveChanged}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
         />;
         return contents;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 085637440..f555d6eef 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -77,6 +77,7 @@ export interface DocumentViewProps {
     setupDragLines?: () => void;
     renderDepth: number;
     ContentScaling: () => number;
+    RenderData?: () => Doc;
     PanelWidth: () => number;
     PanelHeight: () => number;
     pointerEvents?: boolean;
@@ -992,6 +993,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 LayoutDoc={this.props.LayoutDoc}
                 makeLink={this.makeLink}
                 rootSelected={this.rootSelected}
+                RenderData={this.props.RenderData}
                 dontRegisterView={this.props.dontRegisterView}
                 fitToBox={this.props.fitToBox}
                 LibraryPath={this.props.LibraryPath}
@@ -1112,17 +1114,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             Doc.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
         }
     }
-    @observable _animate = 0;
+    @observable _animateScalingTo = 0;
     switchViews = action((custom: boolean, view: string) => {
-        SelectionManager.SetIsDragging(true);
-        this._animate = 0.1;
+        this._animateScalingTo = 0.1;  // shrink doc
         setTimeout(action(() => {
             this.setCustomView(custom, view);
-            this._animate = 1;
-            setTimeout(action(() => {
-                this._animate = 0;
-                SelectionManager.SetIsDragging(false);
-            }), 400);
+            this._animateScalingTo = 1; // expand it
+            setTimeout(action(() => this._animateScalingTo = 0), 400);
         }), 400);
     });
 
@@ -1156,9 +1154,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !entered && Doc.UnBrushDoc(this.props.Document);
             })}
             style={{
-                transformOrigin: this._animate ? "center center" : undefined,
-                transform: this._animate ? `scale(${this._animate})` : undefined,
-                transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
+                transformOrigin: this._animateScalingTo ? "center center" : undefined,
+                transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
+                transition: !this._animateScalingTo ? StrCast(this.Document.transition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
                 pointerEvents: this.ignorePointerEvents ? "none" : undefined,
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 0b9edbcd3..1efee4f5a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -56,6 +56,7 @@ export interface FieldViewProps {
     width?: number;
     background?: string;
     color?: string;
+    RenderData?: () => Doc;
 }
 
 @observer
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index f91a809bb..3fcc97473 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -16,6 +16,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { makeInterface } from "../../../new_fields/Schema";
+import { List } from "../../../new_fields/List";
 
 type PresBoxSchema = makeInterface<[typeof documentSchema]>;
 const PresBoxDocument = makeInterface(documentSchema);
@@ -23,20 +24,15 @@ const PresBoxDocument = makeInterface(documentSchema);
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
-    private _childReaction: IReactionDisposer | undefined;
     @observable _isChildActive = false;
     @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
     @computed get currentIndex() { return NumCast(this.rootDoc._itemIndex); }
 
     componentDidMount() {
+        this.rootDoc.presBox = this.rootDoc;
         this.rootDoc._forceRenderEngine = "timeline";
         this.rootDoc._replacedChrome = "replaced";
-        this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true });
     }
-    componentWillUnmount() {
-        this._childReaction?.();
-    }
-
     updateCurrentPresentation = () => Doc.UserDoc().activePresentation = this.rootDoc;
 
     @undoBatch
@@ -247,16 +243,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
 
     initializeViewAliases = (docList: Doc[], viewtype: CollectionViewType) => {
         const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46;
-        docList.forEach(doc => {
-            doc.presBox = this.rootDoc; // give contained documents a reference to the presentation
-            doc.presCollapsedHeight = hgt;  //  set the collpased height for documents based on the type of view (Tree or Stack) they will be displaye din
-        });
+        this.rootDoc.presCollapsedHeight = hgt;
     }
 
     addDocument = (doc: Doc) => {
-        const newPinDoc = Doc.MakeAlias(doc);
-        newPinDoc.presentationTargetDoc = doc;
-        return Doc.AddDocToList(this.dataDoc, this.fieldKey, newPinDoc);
+        doc.presentationTargetDoc = doc.aliasOf;
+        return Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
     }
 
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
@@ -280,8 +272,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.updateMinimize(e, this.rootDoc._viewType = viewType);
     });
 
+    returnSelf = () => this.rootDoc;
     childLayoutTemplate = () => this.rootDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined;
     render() {
+        this.rootDoc.presOrderedDocs = new List<Doc>(this.childDocs.map((child, i) => child));
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         this.initializeViewAliases(this.childDocs, mode);
         return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
@@ -314,6 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                         childLayoutTemplate={this.childLayoutTemplate}
                         addDocument={this.addDocument}
                         removeDocument={returnFalse}
+                        RenderData={this.returnSelf}
                         focus={this.selectElement}
                         ScreenToLocalTransform={this.getTransform} />
                     : (null)
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index f9e4c5210..9ad5aafb8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
 import { Doc, DocCastAsync } from "../../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { schema } from "./schema_rts";
@@ -192,18 +192,24 @@ export class FormattedTextBoxComment {
                                     fitToBox={true}
                                     moveDocument={returnFalse}
                                     rootSelected={returnFalse}
-                                    getTransform={Transform.Identity}
-                                    active={returnFalse}
+                                    ScreenToLocalTransform={Transform.Identity}
+                                    parentActive={returnFalse}
                                     addDocument={returnFalse}
                                     removeDocument={returnFalse}
                                     addDocTab={returnFalse}
                                     pinToPres={returnFalse}
                                     dontRegisterView={true}
+                                    ContainingCollectionDoc={undefined}
+                                    ContainingCollectionView={undefined}
                                     renderDepth={1}
                                     PanelWidth={() => Math.min(350, NumCast(target._width, 350))}
                                     PanelHeight={() => Math.min(250, NumCast(target._height, 250))}
                                     focus={emptyFunction}
                                     whenActiveChanged={returnFalse}
+                                    bringToFront={returnFalse}
+                                    ContentScaling={returnOne}
+                                    NativeWidth={returnZero}
+                                    NativeHeight={returnZero}
                                 />, FormattedTextBoxComment.tooltipText);
                                 FormattedTextBoxComment.tooltip.style.width = NumCast(target.width) ? `${NumCast(target.width)}` : "100%";
                                 FormattedTextBoxComment.tooltip.style.height = NumCast(target.height) ? `${NumCast(target.height)}` : "100%";
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index 3a16171de..7a0718c16 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -55,6 +55,9 @@
         color: black;
     }
 
+}
+
+.richTextMenu {
     select {
         background-color: #323232;
         color: white;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 66f251b93..1887c8d45 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -1,12 +1,12 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym } from "../../../new_fields/Doc";
+import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from "../../../new_fields/FieldSymbols";
 import { createSchema, makeInterface } from '../../../new_fields/Schema';
 import { Cast, NumCast } from "../../../new_fields/Types";
-import { emptyFunction, emptyPath, returnFalse, returnTrue } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
 import { ViewBoxBaseComponent } from '../DocComponent';
@@ -38,13 +38,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
 
     _heightDisposer: IReactionDisposer | undefined;
-    @computed get indexInPres() { return NumCast(this.rootDoc.presentationIndex); }
-    @computed get presBoxDoc() { return Cast(this.rootDoc.presBox, Doc) as Doc; }
+    @computed get indexInPres() { return DocListCast(this.presBoxDoc.presOrderedDocs).findIndex(d => d === this.rootDoc); }
+    @computed get presBoxDoc() { return Cast(this.props.RenderData?.().presBox, Doc) as Doc; }
     @computed get targetDoc() { return this.rootDoc.presentationTargetDoc as Doc; }
     @computed get currentIndex() { return NumCast(this.presBoxDoc?._itemIndex); }
+    @computed get collapsedHeight() { return NumCast(this.presBoxDoc?.presCollapsedHeight); }
 
     componentDidMount() {
-        this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.rootDoc.presCollapsedHeight],
+        this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
             params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
     }
     componentWillUnmount() {
@@ -147,7 +148,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
      */
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
-    embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - NumCast(this.rootDoc.presCollapsedHeight));
+    embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
     embedWidth = () => this.props.PanelWidth() - 20;
     /**
      * The function that is responsible for rendering the a preview or not for this
@@ -158,7 +159,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
             <div className="presElementBox-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <ContentFittingDocumentView
                     Document={this.targetDoc}
-                    DataDocument={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     LibraryPath={emptyPath}
                     fitToBox={true}
                     rootSelected={returnTrue}
@@ -168,12 +169,18 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     pinToPres={returnFalse}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
-                    getTransform={Transform.Identity}
-                    active={this.props.active}
+                    ScreenToLocalTransform={Transform.Identity}
+                    parentActive={this.props.active}
                     moveDocument={this.props.moveDocument!}
                     renderDepth={this.props.renderDepth + 1}
                     focus={emptyFunction}
                     whenActiveChanged={returnFalse}
+                    bringToFront={returnFalse}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    ContentScaling={returnOne}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                 />
                 <div className="presElementBox-embeddedMask" />
             </div>;
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index fe2000700..96f43e931 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -7,7 +7,7 @@ import { observer } from "mobx-react";
 import { Doc } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, SetupDrag } from "../../util/DragManager";
@@ -164,14 +164,20 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     removeDocument={returnFalse}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    getTransform={Transform.Identity}
+                    ContainingCollectionDoc={undefined}
+                    ContainingCollectionView={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
                     renderDepth={1}
                     PanelWidth={returnXDimension}
                     PanelHeight={returnYDimension}
+                    NativeWidth={returnZero}
+                    NativeHeight={returnZero}
                     focus={emptyFunction}
                     moveDocument={returnFalse}
-                    active={returnFalse}
+                    parentActive={returnFalse}
                     whenActiveChanged={returnFalse}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
                 />
             </div>;
             return docview;
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 5ca0d681e..cd4b9d591 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -9,7 +9,7 @@ export const documentSchema = createSchema({
     layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
     title: "string",            // document title (can be on either data document or layout)
     dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: "string",  // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
+    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
     childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
     _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
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index d7cc1e6bf..f37538252 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -578,7 +578,7 @@ export class CurrentUserUtils {
         }
         if (doc.activePresentation === undefined) {
             doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
-                title: "Presentation", _viewType: CollectionViewType.Stacking,
+                title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
                 _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
             });
         }
-- 
cgit v1.2.3-70-g09d2


From 40d5c3acab6dbdc67f6d4bfd15c802da9fe08ca0 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 2 May 2020 00:17:21 -0400
Subject: cleaned up a lot of layoutTemplate/String props.  fixed link drawing.

---
 src/client/documents/Documents.ts                  |  7 +-
 src/client/util/DocumentManager.ts                 |  2 +-
 src/client/views/DocComponent.tsx                  |  4 +-
 src/client/views/DocumentDecorations.scss          | 24 +++---
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/MainView.scss                     |  6 +-
 .../views/collections/CollectionCarouselView.tsx   | 11 +--
 .../views/collections/CollectionStackingView.tsx   | 29 ++++---
 src/client/views/collections/CollectionSubView.tsx |  2 +
 .../views/collections/CollectionTimeView.tsx       |  4 +-
 .../views/collections/CollectionTreeView.tsx       |  8 +-
 src/client/views/collections/CollectionView.tsx    | 14 +++-
 .../views/collections/CollectionViewChromes.tsx    |  6 +-
 .../CollectionFreeFormLinkView.tsx                 |  2 +-
 .../CollectionFreeFormLinksView.tsx                |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 10 +--
 .../CollectionMulticolumnView.tsx                  |  5 +-
 .../CollectionMultirowView.tsx                     |  5 +-
 src/client/views/nodes/AudioBox.tsx                |  4 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  4 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  5 +-
 src/client/views/nodes/DocumentBox.tsx             | 94 ++++++++++++++-------
 src/client/views/nodes/DocumentContentsView.tsx    | 44 +++++-----
 src/client/views/nodes/DocumentView.tsx            | 29 +++----
 src/client/views/nodes/FieldView.tsx               |  3 +-
 src/client/views/nodes/KeyValueBox.tsx             |  2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 -
 src/client/views/nodes/ScreenshotBox.tsx           |  6 +-
 src/client/views/nodes/VideoBox.tsx                |  6 +-
 src/new_fields/Doc.ts                              |  2 +-
 src/new_fields/documentSchemas.ts                  | 95 ++++++++++++----------
 .../authentication/models/current_user_utils.ts    |  2 +-
 32 files changed, 247 insertions(+), 195 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 434b26312..672f94f75 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -95,6 +95,7 @@ export interface DocumentOptions {
     forceActive?: boolean;
     layout?: string | Doc; // default layout string for a document
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
+    childLayoutString?: string; // template string for collection to use to render its children
     hideFilterView?: boolean; // whether to hide the filter popout on collections
     hideHeadings?: boolean; // whether stacking view column headings should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
@@ -123,7 +124,7 @@ export interface DocumentOptions {
     displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
     borderRounding?: string;
     boxShadow?: string;
-    dontRegisterChildren?: boolean;
+    dontRegisterChildViews?: boolean;
     "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form
     "onClick-rawScript"?: string; // onClick script in raw text form
     "onCheckedClick-rawScript"?: string; // onChecked script in raw text form
@@ -592,9 +593,7 @@ export namespace Docs {
             linkDocProto.anchor1_timecode = source.doc.currentTimecode || source.doc.displayTimecode;
             linkDocProto.anchor2_timecode = target.doc.currentTimecode || target.doc.displayTimecode;
 
-            if (linkDocProto.layout_key1 === undefined) {
-                Cast(linkDocProto.proto, Doc, null).layout_key1 = LinkAnchorBox.LayoutString("anchor1");
-                Cast(linkDocProto.proto, Doc, null).layout_key2 = LinkAnchorBox.LayoutString("anchor2");
+            if (linkDocProto.linkBoxExcludedKeys === undefined) {
                 Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
                 Cast(linkDocProto.proto, Doc, null).layoutKey = undefined;
             }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 4683e77a8..1ba6f0248 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -117,7 +117,7 @@ export class DocumentManager {
                 pairs.push(...linksList.reduce((pairs, link) => {
                     const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
                     linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
-                        if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
+                        if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
                             pairs.push({ a: dv, b: docView1, l: link });
                         }
                     });
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 629b0f447..fd0d2bdbb 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -11,7 +11,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 interface DocComponentProps {
     Document: Doc;
-    LayoutDoc?: () => Opt<Doc>;
+    LayoutTemplate?: () => Opt<Doc>;
 }
 export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
@@ -20,7 +20,7 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
         @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutDoc?.()); }
+        @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutTemplate?.()); }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() { return this.props.Document[DataSym] as Doc; }
 
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 61d517d43..4f34eb9e3 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -44,7 +44,6 @@ $linkGap : 3px;
 
     .documentDecorations-radius {
         pointer-events: auto;
-        background: black;
         opacity: 1;
         transform: translate(10px, 10px);
         grid-row: 4;
@@ -88,12 +87,12 @@ $linkGap : 3px;
         opacity: 1;
     }
     #documentDecorations-topLeftResizer {
-        border-left: black 2px solid;
-        border-top: black solid 2px;
+        border-left:  2px solid;
+        border-top:  solid 2px;
     }
     #documentDecorations-bottomRightResizer {
-        border-right: black 2px solid;
-        border-bottom: black solid 2px;
+        border-right:  2px solid;
+        border-bottom:  solid 2px;
     }
     #documentDecorations-topLeftResizer:hover,
     #documentDecorations-bottomRightResizer:hover {
@@ -111,12 +110,12 @@ $linkGap : 3px;
         opacity: 1;
     }
     #documentDecorations-topRightResizer {
-        border-right: black 2px solid;
-        border-top: black 2px solid;
+        border-right:  2px solid;
+        border-top:  2px solid;
     }
     #documentDecorations-bottomLeftResizer {
-        border-left: black 2px solid;
-        border-bottom: black 2px solid;
+        border-left:  2px solid;
+        border-bottom:  2px solid;
     }
     #documentDecorations-topRightResizer:hover,
     #documentDecorations-bottomLeftResizer:hover {
@@ -136,7 +135,6 @@ $linkGap : 3px;
     }
 
     .documentDecorations-contextMenu {
-        background: $alt-accent;
         width: 25px;
         height: calc(100% + 8px); // 8px for the height of the top resizer bar
         grid-column-start: 1;
@@ -144,14 +142,14 @@ $linkGap : 3px;
         pointer-events: all;
     }
     .documentDecorations-title {
-        background: $alt-accent;
         opacity: 1;
         grid-column-start: 3;
         grid-column-end: 4;
         pointer-events: auto;
         overflow: hidden;
         text-align: center;
-        display:flex;
+        display: flex;    
+        border-bottom: solid 1px;
     }
     .publishBox {
         width: 20px;
@@ -168,7 +166,6 @@ $linkGap : 3px;
 
 
 .documentDecorations-closeButton {
-    background: $alt-accent;
     opacity: 1;
     grid-column-start: 4;
     grid-column-end: 6;
@@ -178,7 +175,6 @@ $linkGap : 3px;
 }
 
 .documentDecorations-minimizeButton {
-    background: $alt-accent;
     opacity: 1;
     grid-column-start: 1;
     grid-column-end: 3;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e2759291a..396fe12b5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DataSym, Field } from "../../new_fields/Doc";
-import { PositionDocument } from '../../new_fields/documentSchemas';
+import { Document } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
 import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
@@ -301,7 +301,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = PositionDocument(element.rootDoc);
+                const doc = Document(element.rootDoc);
                 let nwidth = doc._nativeWidth || 0;
                 let nheight = doc._nativeHeight || 0;
                 const width = (doc._width || 0);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 81d427f64..04288a9e1 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -39,7 +39,12 @@
     }
 }
 
+.mainView-container {
+    color:dimgray;
+}
+
 .mainView-container-dark {
+    color: lightgray;
     .lm_goldenlayout {
         background: dimgray;
     }
@@ -54,7 +59,6 @@
     }
     .contextMenu-cont, .contextMenu-item {
         background: dimGray;
-        color: lightgray;
     }
     .contextMenu-item:hover {
         background: gray;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 4086294ad..769b323ae 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -2,9 +2,9 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { observable, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { documentSchema } from '../../../new_fields/documentSchemas';
+import { documentSchema, collectionSchema } from '../../../new_fields/documentSchemas';
 import { makeInterface } from '../../../new_fields/Schema';
-import { NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
+import { NumCast, StrCast, ScriptCast, Cast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import "./CollectionCarouselView.scss";
@@ -16,8 +16,8 @@ import { ContextMenu } from '../ContextMenu';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import { returnFalse } from '../../../Utils';
 
-type CarouselDocument = makeInterface<[typeof documentSchema,]>;
-const CarouselDocument = makeInterface(documentSchema);
+type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const CarouselDocument = makeInterface(documentSchema, collectionSchema);
 
 @observer
 export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
@@ -40,7 +40,6 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
         e.stopPropagation();
         this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
     }
-
     panelHeight = () => this.props.PanelHeight() - 50;
     @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
@@ -51,6 +50,8 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                         onDoubleClick={ScriptCast(this.layoutDoc.onChildDoubleClick)}
                         onClick={ScriptCast(this.layoutDoc.onChildClick)}
                         renderDepth={this.props.renderDepth + 1}
+                        LayoutTemplate={this.props.ChildLayoutTemplate}
+                        LayoutTemplateString={this.props.ChildLayoutString}
                         Document={this.childLayoutPairs[index].layout}
                         DataDoc={this.childLayoutPairs[index].data}
                         PanelHeight={this.panelHeight}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 01766f65f..eb70cec9d 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,15 +4,17 @@ import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Switch from 'rc-switch';
-import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc";
+import { DataSym, Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
+import { listSpec, makeInterface } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
+import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
@@ -24,11 +26,14 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
+import { ScriptField } from "../../../new_fields/ScriptField";
 const _global = (window /* browser */ || global /* node */) as any;
 
+type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
+const StackingDocument = makeInterface(collectionSchema, documentSchema);
+
 @observer
-export class CollectionStackingView extends CollectionSubView(doc => doc) {
+export class CollectionStackingView extends CollectionSubView(StackingDocument) {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     _pivotFieldDisposer?: IReactionDisposer;
@@ -116,7 +121,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     getSimpleDocHeight(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
@@ -160,14 +165,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         }
         return this.props.addDocTab(doc, where);
     }
+
     getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
-        const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(doc, this.props.ChildLayoutTemplate?.());
         const height = () => this.getDocHeight(doc);
         return <ContentFittingDocumentView
             Document={doc}
             DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
             backgroundColor={this.props.backgroundColor}
-            LayoutDoc={this.props.childLayoutTemplate}
+            LayoutTemplate={this.props.ChildLayoutTemplate}
+            LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
@@ -176,7 +183,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             PanelHeight={height}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
+            fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
@@ -199,13 +206,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     getDocWidth(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e44bbae78..aaea13ded 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -43,6 +43,8 @@ export interface CollectionViewProps extends FieldViewProps {
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
     children?: never | (() => JSX.Element[]) | React.ReactNode;
+    ChildLayoutTemplate?: () => Doc;
+    ChildLayoutString?: string;
     childClickScript?: ScriptField;
     childDoubleClickScript?: ScriptField;
     freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 045134225..a2d4774c8 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -28,7 +28,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _childClickedScript: Opt<ScriptField>;
     @observable _viewDefDivClick: Opt<ScriptField>;
     async componentDidMount() {
-        const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), "");
         const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
@@ -84,7 +84,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}>
-            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={true} layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 296c1a39c..dc9348664 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,7 +6,6 @@ import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
-import { RichTextField } from '../../../new_fields/RichTextField';
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
@@ -15,7 +14,6 @@ import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
-import { makeTemplate } from '../../util/DropConverter';
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
@@ -481,7 +479,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-                        dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)}
+                        dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildViews)}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={this.props.containingCollection}
                     />}
@@ -743,11 +741,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                             Doc.GetProto(img).doubleClickView = doubleClickView;
                         }
                     });
-                    d.layout = ImageBox.LayoutString("hero");
+                    Doc.GetProto(d).layout = ImageBox.LayoutString("hero");
                 });
 
                 Document.childLayoutTemplate = heroView;
-                Document.childDetailView = detailView;
+                Document.childClickedOpenTemplateView = detailView;
                 Document._viewType = CollectionViewType.Time;
                 Document._forceActive = true;
                 Document._pivotField = "company";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 91018980c..d2afb4855 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -72,7 +72,8 @@ export enum CollectionViewType {
     Pile = "pileup"
 }
 export interface CollectionViewCustomProps {
-    filterAddDocument: (doc: Doc) => boolean;
+    filterAddDocument: (doc: Doc) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    childLayoutTemplate?: () => Opt<Doc>;  // specify a layout Doc template to use for children of the collection
 }
 
 export interface CollectionRenderProps {
@@ -82,6 +83,8 @@ export interface CollectionRenderProps {
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     PanelWidth: () => number;
+    ChildLayoutTemplate?: () => Doc;
+    ChildLayoutString?: string;
 }
 
 @observer
@@ -245,8 +248,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             if (this.props.Document.childLayout instanceof Doc) {
                 layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
             }
-            if (this.props.Document.childDetailView instanceof Doc) {
-                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailView as Doc, "onRight"), icon: "project-diagram" });
+            if (this.props.Document.childClickedOpenTemplateView instanceof Doc) {
+                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" });
             }
             layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
@@ -474,6 +477,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 </div>
             </div>;
     }
+    childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
 
     render() {
         TraceMobx();
@@ -483,7 +487,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             moveDocument: this.moveDocument,
             active: this.active,
             whenActiveChanged: this.whenActiveChanged,
-            PanelWidth: this.bodyPanelWidth
+            PanelWidth: this.bodyPanelWidth,
+            ChildLayoutTemplate: this.childLayoutTemplate,
+            ChildLayoutString: StrCast(this.props.Document.childLayoutString),
         };
         return (<div className={"collectionView"}
             style={{
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index d26e3a38b..62b03bbdc 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -44,9 +44,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         initialize: emptyFunction,
     };
     _narrativeCommand = {
-        params: ["target", "source"], title: "=> click item view",
-        script: "this.target.childDetailView = getDocTemplate(this.source?.[0])",
-        immediate: (source: Doc[]) => this.target.childDetailView = Doc.getDocTemplate(source?.[0]),
+        params: ["target", "source"], title: "=> click clicked open view",
+        script: "this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])",
+        immediate: (source: Doc[]) => this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]),
         initialize: emptyFunction,
     };
     _contentCommand = {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index cf12ef382..d67d1993e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -40,7 +40,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 const bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
                     abounds.left, abounds.top, abounds.width, abounds.height,
                     apt.point.x, apt.point.y);
-                const afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
+                const afield = this.props.A.props.LayoutTemplateString?.indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
                 const bfield = afield === "anchor1" ? "anchor2" : "anchor1";
 
                 // really hacky stuff to make the LinkAnchorBox display where we want it to:
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 4b5e977df..7df1e909f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -30,7 +30,7 @@ export class CollectionFreeFormLinksView extends React.Component {
             return drawnPairs;
         }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
         return connections.filter(c =>
-            c.a.props.layoutKey && c.b.props.layoutKey && c.a.props.Document.type === DocumentType.LINK &&
+            c.a.props.Document.type === DocumentType.LINK &&
             c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed
         ).map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 45ef0455e..707e103fb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,7 +5,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
-import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
+import { documentSchema, collectionSchema } from "../../../../new_fields/documentSchemas";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
@@ -66,8 +66,8 @@ export const panZoomSchema = createSchema({
     fitH: "number"
 });
 
-type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>;
-const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>;
+const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema);
 export type collectionFreeformViewProps = {
     forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
     viewDefDivClick?: ScriptField;
@@ -858,7 +858,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     backgroundHalo = () => BoolCast(this.Document.useClusters);
     parentActive = () => this.props.active() || this.backgroundActive ? true : false;
-    childLayoutFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
             ...this.props,
@@ -868,7 +867,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             DataDoc: childData,
             Document: childLayout,
             LibraryPath: this.libraryPath,
-            LayoutDoc: this.childLayoutFunc,
+            LayoutTemplate: this.props.ChildLayoutTemplate,
+            LayoutTemplateString: this.props.ChildLayoutString,
             FreezeDimensions: this.props.freezeChildDimensions,
             layoutKey: undefined,
             setupDragLines: this.setupDragLines,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b3a6a9deb..38b16e184 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -218,7 +218,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
-            LayoutDoc={this.props.childLayoutTemplate}
+            LayoutTemplate={this.props.ChildLayoutTemplate}
+            LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
@@ -226,7 +227,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             PanelHeight={height}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
+            fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 0fb29ca61..b55b67a9e 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -217,7 +217,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
-            LayoutDoc={this.props.childLayoutTemplate}
+            LayoutTemplate={this.props.ChildLayoutTemplate}
+            LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
@@ -225,7 +226,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             PanelHeight={height}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
+            fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 6ff6d1b42..3feb533a0 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -21,6 +21,7 @@ import { Docs } from "../../documents/Documents";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Networking } from "../../Network";
 import { Upload } from "../../../server/SharedMediaTypes";
+import { LinkAnchorBox } from "./LinkAnchorBox";
 
 // testing testing 
 
@@ -266,7 +267,8 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
                                                 NativeHeight={returnZero}
                                                 NativeWidth={returnZero}
                                                 rootSelected={returnFalse}
-                                                layoutKey={Doc.LinkEndpoint(l, la2)}
+                                                LayoutTemplate={undefined}
+                                                LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
                                                 ContainingCollectionDoc={this.props.Document}
                                                 parentActive={returnTrue}
                                                 bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 24468dcc1..2894d9b65 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -7,7 +7,7 @@ import { DocComponent } from "../DocComponent";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
-import { PositionDocument } from "../../../new_fields/documentSchemas";
+import { Document } from "../../../new_fields/documentSchemas";
 import { TraceMobx } from "../../../new_fields/util";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 
@@ -28,7 +28,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
 
 @observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) {
     @observable _animPos: number[] | undefined = undefined;
     random(min: number, max: number) { // min should not be equal to max
         const mseed = Math.abs(this.X * this.Y);
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 637fd5acc..3c2c6c87e 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -14,7 +14,7 @@ import "./ContentFittingDocumentView.scss";
 @observer
 export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
-    private get layoutDoc() { return this.props.LayoutDoc?.() || Doc.Layout(this.props.Document); }
+    private get layoutDoc() { return this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document); }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()));
     nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()));
@@ -56,7 +56,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                     <DocumentView {...this.props}
                         Document={this.props.Document}
                         DataDoc={this.props.DataDoc}
-                        LayoutDoc={this.props.LayoutDoc}
+                        LayoutTemplate={this.props.LayoutTemplate}
+                        LayoutTemplateString={this.props.LayoutTemplateString}
                         LibraryPath={this.props.LibraryPath}
                         NativeWidth={this.nativeWidth}
                         NativeHeight={this.nativeHeight}
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 88eeb1137..8d422fe67 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { IReactionDisposer, reaction, computed } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Field } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema, collectionSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
@@ -16,9 +16,11 @@ import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
 import { TraceMobx } from "../../../new_fields/util";
 import { Docs } from "../../documents/Documents";
+import { KeyValueBox } from "./KeyValueBox";
+import { DocumentView } from "./DocumentView";
 
-type DocHolderBoxSchema = makeInterface<[typeof documentSchema]>;
-const DocHolderBoxDocument = makeInterface(documentSchema);
+type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
 
 @observer
 export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
@@ -43,7 +45,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
         funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" });
         funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" });
-        funcs.push({ description: `Show ${this.layoutDoc.childTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childTemplateName = this.layoutDoc.childTemplateName ? undefined : "keyValue", icon: "project-diagram" });
+        funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "<KeyValueBox {...props} />", icon: "project-diagram" });
 
         ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
@@ -103,6 +105,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
     pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
     pheight = () => this.props.PanelHeight() - 2 * this.yPad;
     getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
+    isActive = () => this.active() || !this.props.renderDepth;
     get renderContents() {
         const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
         const childTemplateName = StrCast(this.layoutDoc.childTemplateName);
@@ -112,33 +115,62 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined);
             }, 0);
         }
-        const contents = !(containedDoc instanceof Doc) ? (null) : <ContentFittingDocumentView
-            Document={containedDoc}
-            DataDoc={undefined}
-            LibraryPath={emptyPath}
-            ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
-            ContainingCollectionDoc={undefined}
-            fitToBox={true}
-            layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
-            rootSelected={this.props.isSelected}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            addDocTab={this.props.addDocTab}
-            pinToPres={this.props.pinToPres}
-            ScreenToLocalTransform={this.getTransform}
-            renderDepth={this.props.renderDepth + 1}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
-            PanelWidth={this.pwidth}
-            PanelHeight={this.pheight}
-            focus={this.props.focus}
-            parentActive={this.props.active}
-            dontRegisterView={!this.isSelectionLocked()}
-            whenActiveChanged={this.props.whenActiveChanged}
-            bringToFront={returnFalse}
-            ContentScaling={returnOne}
-        />;
+        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString ?
+            <DocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={StrCast(this.layoutDoc.childLayoutString)}
+                //layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={!this.isSelectionLocked()}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne} /> :
+            <ContentFittingDocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={StrCast(this.layoutDoc.childLayoutString)}
+                //layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={!this.isSelectionLocked()}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />;
         return contents;
     }
     render() {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 4d20d3e2c..749fb98be 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -96,8 +96,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     isSelected: (outsideReaction: boolean) => boolean,
     select: (ctrl: boolean) => void,
     layoutKey: string,
-    forceLayout?: string,
-    forceFieldKey?: string,
     hideOnLeave?: boolean,
     makeLink?: () => Opt<Doc>,  // function to call when a link is made
 }> {
@@ -105,6 +103,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         TraceMobx();
         if (!this.layoutDoc) return "<p>awaiting layout</p>";
         // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string");  // bcz: replaced this with below... is it right?
+        if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
         const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
         if (this.props.layoutKey === "layout_keyValue") {
             return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
@@ -127,8 +126,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     get layoutDoc() {
         const params = StrCast(this.props.Document.PARAMS);
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
-        // const template: Doc = this.props.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        const template: Doc = this.props.LayoutDoc?.() ||
+        // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+        const template: Doc = this.props.LayoutTemplate?.() || (this.props.LayoutTemplateString && this.props.Document) ||
             (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
             Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
         return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
@@ -186,25 +185,22 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         //  layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
 
         return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
-            this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
-                <FormattedTextBox {...bindings.props} fieldKey={this.props.forceFieldKey} />
-                :
-                <ObserverJsxParser
-                    key={42}
-                    blacklistedAttrs={[]}
-                    renderInWrapper={false}
-                    components={{
-                        FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
-                        CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                        PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
-                        ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
-                        RecommendationsBox, ScreenshotBox, HTMLtag
-                    }}
-                    bindings={bindings}
-                    jsx={layoutFrame}
-                    showWarnings={true}
-
-                    onError={(test: any) => { console.log(test); }}
-                />;
+            <ObserverJsxParser
+                key={42}
+                blacklistedAttrs={[]}
+                renderInWrapper={false}
+                components={{
+                    FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
+                    CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
+                    PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
+                    ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
+                    RecommendationsBox, ScreenshotBox, HTMLtag
+                }}
+                bindings={bindings}
+                jsx={layoutFrame}
+                showWarnings={true}
+
+                onError={(test: any) => { console.log(test); }}
+            />;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f1efa48f4..7c7c03db2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,22 +5,21 @@ import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
-import { Document, PositionDocument } from '../../../new_fields/documentSchemas';
+import { Document } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { InkTool } from '../../../new_fields/InkField';
-import { RichTextField } from '../../../new_fields/RichTextField';
 import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
 import { ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField';
+import { ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { ClientRecommender } from '../../ClientRecommender';
 import { DocServer } from "../../DocServer";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { ClientUtils } from '../../util/ClientUtils';
 import { DocumentManager } from "../../util/DocumentManager";
@@ -42,6 +41,7 @@ import { InkingControl } from '../InkingControl';
 import { KeyphraseQueryView } from '../KeyphraseQueryView';
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
+import { LinkAnchorBox } from './LinkAnchorBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
 
@@ -58,7 +58,8 @@ export interface DocumentViewProps {
     NativeHeight: () => number;
     Document: Doc;
     DataDoc?: Doc;
-    LayoutDoc?: () => Opt<Doc>;
+    LayoutTemplateString?: string;
+    LayoutTemplate?: () => Opt<Doc>;
     LibraryPath: Doc[];
     fitToBox?: boolean;
     contextMenuItems?: () => { script: ScriptField, label: string }[];
@@ -454,8 +455,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             const dY = -1 * Math.sign(dH);
 
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = PositionDocument(this.props.Document);
-                const layoutDoc = PositionDocument(Doc.Layout(this.props.Document));
+                const doc = Document(this.props.Document);
+                const layoutDoc = Document(Doc.Layout(this.props.Document));
                 let nwidth = layoutDoc._nativeWidth || 0;
                 let nheight = layoutDoc._nativeHeight || 0;
                 const width = (layoutDoc._width || 0);
@@ -984,13 +985,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get contents() {
         TraceMobx();
         return (<>
-            <DocumentContentsView key={1} ContainingCollectionView={this.props.ContainingCollectionView}
+            <DocumentContentsView key={1}
+                ContainingCollectionView={this.props.ContainingCollectionView}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                 NativeWidth={this.NativeWidth}
                 NativeHeight={this.NativeHeight}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
-                LayoutDoc={this.props.LayoutDoc}
+                LayoutTemplateString={this.props.LayoutTemplateString}
+                LayoutTemplate={this.props.LayoutTemplate}
                 makeLink={this.makeLink}
                 rootSelected={this.rootSelected}
                 RenderData={this.props.RenderData}
@@ -1021,7 +1024,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         </>
         );
     }
-    linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document);
 
     // used to decide whether a link anchor view should be created or not.
     // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here.
@@ -1049,12 +1051,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
                 PanelWidth={this.anchorPanelWidth}
                 PanelHeight={this.anchorPanelHeight}
-                layoutKey={this.linkEndpoint(d)}
                 ContentScaling={returnOne}
                 backgroundColor={returnTransparent}
                 removeDocument={this.hideLinkAnchor}
                 pointerEvents={false}
-                LayoutDoc={undefined}
+                LayoutTemplate={undefined}
+                LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)}
             />);
     }
     @computed get innards() {
@@ -1073,8 +1075,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
                 <DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
                     hideOnLeave={true}
-                    forceLayout={"FormattedTextBox"}
-                    forceFieldKey={showCaption}
+                    layoutTemplateString={`<FormattedTextBox {...props} fieldKey={'${showCaption}'}/>`}
                     ContentScaling={this.childScaling}
                     ChromeHeight={this.chromeHeight}
                     isSelected={this.isSelected}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 1efee4f5a..40d55ce38 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,13 +50,12 @@ export interface FieldViewProps {
     setVideoBox?: (player: VideoBox) => void;
     ContentScaling: () => number;
     ChromeHeight?: () => number;
-    childLayoutTemplate?: () => Opt<Doc>;
+    RenderData?: () => Doc;
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     height?: number;
     width?: number;
     background?: string;
     color?: string;
-    RenderData?: () => Doc;
 }
 
 @observer
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 2970674a2..d43936949 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -36,7 +36,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
-    get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
+    get fieldDocToLayout() { return this.props.Document; }
 
     @action
     onEnterKey = (e: React.KeyboardEvent): void => {
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 6c50abf21..bc36e056e 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -17,7 +17,6 @@ import { LinkEditor } from "../linking/LinkEditor";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { SelectionManager } from "../../util/SelectionManager";
 import { TraceMobx } from "../../../new_fields/util";
-import { DocumentView } from "./DocumentView";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 125690dc7..a0ecc9ff5 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
-import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
@@ -20,8 +20,8 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./ScreenshotBox.scss";
 const path = require('path');
 
-type ScreenshotDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
-const ScreenshotDocument = makeInterface(documentSchema, positionSchema);
+type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
+const ScreenshotDocument = makeInterface(documentSchema);
 
 library.add(faVideo);
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 613929bca..266b7f43f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -21,14 +21,14 @@ import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
-import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../new_fields/documentSchemas";
 const path = require('path');
 
 export const timeSchema = createSchema({
     currentTimecode: "number",  // the current time of a video or other linear, time-based document.  Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
 });
-type VideoDocument = makeInterface<[typeof documentSchema, typeof positionSchema, typeof timeSchema]>;
-const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema);
+type VideoDocument = makeInterface<[typeof documentSchema, typeof timeSchema]>;
+const VideoDocument = makeInterface(documentSchema, timeSchema);
 
 library.add(faVideo);
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 71325d94f..9256f82c2 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -772,7 +772,7 @@ export namespace Doc {
 
 
     export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; }
-    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layout_key1" : "layout_key2"; }
+    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; }
 
     export function linkFollowUnhighlight() {
         Doc.UnhighlightAll();
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index fd9a304f9..e71fc27f3 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -4,13 +4,25 @@ import { Doc } from "./Doc";
 import { DateField } from "./DateField";
 
 export const documentSchema = createSchema({
+    // content properties
     type: "string",             // enumerated type of document -- should be template-specific (ie, start with an '_')
-    layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
-    layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
     title: "string",            // document title (can be on either data document or layout)
-    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
-    childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
+    isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field
+    creationDate: DateField,    // when the document was created
+    links: listSpec(Doc),       // computed (readonly) list of links associated with this document
+
+    // "Location" properties in a very general sense
+    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
+    x: "number",                // x coordinate when in a freeform view 
+    y: "number",                // y coordinate when in a freeform view 
+    z: "number",                // z "coordinate" - non-zero specifies the overlay layer of a freeformview
+    zIndex: "number",           // zIndex of a document in a freeform view
+    scrollY: "number",          // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
+    scrollTop: "number",        // scroll position of a scrollable document (pdf, text, web)
+
+    // appearance properties on the layout
     _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",    // "
@@ -27,59 +39,57 @@ export const documentSchema = createSchema({
     _showAudio: "boolean",      // whether to show the audio record icon on documents
     _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
     _LODdisable: "boolean",     // whether to disbale LOD switching for CollectionFreeFormViews
-    _pivotField: "string",      // specifies which field should be used as the timeline/pivot axis
+    _pivotField: "string",      // specifies which field key should be used as the timeline/pivot axis
     _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
     _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
-    _freezeChildDimensions: "boolean", // freezes child document dimensions (e.g., used by time/pivot view to make sure all children will be scaled to fit their display rectangle)
     _fontSize: "number",
     _fontFamily: "string",
-    isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
-    color: "string",            // foreground color of document
+
+    // appearance properties on the data document
     backgroundColor: "string",  // background color of document
+    borderRounding: "string",   // border radius rounding of document
+    boxShadow: "string",        // the amount of shadow around the perimeter of a document
+    color: "string",            // foreground color of document
+    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
+    fontSize: "string",
+    layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
+    layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
+    letterSpacing: "string",
     opacity: "number",          // opacity of document
-    creationDate: DateField,    // when the document was created
-    links: listSpec(Doc),       // computed (readonly) list of links associated with this document
+    strokeWidth: "number",
+    textTransform: "string",
+    treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
+    treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
+    treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+
+    // interaction and linking properties
+    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
     onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
     onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
     onPointerUp: ScriptField,   // script to run when document is clicked (can be overriden by an onClick prop)
     onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
-    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
-    isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field
-    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
-    treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
-    treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
-    treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
-    currentTimecode: "number",  // current play back time of a temporal document (video / audio)
     followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) 
+    isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
+    isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
+    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
     lockedPosition: "boolean",  // whether the document can be moved (dragged)
     lockedTransform: "boolean", // whether the document can be panned/zoomed
-    inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
-    borderRounding: "string",   // border radius rounding of document
-    heading: "number",          // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
-    isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
-    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
-    scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
-    scrollY: "number",          // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
-    scrollTop: "number",        // scroll position of a scrollable document (pdf, text, web)
-    strokeWidth: "number",
-    fontSize: "string",
-    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
-    letterSpacing: "string",
-    textTransform: "string",
-    childTemplateName: "string" // the name of a template to use to override the layoutKey when rendering a document in DocHolderBox 
-});
 
-export const positionSchema = createSchema({
-    zIndex: "number",
-    x: "number",
-    y: "number",
-    z: "number",
+    // drag drop properties
+    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
+    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
+    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
+    childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
+    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
 });
 
+
 export const collectionSchema = createSchema({
-    childLayoutTemplate: Doc, // layout template for children of a collecion
-    childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field)
+    childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox 
+    childLayoutTemplate: Doc, // layout template to use to render children of a collecion
+    childLayoutString: "string", //layout string to use to render children of a collection
+    childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template)
+    dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
     onChildClick: ScriptField, // script to run for each child when its clicked
     onChildDoubleClick: ScriptField, // script to run for each child when its clicked
     onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view
@@ -87,6 +97,3 @@ export const collectionSchema = createSchema({
 
 export type Document = makeInterface<[typeof documentSchema]>;
 export const Document = makeInterface(documentSchema);
-
-export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
-export const PositionDocument = makeInterface(documentSchema, positionSchema);
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 2ae42bf52..b9de93559 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -485,7 +485,7 @@ export class CurrentUserUtils {
                 _width: 50, _height: 25, title: "Library", _fontSize: 10,
                 letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
                 sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], {
-                    title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
+                    title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true
                 })) as any as Doc,
                 targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
                 onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
-- 
cgit v1.2.3-70-g09d2


From a698a3cea10fed25127f5dd933b23fb7de5d469b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 5 May 2020 01:36:00 -0400
Subject: fixes for parameterized templates. added a pendingMap to fix issues
 with expandTemplateLayout. added a switchbox template to test templates.  
 changed dimUnits/dimMagnitude to _dimUnits/_dimMagnitude

---
 src/Utils.ts                                       |  8 ++++++--
 src/client/documents/Documents.ts                  |  9 ++++-----
 .../CollectionMulticolumnView.tsx                  | 22 +++++++++++-----------
 .../CollectionMultirowView.tsx                     | 22 +++++++++++-----------
 .../collectionMulticolumn/MulticolumnResizer.tsx   | 14 +++++++-------
 .../collectionMulticolumn/MultirowResizer.tsx      | 14 +++++++-------
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 .../views/nodes/formattedText/DashFieldView.tsx    | 14 +++++++++-----
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/nodes/formattedText/nodes_rts.ts  |  3 ++-
 src/new_fields/Doc.ts                              |  9 +++++++--
 .../authentication/models/current_user_utils.ts    | 19 ++++++++++++++++---
 12 files changed, 82 insertions(+), 56 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 23b59ac9d..732f74bfc 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -313,14 +313,18 @@ export namespace Utils {
     }
 }
 
-export function OmitKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void): { omit: any, extract: any } {
+export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any, extract: any } {
     const omit: any = { ...obj };
     const extract: any = {};
     keys.forEach(key => {
         extract[key] = omit[key];
         delete omit[key];
     });
-    addKeyFunc && addKeyFunc(omit);
+    pattern && Array.from(Object.keys(omit)).filter(key => key.match(pattern)).forEach(key => {
+        extract[key] = omit[key];
+        delete omit[key];
+    })
+    addKeyFunc?.(omit);
     return { omit, extract };
 }
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f5d6cd7f6..a87a77e1d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -58,6 +58,8 @@ export interface DocumentOptions {
     _height?: number;
     _nativeWidth?: number;
     _nativeHeight?: number;
+    _dimMagnitude?: number; // magnitude of collectionMulti{row,col} view element
+    _dimUnit?: string; // "px" or "*" (default = "*")
     _fitWidth?: boolean;
     _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
     _LODdisable?: boolean;
@@ -455,10 +457,7 @@ export namespace Docs {
 
         Scripting.addGlobal(Buxton);
 
-        const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "childDropAction", "_annotationOn",
-            "_chromeStatus", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover", "_backgroundColor",
-            "_xMargin", "_yMargin", "_xPadding", "_yPadding", "_singleLine", "_scrollTop",
-            "_color", "isLinkButton", "isBackground", "removeDropProperties", "treeViewOpen"];
+        const delegateKeys = ["x", "y", "layoutKey", "dropAction", "childDropAction", "isLinkButton", "isBackground", "removeDropProperties", "treeViewOpen"];
 
         /**
          * This function receives the relevant document prototype and uses
@@ -479,7 +478,7 @@ export namespace Docs {
          * main document.
          */
         export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data") {
-            const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys);
+            const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys, "^_");
 
             if (!("author" in protoProps)) {
                 protoProps.author = Doc.CurrentUserEmail;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 38b16e184..a09124304 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -46,12 +46,12 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout.dimUnit, "*") === DimUnit.Ratio);
+        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
     }
 
     /**
-     * This loops through all childLayoutPairs and extracts the values for dimUnit
-     * and dimMagnitude, ignoring any that are malformed. Additionally, it then
+     * This loops through all childLayoutPairs and extracts the values for _dimUnit
+     * and _dimMagnitude, ignoring any that are malformed. Additionally, it then
      * normalizes the ratio values so that one * value is always 1, with the remaining
      * values proportionate to that easily readable metric.
      * @returns the list of the resolved width specifiers (unit and magnitude pairs)
@@ -62,8 +62,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         let starSum = 0;
         const widthSpecifiers: WidthSpecifier[] = [];
         this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout.dimUnit, "*");
-            const magnitude = NumCast(pair.layout.dimMagnitude, 1);
+            const unit = StrCast(pair.layout._dimUnit, "*");
+            const magnitude = NumCast(pair.layout._dimMagnitude, 1);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
                 (unit === DimUnit.Ratio) && (starSum += magnitude);
                 widthSpecifiers.push({ magnitude, unit });
@@ -83,9 +83,9 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         setTimeout(() => {
             const { ratioDefinedDocs } = this;
             if (this.childLayoutPairs.length) {
-                const minimum = Math.min(...ratioDefinedDocs.map(doc => NumCast(doc.dimMagnitude, 1)));
+                const minimum = Math.min(...ratioDefinedDocs.map(doc => NumCast(doc._dimMagnitude, 1)));
                 if (minimum !== 0) {
-                    ratioDefinedDocs.forEach(layout => layout.dimMagnitude = NumCast(layout.dimMagnitude, 1) / minimum, 1);
+                    ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1);
                 }
             }
         });
@@ -161,8 +161,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         if (columnUnitLength === undefined) {
             return 0; // we're still waiting on promises to resolve
         }
-        let width = NumCast(layout.dimMagnitude, 1);
-        if (StrCast(layout.dimUnit, "*") === DimUnit.Ratio) {
+        let width = NumCast(layout._dimMagnitude, 1);
+        if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
             width *= columnUnitLength;
         }
         return width;
@@ -194,8 +194,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (super.onInternalDrop(e, de)) {
             de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                d.dimUnit = "*";
-                d.dimMagnitude = 1;
+                d._dimUnit = "*";
+                d._dimMagnitude = 1;
             }));
         }
         return false;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index b55b67a9e..4326723b1 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -46,12 +46,12 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout.dimUnit, "*") === DimUnit.Ratio);
+        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
     }
 
     /**
-     * This loops through all childLayoutPairs and extracts the values for dimUnit
-     * and dimUnit, ignoring any that are malformed. Additionally, it then
+     * This loops through all childLayoutPairs and extracts the values for _dimUnit
+     * and _dimUnit, ignoring any that are malformed. Additionally, it then
      * normalizes the ratio values so that one * value is always 1, with the remaining
      * values proportionate to that easily readable metric.
      * @returns the list of the resolved width specifiers (unit and magnitude pairs)
@@ -62,8 +62,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
         let starSum = 0;
         const heightSpecifiers: HeightSpecifier[] = [];
         this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout.dimUnit, "*");
-            const magnitude = NumCast(pair.layout.dimMagnitude, 1);
+            const unit = StrCast(pair.layout._dimUnit, "*");
+            const magnitude = NumCast(pair.layout._dimMagnitude, 1);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
                 (unit === DimUnit.Ratio) && (starSum += magnitude);
                 heightSpecifiers.push({ magnitude, unit });
@@ -83,9 +83,9 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
         setTimeout(() => {
             const { ratioDefinedDocs } = this;
             if (this.childLayoutPairs.length) {
-                const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout.dimMagnitude, 1)));
+                const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout._dimMagnitude, 1)));
                 if (minimum !== 0) {
-                    ratioDefinedDocs.forEach(layout => layout.dimMagnitude = NumCast(layout.dimMagnitude, 1) / minimum);
+                    ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum);
                 }
             }
         });
@@ -161,8 +161,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
         if (rowUnitLength === undefined) {
             return 0; // we're still waiting on promises to resolve
         }
-        let height = NumCast(layout.dimMagnitude, 1);
-        if (StrCast(layout.dimUnit, "*") === DimUnit.Ratio) {
+        let height = NumCast(layout._dimMagnitude, 1);
+        if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
             height *= rowUnitLength;
         }
         return height;
@@ -194,8 +194,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (super.onInternalDrop(e, de)) {
             de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                d.dimUnit = "*";
-                d.dimMagnitude = 1;
+                d._dimUnit = "*";
+                d._dimMagnitude = 1;
             }));
         }
         return false;
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index e1e604686..a24eb1631 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -43,12 +43,12 @@ export default class ResizeBar extends React.Component<ResizerProps> {
         const unitLength = columnUnitLength();
         if (unitLength) {
             if (toNarrow) {
-                const scale = StrCast(toNarrow.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
-                toNarrow.dimMagnitude = Math.max(0.05, NumCast(toNarrow.dimMagnitude, 1) - Math.abs(movementX) / scale);
+                const scale = StrCast(toNarrow._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+                toNarrow._dimMagnitude = Math.max(0.05, NumCast(toNarrow._dimMagnitude, 1) - Math.abs(movementX) / scale);
             }
             if (toWiden) {
-                const scale = StrCast(toWiden.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
-                toWiden.dimMagnitude = Math.max(0.05, NumCast(toWiden.dimMagnitude, 1) + Math.abs(movementX) / scale);
+                const scale = StrCast(toWiden._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+                toWiden._dimMagnitude = Math.max(0.05, NumCast(toWiden._dimMagnitude, 1) + Math.abs(movementX) / scale);
             }
         }
     }
@@ -56,17 +56,17 @@ export default class ResizeBar extends React.Component<ResizerProps> {
     private get isActivated() {
         const { toLeft, toRight } = this.props;
         if (toLeft && toRight) {
-            if (StrCast(toLeft.dimUnit, "*") === DimUnit.Pixel && StrCast(toRight.dimUnit, "*") === DimUnit.Pixel) {
+            if (StrCast(toLeft._dimUnit, "*") === DimUnit.Pixel && StrCast(toRight._dimUnit, "*") === DimUnit.Pixel) {
                 return false;
             }
             return true;
         } else if (toLeft) {
-            if (StrCast(toLeft.dimUnit, "*") === DimUnit.Pixel) {
+            if (StrCast(toLeft._dimUnit, "*") === DimUnit.Pixel) {
                 return false;
             }
             return true;
         } else if (toRight) {
-            if (StrCast(toRight.dimUnit, "*") === DimUnit.Pixel) {
+            if (StrCast(toRight._dimUnit, "*") === DimUnit.Pixel) {
                 return false;
             }
             return true;
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 9df8cc3e2..5f00b18b9 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -41,12 +41,12 @@ export default class ResizeBar extends React.Component<ResizerProps> {
         const unitLength = columnUnitLength();
         if (unitLength) {
             if (toNarrow) {
-                const scale = StrCast(toNarrow.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
-                toNarrow.dimMagnitude = Math.max(0.05, NumCast(toNarrow.dimMagnitude, 1) - Math.abs(movementY) / scale);
+                const scale = StrCast(toNarrow._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+                toNarrow._dimMagnitude = Math.max(0.05, NumCast(toNarrow._dimMagnitude, 1) - Math.abs(movementY) / scale);
             }
             if (toWiden) {
-                const scale = StrCast(toWiden.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
-                toWiden.dimMagnitude = Math.max(0.05, NumCast(toWiden.dimMagnitude, 1) + Math.abs(movementY) / scale);
+                const scale = StrCast(toWiden._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+                toWiden._dimMagnitude = Math.max(0.05, NumCast(toWiden._dimMagnitude, 1) + Math.abs(movementY) / scale);
             }
         }
     }
@@ -54,17 +54,17 @@ export default class ResizeBar extends React.Component<ResizerProps> {
     private get isActivated() {
         const { toTop, toBottom } = this.props;
         if (toTop && toBottom) {
-            if (StrCast(toTop.dimUnit, "*") === DimUnit.Pixel && StrCast(toBottom.dimUnit, "*") === DimUnit.Pixel) {
+            if (StrCast(toTop._dimUnit, "*") === DimUnit.Pixel && StrCast(toBottom._dimUnit, "*") === DimUnit.Pixel) {
                 return false;
             }
             return true;
         } else if (toTop) {
-            if (StrCast(toTop.dimUnit, "*") === DimUnit.Pixel) {
+            if (StrCast(toTop._dimUnit, "*") === DimUnit.Pixel) {
                 return false;
             }
             return true;
         } else if (toBottom) {
-            if (StrCast(toBottom.dimUnit, "*") === DimUnit.Pixel) {
+            if (StrCast(toBottom._dimUnit, "*") === DimUnit.Pixel) {
                 return false;
             }
             return true;
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 395a6e0b2..2fc82e524 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -135,7 +135,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
         const list = {
-            ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
+            ...OmitKeys(this.props, ['parentActive'], "", (obj: any) => obj.active = this.props.parentActive).omit,
             RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index d87d6e424..f28057fd7 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -34,6 +34,7 @@ export class DashFieldView {
             docid={node.attrs.docid}
             width={node.attrs.width}
             height={node.attrs.height}
+            hideKey={node.attrs.hideKey}
             tbox={tbox}
         />, this._fieldWrapper);
         (this as any).dom = this._fieldWrapper;
@@ -47,6 +48,7 @@ export class DashFieldView {
 interface IDashFieldViewInternal {
     fieldKey: string;
     docid: string;
+    hideKey: boolean;
     tbox: FormattedTextBox;
     width: number;
     height: number;
@@ -80,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         if (this._dashDoc) {
-            const dashVal = this._dashDoc[this._fieldKey];
+            const dashVal = this._dashDoc![this._fieldKey] || (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
             const fval = StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
             const boolVal = Cast(fval, "boolean", null);
             const strVal = Field.toString(fval as Field) || "";
@@ -151,7 +153,8 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                 } else if (nodeText.startsWith("=:=")) {
                     Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
                 } else {
-                    this._dashDoc![this._fieldKey] = newText;
+                    if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS)
+                        this._dashDoc![this._fieldKey] = newText;
                 }
             });
         }
@@ -192,9 +195,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             width: this.props.width,
             height: this.props.height,
         }}>
-            <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
-                {this._fieldKey}
-            </span>
+            {this.props.hideKey ? (null) :
+                <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
+                    {this._fieldKey}
+                </span>}
 
             <div className="dashFieldView-fieldSpan">
                 {this.fieldValueContent}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 658a55f51..f5c28b350 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -205,7 +205,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
                 this._applyingChange = true;
                 this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
-                if ((!curTemp && !curProto) || curText) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
+                if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                     if (curText !== curLayout?.Text) {
                         this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
                         this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index e7bcf444a..af39ef9c7 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -166,7 +166,8 @@ export const nodes: { [index: string]: NodeSpec } = {
         inline: true,
         attrs: {
             fieldKey: { default: "" },
-            docid: { default: "" }
+            docid: { default: "" },
+            hideKey: { default: false }
         },
         group: "inline",
         draggable: false,
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 9f75b5ffe..b41007e9c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -467,6 +467,7 @@ export namespace Doc {
         return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc;
     }
 
+    const _pendingMap: Map<string, boolean> = new Map();
     //
     // Returns an expanded template layout for a target data document if there is a template relationship
     // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties 
@@ -492,13 +493,16 @@ export namespace Doc {
         const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
         const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]";
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
+
         if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
             expandedTemplateLayout = undefined;
+            _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
         }
-        else if (expandedTemplateLayout === undefined) {
-            if (templateLayoutDoc.resolvedDataDoc === Doc.GetProto(targetDoc) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
+        else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
+            if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
                 expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
             } else {
+                _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true);
                 templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
                 setTimeout(action(() => {
                     if (!targetDoc[expandedLayoutFieldKey]) {
@@ -513,6 +517,7 @@ export namespace Doc {
                         if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) {
                             dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
                         }
+                        _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args);
                     }
                 }), 0);
             }
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index b10c6f119..d3e085b21 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -22,6 +22,7 @@ import { MainView } from "../../../client/views/MainView";
 import { DocumentType } from "../../../client/documents/DocumentTypes";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { faBoxOpen } from "@fortawesome/free-solid-svg-icons";
+import { CollectionMulticolumnView, DimUnit } from "../../../client/views/collections/collectionMulticolumn/CollectionMulticolumnView";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -87,16 +88,28 @@ export class CurrentUserUtils {
         }
 
         if (doc["template-button-switch"] === undefined) {
-            const { FreeformDocument, MulticolumnDocument } = Docs.Create;
+            const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
 
-            const yes = FreeformDocument([], { title: "yes", _height: 100, _width: 100, _LODdisable: true });
+            const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _LODdisable: true, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 });
+            const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1 });
             const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, _LODdisable: true });
+            const labelTemplate = {
+                doc: {
+                    type: "doc", content: [{
+                        type: "paragraph",
+                        content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }]
+                    }]
+                },
+                selection: { type: "text", anchor: 1, head: 1 },
+                storedMarks: []
+            };
+            Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS");
             Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'");
             // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'");
             // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true");
             Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]");
             // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false");
-            const box = MulticolumnDocument([/*no, */ yes], { title: "value", _width: 40, _height: 40, });
+            const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, });
             box.isTemplateDoc = makeTemplate(box, true, "switch");
 
             doc["template-button-switch"] = CurrentUserUtils.ficon({
-- 
cgit v1.2.3-70-g09d2

