From 70e79356ce89f4afb47220393c541f74eada62fc Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 11 Nov 2020 02:09:18 -0500
Subject: turned off selection highlighting in TreeView because it is way too
 inefficient.  more fixes to laying out fitWidth and other documents in
 ContentFittingDocViews. testing against GridViews and TreeViews things seem
 to work.

---
 .../views/collections/collectionGrid/CollectionGridView.tsx      | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index d31427829..57f845961 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,12 +1,12 @@
 import { action, computed, Lambda, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
-import { Doc, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, Opt } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
 import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
+import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
@@ -169,11 +169,14 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             ContainingCollectionDoc={this.props.Document}
             PanelWidth={width}
             PanelHeight={height}
+            ContentScaling={returnOne}
+            fitToBox={false}
+            FreezeDimensions={true}
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
             parentActive={this.props.active}
-            dontCenter={true}
+            dontCenter={false}
         />;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 65e4c1dd781876374734f4520a9f51b6aae0cf79 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 11 Nov 2020 13:45:20 -0500
Subject: fixes to centering of documents in ContentFittingDocumentView.  
 fixed display of document title in tree view when it was alrady rendered in
 the tree view.

---
 src/client/views/PropertiesView.tsx                |   2 +-
 .../views/collections/CollectionSchemaView.tsx     |   1 +
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 src/client/views/collections/TreeView.tsx          | 141 ++++++++++-----------
 .../collectionGrid/CollectionGridView.tsx          |   3 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  28 ++--
 6 files changed, 86 insertions(+), 91 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index fad2f5284..17cfb5a5a 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -273,7 +273,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     backgroundColor={this.props.backgroundColor}
                     fitToBox={true}
                     FreezeDimensions={true}
-                    dontCenter={true}
+                    dontCenter={"y"}
                     NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
                     NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
                     PanelWidth={panelWidth}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9f8468253..a1df9d214 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -402,6 +402,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     DataDoc={undefined}
                     fitToBox={true}
                     FreezeDimensions={true}
+                    dontCenter={"y"}
                     focus={emptyFunction}
                     LibraryPath={this.props.LibraryPath}
                     renderDepth={this.props.renderDepth}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 97eacaeab..cf9ced849 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -202,7 +202,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             PanelHeight={height}
             NativeWidth={this.props.childIgnoreNativeSize ? returnZero : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
             NativeHeight={this.props.childIgnoreNativeSize ? returnZero : undefined}
-            dontCenter={this.props.childIgnoreNativeSize ? true : false}
+            dontCenter={this.props.childIgnoreNativeSize ? "xy" : ""}
             fitToBox={false}
             dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.dontRegisterChildViews, this.props.dontRegisterView)}
             rootSelected={this.rootSelected}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 10b9cfe66..ef46be175 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -78,7 +78,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _openScript: (() => ScriptField) | undefined;
     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
     private _treedropDisposer?: DragManager.DragDropDisposer;
-    private _dref = React.createRef<HTMLDivElement>();
+    private _dref = React.createRef<ContentFittingDocumentView>();
     private _tref = React.createRef<HTMLDivElement>();
     private _docRef = React.createRef<DocumentView>();
     private _uniqueId = Utils.GenerateGuid();
@@ -112,7 +112,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
     @computed get isCollectionDoc() {
-        return !StrCast(this.props.document.type).includes(DocumentType.COL) || !DocListCast(this.props.document[this.fieldKey]).length ? false : true
+        return !StrCast(this.props.document.type).includes(DocumentType.COL) || !DocListCast(this.props.document[this.fieldKey]).length ? false : true;
     }
 
     @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
@@ -272,14 +272,15 @@ export class TreeView extends React.Component<TreeViewProps> {
         return false;
     }
 
-    refTransform = (ref: HTMLDivElement) => {
+    refTransform = (ref: HTMLDivElement | undefined | null) => {
+        if (!ref) return this.props.ScreenToLocalTransform();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
         const outerXf = this.props.outerXf();
         const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
         return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
     }
-    docTransform = () => this.refTransform(this._dref.current!);
-    getTransform = () => this.refTransform(this._tref.current!);
+    docTransform = () => this.refTransform(this._dref.current?.ContentRef?.current);
+    getTransform = () => this.refTransform(this._tref.current);
     docWidth = () => {
         const layoutDoc = this.layoutDoc;
         const aspect = Doc.NativeAspect(layoutDoc);
@@ -394,47 +395,45 @@ export class TreeView extends React.Component<TreeViewProps> {
                         [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
-            return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
+            return <ul key={this.doc[Id] + this.doc.title}><div style={{ display: "inline-block" }} >
                 {this.expandedField}
             </div></ul>;
         } else {
             const layoutDoc = this.layoutDoc;
             const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.expandPanelHeight;
             const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.expandPanelWidth;
-            return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.doc[Id]}>
-                <ContentFittingDocumentView
-                    Document={this.doc}
-                    DataDoc={undefined}
-                    LibraryPath={emptyPath}
-                    dontRegisterView={this.props.dontRegisterView}
-                    renderDepth={this.props.renderDepth + 1}
-                    rootSelected={returnTrue}
-                    treeViewDoc={undefined}
-                    backgroundColor={this.props.backgroundColor}
-                    fitToBox={this.isCollectionDoc !== undefined}
-                    FreezeDimensions={true}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
-                    PanelWidth={panelWidth}
-                    PanelHeight={panelHeight}
-                    focus={returnFalse}
-                    ScreenToLocalTransform={this.docTransform}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionDoc={this.props.containingCollection}
-                    ContainingCollectionView={undefined}
-                    addDocument={returnFalse}
-                    moveDocument={this.move}
-                    removeDocument={this.props.removeDoc}
-                    parentActive={this.props.active}
-                    whenActiveChanged={this.props.whenActiveChanged}
-                    addDocTab={this.props.addDocTab}
-                    pinToPres={this.props.pinToPres}
-                    bringToFront={returnFalse}
-                    ContentScaling={returnOne}
-                />
-            </div>;
+            return <ContentFittingDocumentView key={this.doc[Id]} ref={this._dref}
+                Document={this.doc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                dontRegisterView={this.props.dontRegisterView}
+                renderDepth={this.props.renderDepth + 1}
+                rootSelected={returnTrue}
+                treeViewDoc={undefined}
+                backgroundColor={this.props.backgroundColor}
+                fitToBox={this.isCollectionDoc !== undefined}
+                FreezeDimensions={true}
+                NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+                NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+                PanelWidth={panelWidth}
+                PanelHeight={panelHeight}
+                focus={returnFalse}
+                ScreenToLocalTransform={this.docTransform}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.props.containingCollection}
+                ContainingCollectionView={undefined}
+                addDocument={returnFalse}
+                moveDocument={this.move}
+                removeDocument={this.props.removeDoc}
+                parentActive={this.props.active}
+                whenActiveChanged={this.props.whenActiveChanged}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />;
         }
     }
 
@@ -557,7 +556,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     render() {
         TraceMobx();
-        if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null;
+        if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return "<" + this.doc.title + ">";
         const sorting = this.doc[`${this.fieldKey}-sortAscending`];
         if (this.showTitleEditorControl) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
             let par: any = this._header?.current;
@@ -589,37 +588,35 @@ export class TreeView extends React.Component<TreeViewProps> {
                         onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
                         onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
                         {this.renderBullet}
-                        <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
-                            <ContentFittingDocumentView
-                                Document={this.doc}
-                                DataDoc={undefined}
-                                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
-                                LibraryPath={emptyPath}
-                                renderDepth={this.props.renderDepth + 1}
-                                rootSelected={returnTrue}
-                                treeViewDoc={undefined}
-                                backgroundColor={this.props.backgroundColor}
-                                fitToBox={this.isCollectionDoc !== undefined}
-                                PanelWidth={this.rtfWidth}
-                                PanelHeight={this.rtfOutlineHeight}
-                                focus={this.refocus}
-                                ScreenToLocalTransform={this.docTransform}
-                                docFilters={returnEmptyFilter}
-                                docRangeFilters={returnEmptyFilter}
-                                searchFilterDocs={returnEmptyDoclist}
-                                ContainingCollectionDoc={this.props.containingCollection}
-                                ContainingCollectionView={undefined}
-                                addDocument={this.props.addDocument}
-                                moveDocument={this.move}
-                                removeDocument={this.props.removeDoc}
-                                parentActive={this.props.active}
-                                whenActiveChanged={this.props.whenActiveChanged}
-                                addDocTab={this.props.addDocTab}
-                                pinToPres={this.props.pinToPres}
-                                bringToFront={returnFalse}
-                                ContentScaling={returnOne}
-                            />
-                        </div>
+                        <ContentFittingDocumentView key={this.doc[Id]} ref={this._dref}
+                            Document={this.doc}
+                            DataDoc={undefined}
+                            LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+                            LibraryPath={emptyPath}
+                            renderDepth={this.props.renderDepth + 1}
+                            rootSelected={returnTrue}
+                            treeViewDoc={undefined}
+                            backgroundColor={this.props.backgroundColor}
+                            fitToBox={this.isCollectionDoc !== undefined}
+                            PanelWidth={this.rtfWidth}
+                            PanelHeight={this.rtfOutlineHeight}
+                            focus={this.refocus}
+                            ScreenToLocalTransform={this.docTransform}
+                            docFilters={returnEmptyFilter}
+                            docRangeFilters={returnEmptyFilter}
+                            searchFilterDocs={returnEmptyDoclist}
+                            ContainingCollectionDoc={this.props.containingCollection}
+                            ContainingCollectionView={undefined}
+                            addDocument={this.props.addDocument}
+                            moveDocument={this.move}
+                            removeDocument={this.props.removeDoc}
+                            parentActive={this.props.active}
+                            whenActiveChanged={this.props.whenActiveChanged}
+                            addDocTab={this.props.addDocTab}
+                            pinToPres={this.props.pinToPres}
+                            bringToFront={returnFalse}
+                            ContentScaling={returnOne}
+                        />
                     </div>
 
                     <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 57f845961..5c7373a2f 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -170,13 +170,12 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             PanelWidth={width}
             PanelHeight={height}
             ContentScaling={returnOne}
-            fitToBox={false}
             FreezeDimensions={true}
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
             parentActive={this.props.active}
-            dontCenter={false}
+            dontCenter={"y"}
         />;
     }
 
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 7b698c8bd..99b5f7478 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -9,7 +9,7 @@ import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import "./ContentFittingDocumentView.scss";
 
 interface ContentFittingDocumentViewProps {
-    dontCenter?: boolean;
+    dontCenter?: string; // "x" ,"y", "xy"
 }
 
 @observer
@@ -21,8 +21,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
             Doc.Layout(this.props.Document);
     }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
-    @computed get trueNativeWidth() { return !this.layoutDoc._fitWidth && returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, false)); }
-    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 0); }
+    @computed get nativeWidth() { return !this.layoutDoc._fitWidth && returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions)); }
     @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 0); }
     @computed get nativeScaling() {
         if (!this.nativeWidth || !this.nativeHeight) return 1;
@@ -34,7 +33,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
         return wscale || 1;
     }
 
-    @computed get panelWidth() { return this.trueNativeWidth ? this.nativeWidth * this.nativeScaling : this.props.PanelWidth(); }
+    @computed get panelWidth() { return this.nativeWidth ? this.nativeWidth * this.nativeScaling : this.props.PanelWidth(); }
     @computed get panelHeight() {
         if (this.nativeHeight) {
             if (this.props.Document._fitWidth) return Math.min(this.props.PanelHeight(), this.panelWidth / Doc.NativeAspect(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 1);
@@ -44,10 +43,10 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     }
 
     @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); }  // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply.
-    private getTransform = () => this.props.dontCenter ?
-        this.props.ScreenToLocalTransform().scale(this.childXf) :
-        this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(this.childXf)
-    private get centeringOffset() { return this.trueNativeWidth && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth * this.nativeScaling) / 2 : 0; }
+    private getTransform = () => this.props.ScreenToLocalTransform().
+        translate(this.props.dontCenter?.includes("x") ? 0 : -this.centeringOffset, this.props.dontCenter?.includes("y") ? 0 : -this.centeringYOffset).
+        scale(this.childXf)
+    private get centeringOffset() { return this.nativeWidth && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth * this.nativeScaling) / 2 : 0; }
     private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 && this.nativeHeight ? (this.props.PanelHeight() - this.nativeHeight * this.nativeScaling) / 2 : 0; }
 
     @computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); }
@@ -57,20 +56,21 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     contentScaling = () => {
         const layoutStr = (this.props.LayoutTemplateString || StrCast(this.layoutDoc.layout));
         if (layoutStr.includes("FormattedTextBox")) return this.nativeScaling;
-        const wscale = this.trueNativeWidth ? 1 : this.layoutDoc[WidthSym]() / this.props.PanelWidth();
-        const hscale = this.trueNativeWidth ? 1 : this.layoutDoc[HeightSym]() / this.props.PanelHeight();
+        const wscale = this.nativeWidth ? 1 : this.layoutDoc[WidthSym]() / this.props.PanelWidth();
+        const hscale = this.nativeWidth ? 1 : this.layoutDoc[HeightSym]() / this.props.PanelHeight();
         return this.nativeScaling * Math.max(wscale, hscale);
     }
 
+    public ContentRef = React.createRef<HTMLDivElement>();
     render() {
         TraceMobx();
         return (<div className="contentFittingDocumentView">
             {!this.props.Document || !this.props.PanelWidth ? (null) : (
-                <div className="contentFittingDocumentView-previewDoc"
+                <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
                     style={{
-                        transform: !this.props.dontCenter ? `translate(${this.centeringOffset}px, ${this.centeringYOffset}px)` : undefined,
+                        transform: `translate(${this.props.dontCenter?.includes("x") ? 0 : this.centeringOffset}px, ${this.props.dontCenter?.includes("y") ? 0 : this.centeringYOffset}px)`,
                         borderRadius: this.borderRounding,
-                        height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
+                        height: Math.abs(this.centeringYOffset) > 0.001 && this.nativeWidth ? `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
                         width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth(),
                     }}>
                     <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
@@ -79,8 +79,6 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                         LayoutTemplate={this.props.LayoutTemplate}
                         LayoutTemplateString={this.props.LayoutTemplateString}
                         LibraryPath={this.props.LibraryPath}
-                        // NativeWidth={this.nativeWidth}
-                        // NativeHeight={this.nativeHeight}
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
                         ContentScaling={this.contentScaling}
-- 
cgit v1.2.3-70-g09d2


From 6286ec5286caa483e1ffed9bf2bae942f353e8f4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 13 Nov 2020 13:50:21 -0500
Subject: fixed treeView to be able to access active() on contained document in
 order to keep slide sidebar active when sidebar documents are selected. 
 fixed ContetntFittingDocumentView to show fitToBox correctly and more simplhy

---
 src/client/views/collections/TreeView.scss         |   2 +-
 src/client/views/collections/TreeView.tsx          | 197 ++++++++++++---------
 .../collectionGrid/CollectionGridView.tsx          |   1 +
 .../views/nodes/ContentFittingDocumentView.tsx     |  21 +--
 src/client/views/nodes/DocumentView.scss           |   6 +
 src/client/views/nodes/DocumentView.tsx            |  10 +-
 src/client/views/nodes/FieldView.tsx               |   1 +
 .../nodes/formattedText/FormattedTextBox.scss      |   3 +
 .../views/nodes/formattedText/FormattedTextBox.tsx |  12 +-
 9 files changed, 144 insertions(+), 109 deletions(-)

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

diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 17c6b0750..580fec9d6 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -22,7 +22,7 @@
 }
 .treeView-container-active {
     z-index: 100;
-    position: relative;;
+    position: relative;
     .formattedTextbox-sidebar {
         background-color: #ffff001f !important;
         height: 500px !important;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ef46be175..8746e7e0a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -78,12 +78,12 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _openScript: (() => ScriptField) | undefined;
     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
     private _treedropDisposer?: DragManager.DragDropDisposer;
-    private _dref = React.createRef<ContentFittingDocumentView>();
     private _tref = React.createRef<HTMLDivElement>();
     private _docRef = React.createRef<DocumentView>();
     private _uniqueId = Utils.GenerateGuid();
     private _editMaxWidth: number | string = 0;
 
+    @observable _dref: ContentFittingDocumentView | undefined | null;
     @computed get doc() { TraceMobx(); return this.props.document; }
     get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
     get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
@@ -187,7 +187,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const bullet = Docs.Create.TextDocument("-text-", {
             layout: CollectionView.LayoutString("data"),
             title: "-title-", "sidebarColor": "transparent", "sidebarViewType": CollectionViewType.Freeform,
-            forceActive: true, _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true,
+            _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true,
             x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
@@ -279,7 +279,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
         return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
     }
-    docTransform = () => this.refTransform(this._dref.current?.ContentRef?.current);
+    docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
     getTransform = () => this.refTransform(this._tref.current);
     docWidth = () => {
         const layoutDoc = this.layoutDoc;
@@ -402,7 +402,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const layoutDoc = this.layoutDoc;
             const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.expandPanelHeight;
             const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.expandPanelWidth;
-            return <ContentFittingDocumentView key={this.doc[Id]} ref={this._dref}
+            return <ContentFittingDocumentView key={this.doc[Id]} ref={action((r: ContentFittingDocumentView | null) => this._dref = r)}
                 Document={this.doc}
                 DataDoc={undefined}
                 LibraryPath={emptyPath}
@@ -457,7 +457,8 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get renderBullet() {
         TraceMobx();
         const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
-        return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
+        return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} key={"bullet"}
+            title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
             onClick={this.bulletClick}
             style={this.outlineMode ? { opacity: NumCast(this.doc.opacity, 1) } : {
                 color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
@@ -498,6 +499,27 @@ export class TreeView extends React.Component<TreeViewProps> {
     truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+
+    refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+    ignoreEvent = (e: any) => {
+        if (this.props.active(true)) {
+            e.stopPropagation();
+            e.preventDefault();
+        }
+    }
+    onKeyDown = (e: React.KeyboardEvent) => {
+        if (this.doc.treeViewHideHeader || this.outlineMode) {
+            e.stopPropagation();
+            e.preventDefault();
+            switch (e.key) {
+                case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+                    return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
+                case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
+                case "Enter": return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+            }
+        }
+    }
+
     /**
      * Renders the EditableView title element for placement into the tree.
      */
@@ -505,7 +527,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     get renderTitle() {
         TraceMobx();
         const view = this.showTitleEditorControl ? this.editableView("title") :
-            <DocumentView
+            <DocumentView key="title"
                 ref={this._docRef}
                 Document={this.doc}
                 DataDoc={undefined}
@@ -552,12 +574,71 @@ export class TreeView extends React.Component<TreeViewProps> {
         </>;
     }
 
-    refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+    renderBulletHeader = (contents: JSX.Element) => {
+        return <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader"
+            ref={this._header}
+            style={{ maxWidth: this._editMaxWidth }}
+            onClick={this.ignoreEvent}
+            onPointerDown={this.ignoreEvent}
+            onPointerEnter={this.onPointerEnter}
+            onPointerLeave={this.onPointerLeave}>
+            {contents}
+        </div>;
+    }
+
+    @computed get renderTitleAsText() {
+        return <>
+            {this.renderBullet}
+            {this.renderTitle}
+        </>;
+    }
+
+    @computed get renderDocumentInHeader() {
+        return <>
+            {this.renderBullet}
+            <ContentFittingDocumentView key={this.doc[Id]} ref={action((r: ContentFittingDocumentView | null) => this._dref = r)}
+                Document={this.doc}
+                DataDoc={undefined}
+                LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+                LibraryPath={emptyPath}
+                renderDepth={this.props.renderDepth + 1}
+                rootSelected={returnTrue}
+                treeViewDoc={undefined}
+                backgroundColor={this.props.backgroundColor}
+                fitToBox={this.isCollectionDoc !== undefined}
+                PanelWidth={this.rtfWidth}
+                PanelHeight={this.rtfOutlineHeight}
+                focus={this.refocus}
+                ScreenToLocalTransform={this.docTransform}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.props.containingCollection}
+                ContainingCollectionView={undefined}
+                addDocument={this.props.addDocument}
+                moveDocument={this.move}
+                removeDocument={this.props.removeDoc}
+                parentActive={this.props.active}
+                whenActiveChanged={this.props.whenActiveChanged}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />
+        </>;
+    }
+
+    @computed get renderBorder() {
+        const sorting = this.doc[`${this.fieldKey}-sortAscending`];
+        return <div className={`treeView-border${this.outlineMode ? "outline" : ""}`}
+            style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+            {!this.treeViewOpen ? (null) : this.renderContent}
+        </div>;
+    }
 
     render() {
         TraceMobx();
         if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return "<" + this.doc.title + ">";
-        const sorting = this.doc[`${this.fieldKey}-sortAscending`];
         if (this.showTitleEditorControl) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
             let par: any = this._header?.current;
             while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
@@ -568,88 +649,28 @@ export class TreeView extends React.Component<TreeViewProps> {
             }
         }
         else this._editMaxWidth = "";
-        const selected = false;// SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc)); // bcz: need to fix so that this doesn't get called for every selection/view creation.  this is used to highlight bullet items in Slide views
-        return this.doc.treeViewHideHeader || this.outlineMode ?
-            !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ?
-                this.renderContent
-                : <div className={`treeView-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
-                    onKeyDown={e => {
-                        e.stopPropagation();
-                        e.preventDefault();
-                        switch (e.key) {
-                            case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
-                            case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
-                            case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
-                                return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
-                        }
-                    }} >
-                    <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ alignItems: this.outlineMode ? "center" : undefined, maxWidth: this._editMaxWidth }}
-                        onClick={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
-                        onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }}
-                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        {this.renderBullet}
-                        <ContentFittingDocumentView key={this.doc[Id]} ref={this._dref}
-                            Document={this.doc}
-                            DataDoc={undefined}
-                            LayoutTemplateString={FormattedTextBox.LayoutString("text")}
-                            LibraryPath={emptyPath}
-                            renderDepth={this.props.renderDepth + 1}
-                            rootSelected={returnTrue}
-                            treeViewDoc={undefined}
-                            backgroundColor={this.props.backgroundColor}
-                            fitToBox={this.isCollectionDoc !== undefined}
-                            PanelWidth={this.rtfWidth}
-                            PanelHeight={this.rtfOutlineHeight}
-                            focus={this.refocus}
-                            ScreenToLocalTransform={this.docTransform}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionDoc={this.props.containingCollection}
-                            ContainingCollectionView={undefined}
-                            addDocument={this.props.addDocument}
-                            moveDocument={this.move}
-                            removeDocument={this.props.removeDoc}
-                            parentActive={this.props.active}
-                            whenActiveChanged={this.props.whenActiveChanged}
-                            addDocTab={this.props.addDocTab}
-                            pinToPres={this.props.pinToPres}
-                            bringToFront={returnFalse}
-                            ContentScaling={returnOne}
-                        />
-                    </div>
-
-                    <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
-                        {!this.treeViewOpen ? (null) : this.renderContent}
-                    </div>
-                </div> :
-            <div className="treeView-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
-                <li className="collection-child">
-                    <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
-                        if (this.props.active(true)) {
-                            e.stopPropagation();
-                            e.preventDefault();
-                            SelectionManager.DeselectAll();
-                        }
-                    }}
-                        onPointerDown={e => {
-                            if (this.props.active(true)) {
-                                e.stopPropagation();
-                                e.preventDefault();
-                            }
-                        }}
-                        onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                        {this.renderBullet}
-                        {this.renderTitle}
-                    </div>
-                    <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
-                        {!this.treeViewOpen ? (null) : this.renderContent}
-                    </div>
-                </li>
+
+        const hideTitle = this.doc.treeViewHideHeader || this.outlineMode;
+        return hideTitle && !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ?
+            this.renderContent
+            :
+            <div className={`treeView-container${this._dref?.docView?.contentsActive() ? "-active" : ""}`}
+                ref={this.createTreeDropTarget}
+                onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+                onKeyDown={this.onKeyDown}>
+                {hideTitle ?
+                    <li className="collection-child">
+                        {this.renderBulletHeader(this.renderDocumentInHeader)}
+                        {this.renderBorder}
+                    </li> :
+                    <li className="collection-child">
+                        {this.renderBulletHeader(this.renderTitleAsText)}
+                        {this.renderBorder}
+                    </li>
+                }
             </div>;
     }
 
-
     public static GetChildElements(
         childDocs: Doc[],
         treeView: CollectionTreeView,
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 5c7373a2f..8e86b6d0d 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -171,6 +171,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             PanelHeight={height}
             ContentScaling={returnOne}
             FreezeDimensions={true}
+            fitToBox={true}
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 99b5f7478..d5b91f4a7 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -1,10 +1,10 @@
 import React = require("react");
-import { computed } from "mobx";
+import { computed, observable, action } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
 import { Cast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnVal } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnVal, returnOne } from "../../../Utils";
 import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import "./ContentFittingDocumentView.scss";
 
@@ -15,6 +15,8 @@ interface ContentFittingDocumentViewProps {
 @observer
 export class ContentFittingDocumentView extends React.Component<DocumentViewProps & ContentFittingDocumentViewProps> {
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
+    public ContentRef = React.createRef<HTMLDivElement>();
+    @observable public docView: DocumentView | undefined | null;
     @computed get layoutDoc() {
         return this.props.LayoutTemplate?.() ||
             (this.props.layoutKey && Doc.Layout(this.props.Document, Cast(this.props.Document[this.props.layoutKey], Doc, null))) ||
@@ -42,10 +44,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
         return this.props.PanelHeight();
     }
 
-    @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); }  // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply.
     private getTransform = () => this.props.ScreenToLocalTransform().
-        translate(this.props.dontCenter?.includes("x") ? 0 : -this.centeringOffset, this.props.dontCenter?.includes("y") ? 0 : -this.centeringYOffset).
-        scale(this.childXf)
+        translate(this.props.dontCenter?.includes("x") ? 0 : -this.centeringOffset, this.props.dontCenter?.includes("y") ? 0 : -this.centeringYOffset);
     private get centeringOffset() { return this.nativeWidth && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth * this.nativeScaling) / 2 : 0; }
     private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 && this.nativeHeight ? (this.props.PanelHeight() - this.nativeHeight * this.nativeScaling) / 2 : 0; }
 
@@ -53,15 +53,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
 
     PanelWidth = () => this.panelWidth;
     PanelHeight = () => this.panelHeight;
-    contentScaling = () => {
-        const layoutStr = (this.props.LayoutTemplateString || StrCast(this.layoutDoc.layout));
-        if (layoutStr.includes("FormattedTextBox")) return this.nativeScaling;
-        const wscale = this.nativeWidth ? 1 : this.layoutDoc[WidthSym]() / this.props.PanelWidth();
-        const hscale = this.nativeWidth ? 1 : this.layoutDoc[HeightSym]() / this.props.PanelHeight();
-        return this.nativeScaling * Math.max(wscale, hscale);
-    }
 
-    public ContentRef = React.createRef<HTMLDivElement>();
     render() {
         TraceMobx();
         return (<div className="contentFittingDocumentView">
@@ -74,6 +66,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                         width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth(),
                     }}>
                     <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                        ref={action((r: DocumentView | null) => this.docView = r)}
                         Document={this.props.Document}
                         DataDoc={this.props.DataDoc}
                         LayoutTemplate={this.props.LayoutTemplate}
@@ -81,7 +74,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                         LibraryPath={this.props.LibraryPath}
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
-                        ContentScaling={this.contentScaling}
+                        ContentScaling={returnOne}
                         fitToBox={this.props.fitToBox}
                         layoutKey={this.props.layoutKey}
                         dropAction={this.props.dropAction}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index c31172e22..ad72250b6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -54,6 +54,12 @@
         }
     }
 
+    .documentView-contentsView {
+        border-radius: inherit;
+        width: 100%;
+        height: 100%;
+    }
+
     .documentView-anchorCont {
         position: absolute;
         top: 0; 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 77f63b457..4485c744d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,7 +12,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, OmitKeys, returnOne, returnTransparent, returnVal, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, returnVal, Utils, returnFalse } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -49,6 +49,7 @@ export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     docFilters: () => string[];
+    contentsActive?: (setActive: () => boolean) => void;
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
     FreezeDimensions?: boolean;
@@ -118,7 +119,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-    private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
+    private get active() { return this.isSelected(true) || this.props.parentActive(true); }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
     public get LayoutFieldKey() { return this.props.layoutKey || Doc.LayoutFieldKey(this.layoutDoc); }
@@ -918,11 +919,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
     @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, -20]; }
+    @observable contentsActive: () => boolean = returnFalse;
+    @action setContentsActive = (setActive: () => boolean) => { this.contentsActive = setActive; }
     @computed get contents() {
         TraceMobx();
-        return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}>
+        return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any }}>
             <DocumentContentsView key={1}
                 docFilters={this.props.docFilters}
+                contentsActive={this.setContentsActive}
                 docRangeFilters={this.props.docRangeFilters}
                 searchFilterDocs={this.props.searchFilterDocs}
                 ContainingCollectionView={this.props.ContainingCollectionView}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 79947c023..d7ff051cf 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -26,6 +26,7 @@ export interface FieldViewProps {
     Document: Doc;
     DataDoc?: Doc;
     LibraryPath: Doc[];
+    contentsActive?: (setActive: () => boolean) => void;
     onClick?: () => ScriptField;
     dropAction: dropActionType;
     backgroundHalo?: () => boolean;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index b75cc230f..b04f60500 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -113,6 +113,9 @@
     .ProseMirror {
         padding:10px;
     }
+}
+.formattedTextBox-outer-selected {
+
     .ProseMirror:hover {
         background: unset;
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fe38939c5..dec81236d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -810,7 +810,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
         }
     }
+
+    IsActive = () => {
+        return this.active();//this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
+    }
+
     componentDidMount() {
+        this.props.contentsActive?.(this.IsActive);
         this._cachedLinks = DocListCast(this.Document.links);
         this._disposers.sidebarheight = reaction(
             () => ({ annoHeight: NumCast(this.rootDoc[this.annotationKey + "-height"]), textHeight: NumCast(this.rootDoc[this.fieldKey + "-height"]) }),
@@ -1622,7 +1628,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
         const selPad = Math.min(margins, 10);
         const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
-        const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
+        const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
         return (
             <div className="formattedTextBox-cont" ref={this._boxRef}
                 style={{
@@ -1665,10 +1671,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     })}
                     onDoubleClick={this.onDoubleClick}
                 >
-                    <div className={`formattedTextBox-outer${selclass}`} ref={this._scrollRef}
+                    <div className={`formattedTextBox-outer${selected ? "-selected" : ""}`} ref={this._scrollRef}
                         style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined }}
                         onScroll={this.onscrolled} onDrop={this.ondrop} >
-                        <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
+                        <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
                             style={{
                                 overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
                                 padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
-- 
cgit v1.2.3-70-g09d2


From 6862403368f752ad44c9e64bfdbcb38d2c32287c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 22 Nov 2020 10:34:14 -0500
Subject: converted backgroundColor prop to a more general styleProvider.

---
 src/client/views/GestureOverlay.tsx                |  2 +-
 src/client/views/MainView.tsx                      | 56 ++-----------
 src/client/views/OverlayView.tsx                   |  2 +-
 src/client/views/Palette.tsx                       |  2 +-
 src/client/views/PropertiesView.tsx                |  4 +-
 .../views/collections/CollectionCarouselView.tsx   |  2 +-
 .../views/collections/CollectionLinearView.tsx     |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  6 +-
 src/client/views/collections/CollectionView.tsx    |  4 +-
 src/client/views/collections/TabDocView.tsx        | 95 ++++++++++++++++++++--
 src/client/views/collections/TreeView.tsx          |  6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +-
 .../collectionGrid/CollectionGridView.tsx          |  2 +-
 .../CollectionMulticolumnView.tsx                  |  2 +-
 .../CollectionMultirowView.tsx                     |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 +--
 .../views/nodes/ContentFittingDocumentView.tsx     |  2 +-
 src/client/views/nodes/DocHolderBox.tsx            |  6 +-
 src/client/views/nodes/DocumentView.tsx            | 48 ++---------
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/FilterBox.tsx               |  2 +-
 src/client/views/nodes/FontIconBox.tsx             |  2 +-
 src/client/views/nodes/LinkBox.tsx                 |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  4 +-
 .../views/nodes/formattedText/DashDocView.tsx      |  2 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |  2 +-
 .../views/presentationview/PresElementBox.tsx      |  4 +-
 src/fields/Doc.ts                                  |  1 +
 src/mobile/AudioUpload.tsx                         |  2 +-
 src/mobile/MobileInterface.tsx                     |  2 +-
 32 files changed, 149 insertions(+), 142 deletions(-)

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

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index ffa089af1..35dedf016 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -901,7 +901,7 @@ export class GestureOverlay extends Touchable {
                 PanelWidth={this.return300}
                 PanelHeight={this.return300}
                 renderDepth={0}
-                backgroundColor={returnEmptyString}
+                styleProvider={returnEmptyString}
                 focus={emptyFunction}
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6947dd3cd..ce526f842 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -16,7 +16,6 @@ import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, return
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs } from '../documents/Documents';
-import { DocumentType } from '../documents/DocumentTypes';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
@@ -59,8 +58,8 @@ import { SearchBox } from './search/SearchBox';
 import { TraceMobx } from '../../fields/util';
 import { SelectionManager } from '../util/SelectionManager';
 import { UndoManager } from '../util/UndoManager';
+import { TabDocView } from './collections/TabDocView';
 const _global = (window /* browser */ || global /* node */) as any;
-import Color = require('color');
 
 @observer
 export class MainView extends React.Component {
@@ -230,46 +229,6 @@ export class MainView extends React.Component {
     getPHeight = () => this._panelHeight;
     getContentsHeight = () => this._panelHeight - Number(SEARCH_PANEL_HEIGHT.replace("px", ""));
 
-    defaultBackgroundColors = (doc: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
-        let docColor = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
-        if (!docColor) {
-            if (this.darkScheme) {
-                switch (doc?.type) {
-                    case DocumentType.PRESELEMENT: docColor = "dimgrey"; break;
-                    case DocumentType.PRES: docColor = "#3e3e3e"; break;
-                    case DocumentType.FONTICON: docColor = "black"; break;
-                    case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: docColor = "#2d2d2d"; break;
-                    case DocumentType.LINK:
-                    case DocumentType.COL:
-                        docColor = Doc.IsSystem(doc) ? "rgb(62,62,62)" : StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
-                        break;
-                    //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
-                    default: docColor = "black"; break;
-                }
-            } else {
-                switch (doc?.type) {
-                    case DocumentType.PRESELEMENT: docColor = ""; break;
-                    case DocumentType.FONTICON: docColor = "black"; break;
-                    case DocumentType.RTF: docColor = "#f1efeb"; break;
-                    case DocumentType.BUTTON:
-                    case DocumentType.LABEL: docColor = "lightgray"; break;
-                    case DocumentType.LINK:
-                    case DocumentType.COL:
-                        docColor = Doc.IsSystem(doc) ? "lightgrey" :
-                            StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground :
-                                Doc.UserDoc().activeCollectionBackground);
-                        break;
-                    //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray";
-                    default: docColor = "white"; break;
-                }
-            }
-        }
-        if (!doc || layerProvider?.(doc) === false) {
-            return Color(docColor).fade(0.5).toString();
-        }
-        return docColor;
-    }
-
     @computed get mainDocView() {
         return <DocumentView
             Document={this.mainContainer!}
@@ -280,7 +239,6 @@ export class MainView extends React.Component {
             pinToPres={emptyFunction}
             rootSelected={returnTrue}
             onClick={undefined}
-            backgroundColor={this.defaultBackgroundColors}
             removeDocument={undefined}
             ScreenToLocalTransform={Transform.Identity}
             ContentScaling={returnOne}
@@ -352,7 +310,7 @@ export class MainView extends React.Component {
                         PanelHeight={this.getContentsHeight}
                         renderDepth={0}
                         focus={emptyFunction}
-                        backgroundColor={this.defaultBackgroundColors}
+                        styleProvider={TabDocView.styleProvider}
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
@@ -387,7 +345,7 @@ export class MainView extends React.Component {
                 PanelHeight={this.getContentsHeight}
                 renderDepth={0}
                 focus={emptyFunction}
-                backgroundColor={this.defaultBackgroundColors}
+                styleProvider={TabDocView.styleProvider}
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
@@ -439,7 +397,7 @@ export class MainView extends React.Component {
                 <div className="mainView-propertiesDragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
                     <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? "white" : "black"} size="sm" />
                 </div>
-                {this.propertiesWidth() < 10 ? (null) : <PropertiesView backgroundColor={this.defaultBackgroundColors} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
+                {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={TabDocView.styleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
             </div>
         </>;
     }
@@ -494,7 +452,7 @@ export class MainView extends React.Component {
                     dropAction={"alias"}
                     annotationsKey={""}
                     parentActive={returnFalse}
-                    backgroundColor={this.defaultBackgroundColors}
+                    styleProvider={TabDocView.styleProvider}
                     rootSelected={returnTrue}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
@@ -570,7 +528,7 @@ export class MainView extends React.Component {
                 pinToPres={emptyFunction}
                 rootSelected={returnTrue}
                 onClick={undefined}
-                backgroundColor={this.defaultBackgroundColors}
+                styleProvider={TabDocView.styleProvider}
                 removeDocument={undefined}
                 ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={returnOne}
@@ -634,7 +592,7 @@ export class MainView extends React.Component {
             <CollectionMenu />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.EditLink ? <LinkMenu docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
-            {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
+            {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} styleProvider={TabDocView.styleProvider}
                 linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
                 addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
             <GestureOverlay >
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7d47abdce..2727ac2df 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -195,7 +195,7 @@ export class OverlayView extends React.Component {
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     focus={emptyFunction}
-                    backgroundColor={returnEmptyString}
+                    styleProvider={returnEmptyString}
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
                     docFilters={returnEmptyFilter}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 62e0fb379..536af2776 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -53,7 +53,7 @@ export default class Palette extends React.Component<PaletteProps> {
                             PanelHeight={() => window.screen.height}
                             renderDepth={0}
                             focus={emptyFunction}
-                            backgroundColor={returnEmptyString}
+                            styleProvider={returnEmptyString}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 17cfb5a5a..245e612b3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -36,7 +36,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 interface PropertiesViewProps {
     width: number;
     height: number;
-    backgroundColor: (doc: Opt<Doc>, renderDepth: number) => Opt<string>;
+    styleProvider?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
 }
 
 @observer
@@ -270,7 +270,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     renderDepth={1}
                     rootSelected={returnFalse}
                     treeViewDoc={undefined}
-                    backgroundColor={this.props.backgroundColor}
+                    styleProvider={this.props.styleProvider}
                     fitToBox={true}
                     FreezeDimensions={true}
                     dontCenter={"y"}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 8c58a5679..e32400385 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -65,7 +65,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                 </div>
                 <div className="collectionCarouselView-caption" key="caption"
                     style={{
-                        background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.backgroundColor?.(this.props.Document, this.props.renderDepth, this.props.layerProvider)),
+                        background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "color", this.props.layerProvider)),
                         color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
                         borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
                     }}>
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index b6ab3f0e0..b427e35c3 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -154,7 +154,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 PanelHeight={nested ? pair.layout[HeightSym] : this.dimension}
                                 renderDepth={this.props.renderDepth + 1}
                                 focus={emptyFunction}
-                                backgroundColor={this.props.backgroundColor}
+                                styleProvider={this.props.styleProvider}
                                 parentActive={returnTrue}
                                 whenActiveChanged={emptyFunction}
                                 bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 41e2ad81a..4b3393e14 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -192,7 +192,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
         return <ContentFittingDocumentView
             Document={doc}
             DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
-            backgroundColor={this.props.backgroundColor}
+            styleProvider={this.props.styleProvider}
             LayoutTemplate={this.props.ChildLayoutTemplate}
             LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 56c6978f1..85bf76e6d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -168,7 +168,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 rootSelected={returnTrue}
                 treeViewDoc={undefined}
                 //dontRegisterView={true}
-                backgroundColor={this.props.backgroundColor}
+                styleProvider={this.props.styleProvider}
                 PanelWidth={this.rtfWidth}
                 PanelHeight={this.rtfOutlineHeight}
                 focus={this.props.focus}
@@ -204,7 +204,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
         return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
-            moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+            moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, this.props.ScreenToLocalTransform,
             this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
             BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
             this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null));
@@ -216,7 +216,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     render() {
         TraceMobx();
         if (!(this.doc instanceof Doc)) return (null);
-        const background = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth, this.props.layerProvider);
+        const background = this.props.styleProvider?.(this.doc, this.props.renderDepth, "color", this.props.layerProvider);
         const paddingX = `${NumCast(this.doc._xPadding, 10)}px`;
         const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`;
         const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4d620bc61..e2c6bcaf6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -398,8 +398,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutTemplate: this.childLayoutTemplate,
             ChildLayoutString: this.childLayoutString,
         };
-        const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || Cast(this.props.Document.layers, listSpec("string"), []).includes("background") || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            `${CurrentUserUtils.ActiveDashboard?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+        const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined :
+            this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "boxShadow", this.props.layerProvider);
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 471cc639e..6d22f0420 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -6,11 +6,11 @@ import { clamp } from 'lodash';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
+import { DataSym, Doc, DocListCast, Opt, DocListCastAsync, StrListCast } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
 import { FieldId } from "../../../fields/RefField";
 import { listSpec } from '../../../fields/Schema';
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
@@ -30,6 +30,9 @@ import { CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
 import { List } from '../../../fields/List';
+import { DocumentType } from '../../documents/DocumentTypes';
+import Color = require('color');
+import { InkTool } from '../../../fields/InkField';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -274,6 +277,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.ContentScaling()) / 2 : 0; }
     @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.ContentScaling()) / this._panelWidth * 100}% ` : undefined; }
     @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
+    @computed static get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
 
     // adds a tab to the layout based on the locaiton parameter which can be:
     //  close[:{left,right,top,bottom}]  - e.g., "close" will close the tab, "close:left" will close the left tab, 
@@ -297,7 +301,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         }
     }
 
-    @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document, 0))); }
+    @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, TabDocView.styleProvider(this._document, 0, "color"))); }
     @computed get renderBounds() {
         const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
         const xbounds = bounds[2] - bounds[0];
@@ -355,7 +359,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     renderDepth={0}
                     whenActiveChanged={emptyFunction}
                     focus={emptyFunction}
-                    backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
+                    styleProvider={TabDocView.styleProvider}
                     addDocTab={this.addDocTab}
                     pinToPres={TabDocView.PinDoc}
                     docFilters={CollectionDockingView.Instance.docFilters}
@@ -370,7 +374,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
             <Tooltip title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
                 <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })}
-                    style={{ background: CollectionDockingView.Instance.props.backgroundColor?.(this._document, 0) }} >
+                    style={{ background: TabDocView.styleProvider(this._document, 0, "color") }} >
                     <FontAwesomeIcon icon={"globe-asia"} size="lg" />
                 </div>
             </Tooltip>
@@ -385,6 +389,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     setView = action((view: DocumentView) => this._view = view);
     active = () => this._isActive;
 
+    //
+    // a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
+    //  currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
+    //     if it matches, then the document gets pointer events, otherwise it does not.
+    //
     layerProvider = (doc: Doc, assign?: boolean) => {
         if (doc.z) return true;
         if (assign) {
@@ -404,6 +413,80 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             return false;
         }
     }
+
+    @undoBatch
+    @action
+    static toggleBackground = (doc: Doc) => {
+        const layers = StrListCast(doc.layers);
+        if (!layers.includes("background")) {
+            if (!layers.length) doc.layers = new List<string>(["background"]);
+            else layers.push("background");
+        }
+        else layers.splice(layers.indexOf("background"), 1);
+        doc._overflow = !layers.includes("background") ? "visible" : undefined;
+        if (!layers.includes("background")) {
+            //this.props.bringToFront(doc, true);
+            // const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
+            // const hgt = this.Document[HeightSym]();
+            // Doc.SetNativeWidth(this.props.Document[DataSym], wid);
+            // Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
+        }
+    }
+    //
+    // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
+    // 
+    public static styleProvider = (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean): any => {
+        if (property === "color") {
+            if (Doc.UserDoc().renderStyle === "comic") return undefined;
+            let docColor = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
+            if (!docColor) {
+                switch (doc?.type) {
+                    case DocumentType.PRESELEMENT: docColor = TabDocView.darkScheme ? "dimgrey" : ""; break;
+                    case DocumentType.PRES: docColor = TabDocView.darkScheme ? "#3e3e3e" : "black"; break;
+                    case DocumentType.FONTICON: docColor = "black"; break;
+                    case DocumentType.RTF: docColor = TabDocView.darkScheme ? "#2d2d2d" : "#f1efeb";
+                    case DocumentType.LABEL:
+                    case DocumentType.BUTTON: docColor = TabDocView.darkScheme ? "#2d2d2d" : "lightgray"; break;
+                    case DocumentType.LINK:
+                    case DocumentType.COL:
+                        docColor = Doc.IsSystem(doc) ? (TabDocView.darkScheme ? "rgb(62,62,62)" : "lightgrey") :
+                            StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
+                        break;
+                    //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
+                    default: docColor = TabDocView.darkScheme ? "black" : "white"; break;
+                }
+            }
+            if (docColor && (!doc || layerProvider?.(doc) === false)) docColor = Color(docColor).fade(0.5).toString();
+            return docColor;
+        }
+        if (property.startsWith("pointerEvents")) {
+            const layer = doc && layerProvider?.(doc);
+            if (layer === false && !property.includes(":selected") && !SnappingManager.GetIsDragging()) return "none";
+            if (doc?.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None) return "none";
+            if (layer === true) return "all";
+            return undefined;
+        }
+        if (property.startsWith("decorations")) {
+            const isBackground = StrListCast(doc?.layers).includes("background");
+            return doc && (isBackground || property.includes(":selected")) && renderDepth > 0 &&
+                ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
+                <div className="documentView-lock" onClick={() => TabDocView.toggleBackground(doc)}>
+                    <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" />
+                </div>
+                : (null);
+        }
+        if (property === "hidden") {
+            if (doc && (doc.hidden /* || layerProvider?.(doc) === false*/)) return true;
+            return false;
+        }
+        if (property === "boxShadow") {
+            switch (doc?.type) {
+                case DocumentType.COL: return StrListCast(doc.layers).includes("background") ? undefined :
+                    `${TabDocView.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+            }
+            return undefined;
+        }
+    }
     @computed get docView() {
         TraceMobx();
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
@@ -427,7 +510,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 parentActive={this.active}
                 whenActiveChanged={emptyFunction}
                 focus={this.focusFunc}
-                backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
+                styleProvider={TabDocView.styleProvider}
                 addDocTab={this.addDocTab}
                 pinToPres={TabDocView.PinDoc}
                 docFilters={CollectionDockingView.Instance.docFilters}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ce13c1d38..cb521ea75 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -49,7 +49,7 @@ export interface TreeViewProps {
     outdentDocument?: () => void;
     ScreenToLocalTransform: () => Transform;
     dontRegisterView?: boolean;
-    backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
+    backgroundColor?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
     outerXf: () => { translateX: number, translateY: number };
     treeView: CollectionTreeView;
     parentKey: string;
@@ -579,7 +579,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             renderDepth={this.props.renderDepth + 1}
             rootSelected={returnTrue}
             treeViewDoc={undefined}
-            backgroundColor={this.props.backgroundColor}
+            styleProvider={this.props.backgroundColor}
             docFilters={returnEmptyFilter}
             docRangeFilters={returnEmptyFilter}
             searchFilterDocs={returnEmptyDoclist}
@@ -661,7 +661,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         dropAction: dropActionType,
         addDocTab: (doc: Doc, where: string) => boolean,
         pinToPres: (document: Doc) => void,
-        backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined),
+        backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined),
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
         active: (outsideReaction?: boolean) => boolean,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f36518439..84f82beda 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -393,8 +393,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    getClusterColor = (doc: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
-        let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1, layerProvider);
+    getClusterColor = (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => {
+        let clusterColor = this.props.styleProvider?.(doc, this.props.renderDepth + 1, property, layerProvider);
+        if (property !== "color") return clusterColor;
         const cluster = NumCast(doc?.cluster);
         if (this.Document._useClusters) {
             if (this._clusterSets.length <= cluster) {
@@ -1016,7 +1017,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             docRangeFilters: this.docRangeFilters,
             searchFilterDocs: this.searchFilterDocs,
             focus: this.focusDocument,
-            backgroundColor: this.getClusterColor,
+            styleProvider: this.getClusterColor,
             backgroundHalo: this.backgroundHalo(childLayout),
             bringToFront: this.bringToFront,
             addDocTab: this.addDocTab,
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 8e86b6d0d..f3e385746 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -165,7 +165,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
             {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
-            backgroundColor={this.props.backgroundColor}
+            styleProvider={this.props.styleProvider}
             ContainingCollectionDoc={this.props.Document}
             PanelWidth={width}
             PanelHeight={height}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 6e16137b5..2e7cdf6d0 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -216,7 +216,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
         return <ContentFittingDocumentView
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
-            backgroundColor={this.props.backgroundColor}
+            styleProvider={this.props.styleProvider}
             LayoutTemplate={this.props.ChildLayoutTemplate}
             LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index c5e1816d9..4206a5171 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -216,7 +216,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
         return <ContentFittingDocumentView
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
-            backgroundColor={this.props.backgroundColor}
+            styleProvider={this.props.styleProvider}
             LayoutTemplate={this.props.ChildLayoutTemplate}
             LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index cba65f663..4f6c5cebe 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -623,7 +623,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                                             ContainingCollectionDoc={this.props.Document}
                                             parentActive={returnTrue}
                                             bringToFront={emptyFunction}
-                                            backgroundColor={returnTransparent}
+                                            styleProvider={returnTransparent}
                                             ContentScaling={returnOne}
                                             forcedBackgroundColor={returnTransparent}
                                             pointerEvents={"none"}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e6cb160dd..caacd1bd4 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -208,7 +208,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             dragDivName={"collectionFreeFormDocumentView-container"}
             ContentScaling={this.contentScaling}
             ScreenToLocalTransform={this.getTransform}
-            backgroundColor={this.props.backgroundColor}
+            styleProvider={this.props.styleProvider}
             opacity={this.opacity}
             layerProvider={this.props.layerProvider}
             NativeHeight={this.NativeHeight}
@@ -251,22 +251,18 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     NativeHeight = () => this.nativeHeight;
     @computed get pointerEvents() {
         if (this.props.pointerEvents === "none") return "none";
-        const layer = this.props.layerProvider?.(this.Document);
-        if (layer === false && !this._contentView?.docView?.isSelected() && !SnappingManager.GetIsDragging()) return "none";
-        if (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None) return "none";
-        if (layer === true) return "all";
-        return this.props.pointerEvents;
+        return this.props.styleProvider?.(this.Document, this.props.renderDepth, !this._contentView?.docView?.isSelected() ? "pointerEvents:selected" : "pointerEvents", this.props.layerProvider);
     }
     render() {
         TraceMobx();
-        const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth, this.props.layerProvider);
+        const backgroundColor = this.props.styleProvider?.(this.Document, this.props.renderDepth, "color", this.props.layerProvider);
         const borderRounding = StrCast(Doc.Layout(this.layoutDoc).borderRounding) || StrCast(this.layoutDoc.borderRounding) || StrCast(this.Document.borderRounding) || undefined;
         return <div className="collectionFreeFormDocumentView-container"
             style={{
                 boxShadow:
                     this.Opacity === 0 ? undefined :  // if it's not visible, then no shadow
                         this.layoutDoc.z ? `#9c9396  ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                            this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth, this.props.layerProvider)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(Cast(this.layoutDoc.layers, listSpec("string"), []).includes("background") ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                            this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "color", this.props.layerProvider)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(Cast(this.layoutDoc.layers, listSpec("string"), []).includes("background") ? 100 : 50) / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
                                 Cast(this.layoutDoc.layers, listSpec("string"), []).includes('background') ? undefined :  // if it's a background & has a cluster color, make the shadow spread really big
                                     StrCast(this.layoutDoc.boxShadow, ""),
                 borderRadius: borderRounding,
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 5c2a7843f..a54479f55 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -79,7 +79,7 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                         layoutKey={this.props.layoutKey}
                         dropAction={this.props.dropAction}
                         onClick={this.props.onClick}
-                        backgroundColor={this.props.backgroundColor}
+                        styleProvider={this.props.styleProvider}
                         addDocument={this.props.addDocument}
                         removeDocument={this.props.removeDocument}
                         moveDocument={this.props.moveDocument}
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index b5e0df659..a20423fdc 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -125,7 +125,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     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}
-                    backgroundColor={this.props.backgroundColor}
+                    styleProvider={this.props.styleProvider}
                     LayoutTemplateString={layoutTemplate}
                     LayoutTemplate={this.layoutTemplateDoc}
                     rootSelected={this.props.isSelected}
@@ -154,7 +154,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                     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}
-                    backgroundColor={this.props.backgroundColor}
+                    styleProvider={this.props.styleProvider}
                     ignoreAutoHeight={true}
                     LayoutTemplateString={layoutTemplate}
                     LayoutTemplate={this.layoutTemplateDoc}
@@ -184,7 +184,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
             onContextMenu={this.specificContextMenu}
             onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
-                background: this.props.backgroundColor?.(containedDoc, this.props.renderDepth, this.props.layerProvider),
+                background: this.props.styleProvider?.(containedDoc, this.props.renderDepth, "color", this.props.layerProvider),
                 border: `#00000021 solid ${this.xPad}px`,
                 borderTop: `#0000005e solid ${this.yPad}px`,
                 borderBottom: `#0000005e solid ${this.yPad}px`,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 067732a80..9e24d2284 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, StrListCast } from "../../../fields/Doc";
 import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
@@ -93,7 +93,7 @@ export interface DocumentViewProps {
     addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     backgroundHalo?: () => boolean;
-    backgroundColor?: (doc: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
+    styleProvider?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
     forcedBackgroundColor?: (doc: Doc) => string | undefined;
     opacity?: () => number | undefined;
     ChromeHeight?: () => number;
@@ -745,24 +745,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.Document.isLinkButton = true;
     }
 
-    @undoBatch
-    @action
-    toggleBackground = () => {
-        const layers = Cast(this.Document.layers, listSpec("string"), []);
-        if (!layers.includes("background")) {
-            if (!layers.length) this.Document.layers = new List<string>(["background"]);
-            else layers.push("background");
-        }
-        else layers.splice(layers.indexOf("background"), 1);
-        this.Document._overflow = !layers.includes("background") ? "visible" : undefined;
-        if (!layers.includes("background")) {
-            this.props.bringToFront(this.props.Document, true);
-            // const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
-            // const hgt = this.Document[HeightSym]();
-            // Doc.SetNativeWidth(this.props.Document[DataSym], wid);
-            // Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
-        }
-    }
 
     @action
     onCopy = () => {
@@ -966,7 +948,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 bringToFront={this.props.bringToFront}
                 addDocTab={this.props.addDocTab}
                 pinToPres={this.props.pinToPres}
-                backgroundColor={this.props.backgroundColor}
+                styleProvider={this.props.styleProvider}
                 ContentScaling={this.childScaling}
                 ChromeHeight={this.chromeHeight}
                 isSelected={this.isSelected}
@@ -1083,11 +1065,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get pointerEvents() {
         if (this.props.pointerEvents === "none") return "none";
-        const layer = this.props.layerProvider?.(this.Document);
-        if (layer === false && !this.isSelected() && !SnappingManager.GetIsDragging()) return "none";
-        if (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None) return "none";
-        if (layer === true) return "all";
-        return undefined;
+        return this.props.styleProvider?.(this.Document, this.props.renderDepth, this.isSelected() ? "pointerEvents:selected" : "pointerEvents", this.props.layerProvider);
     }
     @undoBatch
     @action
@@ -1107,23 +1085,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }), 400);
     });
 
-    renderLock() {
-        const isBackground = Cast(this.Document.layers, listSpec("string"), []).includes("background");
-        return (isBackground || this.isSelected(false)) &&
-            ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.RTF || this.Document.type === DocumentType.IMG || this.Document.type === DocumentType.INK) &&
-            this.props.renderDepth > 0 && !this.props.treeViewDoc ?
-            <div className="documentView-lock" onClick={this.toggleBackground}>
-                <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" />
-            </div>
-            : (null);
-    }
 
     render() {
         TraceMobx();
         if (!(this.props.Document instanceof Doc)) return (null);
         if (GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate) return (null);
-        if (this.props.Document.hidden) return (null);
-        const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || this.props.backgroundColor?.(this.layoutDoc, this.props.renderDepth, this.props.layerProvider);
+        if (this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "hidden", this.props.layerProvider)) return null;
+        const backgroundColor = this.props.forcedBackgroundColor?.(this.Document) || this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "color", this.props.layerProvider);
         const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
         const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
         const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
@@ -1159,7 +1127,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 transformOrigin: this._animateScalingTo ? "center center" : undefined,
                 transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
                 transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
-                pointerEvents: this.pointerEvents,
+                pointerEvents: this.pointerEvents as any,
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
                 border: highlighting && borderRounding && highlightStyles[fullDegree] === "dashed" ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
@@ -1178,7 +1146,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 <div className="documentView-contentBlocker" />
             </> :
                 this.innards}
-            {this.renderLock()}
+            {!this.props.treeViewDoc && this.props.styleProvider?.(this.rootDoc, this.props.renderDepth, this.isSelected() ? "decorations:selected" : "decorations", this.props.layerProvider) || (null)}
         </div>;
     }
 }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index b8f2d5d6f..9df3e6b81 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -43,7 +43,7 @@ export interface FieldViewProps {
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc | Doc[]) => boolean;
     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    backgroundColor?: (document: Opt<Doc>, renderDepth: number, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined;
+    styleProvider?: (document: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     parentActive: (outsideReaction: boolean) => boolean;
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 24cd6f21f..4db421e7a 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -214,7 +214,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     ignoreFields={this.ignoreFields}
                     annotationsKey={""}
                     dontRegisterView={true}
-                    backgroundColor={this.filterBackground}
+                    styleProvider={this.filterBackground}
                     moveDocument={returnFalse}
                     removeDocument={returnFalse}
                     addDocument={returnFalse} />
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 8eb107274..d4b6f1a27 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -61,7 +61,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
     render() {
         const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
         const color = StrCast(this.layoutDoc.color, this._foregroundColor);
-        const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc, this.props.renderDepth, this.props.layerProvider)));
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props.renderDepth, "color", this.props.layerProvider);
         const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle");
         const icon = StrCast(this.dataDoc.icon, "user") as any;
         const presSize = shape === 'round' ? 25 : 30;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index f80eb8f79..edad14e96 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -17,7 +17,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
     render() {
         return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
-            style={{ background: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth, this.props.layerProvider) }} >
+            style={{ background: this.props.styleProvider?.(this.props.Document, this.props.renderDepth, "color", this.props.layerProvider) }} >
 
             <CollectionTreeView {...this.props}
                 ChromeHeight={returnZero}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index d47942bd9..dfdca7ebb 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -16,7 +16,7 @@ interface Props {
     linkDoc?: Doc;
     linkSrc?: Doc;
     href?: string;
-    backgroundColor: (doc: Opt<Doc>, renderDepth: number) => string;
+    styleProvider?: (doc: Opt<Doc>, renderDepth: number, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => any;
     addDocTab: (document: Doc, where: string) => boolean;
     location: number[];
 }
@@ -112,7 +112,7 @@ export class LinkDocPreview extends React.Component<Props> {
                 whenActiveChanged={returnFalse}
                 bringToFront={returnFalse}
                 ContentScaling={returnOne}
-                backgroundColor={this.props.backgroundColor} />;
+                styleProvider={this.props.styleProvider} />;
     }
 
     render() {
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 7cd92b8b9..67bfd435b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -240,7 +240,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                             PanelWidth={finalLayout[WidthSym]}
                             PanelHeight={finalLayout[HeightSym]}
                             focus={this.outerFocus}
-                            backgroundColor={returnEmptyString}
+                            styleProvider={returnEmptyString}
                             parentActive={returnFalse}
                             whenActiveChanged={returnFalse}
                             bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 40c1d1cac..b5d984aac 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -148,7 +148,7 @@ export class DashDocView {
                     PanelWidth={finalLayout[WidthSym]}
                     PanelHeight={finalLayout[HeightSym]}
                     focus={this.outerFocus}
-                    backgroundColor={returnEmptyString}
+                    styleProvider={returnEmptyString}
                     parentActive={returnFalse}
                     whenActiveChanged={returnFalse}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 574bda970..8d4bd4b8b 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -88,7 +88,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     LibraryPath={emptyPath}
                     fitToBox={true}
-                    backgroundColor={this.props.backgroundColor}
+                    styleProvider={this.props.styleProvider}
                     rootSelected={returnTrue}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
@@ -312,7 +312,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     <div className="presItem-number">
                         {`${this.indexInPres + 1}.`}
                     </div>}
-                {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`} style={{ backgroundColor: this.props.backgroundColor?.(this.layoutDoc, this.props.renderDepth, this.props.layerProvider) }}>
+                {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`} style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "color", this.props.layerProvider) }}>
                     <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 185) : toolbarWidth - 95, cursor: isSelected ? 'text' : 'grab' }}>
                         <EditableView
                             ref={this._titleRef}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4c3c45d92..7b01d60e5 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -76,6 +76,7 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
 
 export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> { return Cast(field, Doc); }
 
+export function StrListCast(field: FieldResult) { return Cast(field, listSpec("string"), []) as string[]; }
 export function DocListCast(field: FieldResult) { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; }
 export function DocListCastOrNull(field: FieldResult) { return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; }
 
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index ebc8bc8a7..604c9a5ab 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -97,7 +97,7 @@ export class AudioUpload extends React.Component {
                         PanelHeight={() => 400}
                         renderDepth={0}
                         focus={emptyFunction}
-                        backgroundColor={() => "rgba(0,0,0,0)"}
+                        styleProvider={() => "rgba(0,0,0,0)"}
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index a42d85b56..ae88a2eaf 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -215,7 +215,7 @@ export class MobileInterface extends React.Component {
                     PanelHeight={this.returnHeight}
                     renderDepth={0}
                     focus={emptyFunction}
-                    backgroundColor={this.whitebackground}
+                    styleProvider={this.whitebackground}
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-- 
cgit v1.2.3-70-g09d2

