From 72dad52c4fea5a2fccabacbb6a49ca1494093954 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 22 Jun 2023 10:14:26 -0400
Subject: cleaned up dragging - abort presItem drags properly, fix naming for
 drag/dropAction, small fix undo of drag removeproperties, dragging in/out of
 headerBar,  fixed isContentActive to avoid SnappingManager.GetIsDragging. 
 fixed resizing videos.

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

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d1b7f6ff6..539dde7e0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -187,7 +187,7 @@ export function CollectionSubView<X>(moreProps?: X) {
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
-                // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
+                // if dropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
                 // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys)
                 if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
                     de.complete.docDragData.dropAction = targetAction;
@@ -212,16 +212,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = this.props.Document._type_collection === CollectionViewType.Pile || de.embedKey || this.props.Document.allowOverlayDrop || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
-                        added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
+                        const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+                        added = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
                     } else {
-                        ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
+                        ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                         added = addedDocs.length ? this.addDocument(addedDocs) : true;
                     }
                     added && e.stopPropagation();
                     return added;
                 } else {
-                    ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
+                    ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
                 }
                 !added && alert('You cannot perform this move');
-- 
cgit v1.2.3-70-g09d2


From 1429ab79eac9aa316082f52c14c576f6b3a97111 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 23 Jun 2023 13:14:18 -0400
Subject: cleaned up more issues with pointer events/contents active.  fixed
 dragging from piles.

---
 src/client/documents/Documents.ts                  |  7 ++-
 src/client/util/UndoManager.ts                     |  3 +-
 src/client/views/DocComponent.tsx                  |  2 +-
 src/client/views/PropertiesButtons.tsx             |  1 +
 .../collections/CollectionMasonryViewFieldRow.tsx  |  9 +--
 src/client/views/collections/CollectionMenu.tsx    |  3 +-
 .../views/collections/CollectionNoteTakingView.tsx | 13 +++--
 .../collections/CollectionNoteTakingViewColumn.tsx |  1 +
 .../views/collections/CollectionPileView.tsx       |  8 +--
 .../collections/CollectionStackedTimeline.tsx      | 30 +++++-----
 .../views/collections/CollectionStackingView.tsx   | 10 +++-
 .../CollectionStackingViewFieldColumn.tsx          |  1 +
 src/client/views/collections/CollectionSubView.tsx | 18 +++---
 .../views/collections/CollectionTreeView.tsx       | 15 +++--
 src/client/views/collections/TreeView.tsx          | 23 ++++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 17 +++---
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 +
 .../CollectionMulticolumnView.tsx                  |  1 +
 .../CollectionMultirowView.tsx                     |  1 +
 .../collectionSchema/CollectionSchemaView.tsx      |  3 +-
 src/client/views/nodes/ComparisonBox.tsx           | 11 ++--
 src/client/views/nodes/DocumentView.tsx            | 22 +++++---
 src/client/views/nodes/FunctionPlotBox.tsx         |  5 +-
 src/client/views/nodes/ImageBox.tsx                | 17 +++---
 src/client/views/nodes/LabelBox.tsx                |  2 +
 src/client/views/nodes/ScriptingBox.tsx            |  8 ++-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 64 +++++++++++++---------
 src/fields/Proxy.ts                                |  4 +-
 src/fields/util.ts                                 |  3 +-
 29 files changed, 184 insertions(+), 119 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a61ef1da3..acd323eca 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -361,12 +361,13 @@ export class DocumentOptions {
     _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)');
     _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
     _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.');
-    childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged fromt he collection');
+    childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged from the collection');
     dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
     dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
     _dropPropertiesToRemove?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
+    dragWhenActive?: BOOLt = new BoolInfo('should document drag when it is active - e.g., pileView, group');
     dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)');
     dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
@@ -667,7 +668,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDragAction: 'embed', title: 'Global Group Database' },
+                    options: { title: 'Global Group Database' },
                 },
             ],
             [
@@ -1663,7 +1664,7 @@ export namespace DocUtils {
             });
         });
         if (create) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2 });
+            const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, dragWhenActive: true });
             newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
             newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
             newCollection._width = newCollection._height = size * 2;
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 2379cb2ab..b59af6656 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,4 +1,5 @@
 import { observable, action, runInAction } from 'mobx';
+import { Field } from '../../fields/Doc';
 import { Without } from '../../Utils';
 
 function getBatchName(target: any, key: string | symbol): string {
@@ -95,7 +96,7 @@ export namespace UndoManager {
 
     export function AddEvent(event: UndoEvent, value?: any): void {
         if (currentBatch && batchCounter.get() && !undoing) {
-            console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value);
+            console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + (value instanceof Array ? value.map(val => Field.toScriptString(val)).join(',') : Field.toScriptString(value)));
             currentBatch.push(event);
             tempEvents?.push(event);
         }
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 0709d6cb9..3de40a640 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -220,7 +220,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
                         });
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
-                    if (annoDocs instanceof List) annoDocs.push(...added);
+                    if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add)));
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
                     targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now()));
                 }
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 11b89fd69..74dd1c2f7 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -209,6 +209,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             (dv, doc) => {
                 doc.isGroup = !doc.isGroup;
                 doc.forceActive = doc.isGroup;
+                doc.dragWhenActive = doc.isGroup;
             }
         );
     }
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 64f9c6a87..6f88f6727 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -98,14 +98,15 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     rowDrop = action((e: Event, de: DragManager.DropEvent) => {
         this._createEmbeddingSelected = false;
         if (de.complete.docDragData) {
-            this.props.parent.Document.dropConverter instanceof ScriptField && this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData });
             const key = this.props.pivotField;
             const castedValue = this.getValue(this.heading);
             const onLayoutDoc = this.onLayoutDoc(key);
-            de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !onLayoutDoc));
-            this.props.parent.onInternalDrop(e, de);
-            e.stopPropagation();
+            if (this.props.parent.onInternalDrop(e, de)) {
+                de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !onLayoutDoc));
+            }
+            return true;
         }
+        return false;
     });
 
     getValue = (value: string): any => {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index fc3f713ce..7d71bce13 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -427,8 +427,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         if (docDragData?.draggedDocuments.length) {
             this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || []));
             e.stopPropagation();
+            return true;
         }
-        return true;
+        return false;
     }
 
     dragViewDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 9a3ab8074..f1c4c2cdf 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -441,23 +441,24 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                         docs.splice(previousDocIndex + 1, 0, ...newDocs);
                     }
                 }
+                return true;
             }
         } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
             const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
-            this.props.addDocument?.(source);
+            if (!this.props.addDocument?.(source)) e.preventDefault();
             de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
-        } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+            return true;
+        } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) {
+            return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+        }
         return false;
     };
 
     @undoBatch
     internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) {
         const dropCreator = annoDragData.dropDocCreator;
-        annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => {
-            const dropDoc = dropCreator(annotationOn);
-            return dropDoc || this.rootDoc;
-        };
+        annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => dropCreator(annotationOn) || this.rootDoc;
         return true;
     }
 
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 2f28ecd00..3286d60bd 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -92,6 +92,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
         drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
+        return true;
     });
 
     getValue = (value: string): any => {
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 15570b195..91be31289 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -63,12 +63,14 @@ export class CollectionPileView extends CollectionSubView() {
             <div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
                 <CollectionFreeFormView
                     {...this.props} //
-                    childContentsActive={returnFalse}
                     layoutEngine={this.layoutEngine}
                     addDocument={this.addPileDoc}
+                    moveDocument={this.removePileDoc}
+                    // pile children never have their contents active, but will be document active whenever the entire pile is.
+                    childContentsActive={returnFalse}
+                    childDocumentsActive={this.props.isDocumentActive}
                     childDragAction="move"
                     childClickScript={this.toggleIcon}
-                    moveDocument={this.removePileDoc}
                 />
             </div>
         );
@@ -106,7 +108,6 @@ export class CollectionPileView extends CollectionSubView() {
     _undoBatch: UndoManager.Batch | undefined;
     pointerDown = (e: React.PointerEvent) => {
         let dist = 0;
-        SnappingManager.SetIsDragging(true);
         setupMoveUpEvents(
             this,
             e,
@@ -129,7 +130,6 @@ export class CollectionPileView extends CollectionSubView() {
             () => {
                 this._undoBatch?.end();
                 this._undoBatch = undefined;
-                SnappingManager.SetIsDragging(false);
             },
             emptyFunction,
             e.shiftKey && this.layoutEngine() === computePassLayout.name,
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index e835fa659..ad84d859d 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -369,22 +369,22 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // handles dragging and dropping markers in timeline
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
-        if (!de.embedKey && this.props.Document._isGroup) return false;
-        if (!super.onInternalDrop(e, de)) return false;
-
-        // determine x coordinate of drop and assign it to the documents being dragged  --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
-        const localPt = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        const x = localPt[0] - docDragData.offset[0];
-        const timelinePt = this.toTimeline(x + this._scroll, this.timelineContentWidth);
-        docDragData.droppedDocuments.forEach(drop => {
-            const anchorEnd = this.anchorEnd(drop);
-            if (anchorEnd !== undefined) {
-                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
-            }
-            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false);
-        });
+        if (super.onInternalDrop(e, de)) {
+            // determine x coordinate of drop and assign it to the documents being dragged  --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
+            const localPt = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+            const x = localPt[0] - docDragData.offset[0];
+            const timelinePt = this.toTimeline(x + this._scroll, this.timelineContentWidth);
+            docDragData.droppedDocuments.forEach(drop => {
+                const anchorEnd = this.anchorEnd(drop);
+                if (anchorEnd !== undefined) {
+                    Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
+                }
+                Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false);
+            });
 
-        return true;
+            return true;
+        }
+        return false;
     }
 
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 8eed4f716..4756b4cd3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -459,18 +459,24 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 const docs = this.childDocList;
                 // still figuring out where to add the document
                 if (docs && newDocs.length) {
+                    newDocs.forEach(newdoc => docs.indexOf(newdoc) !== -1 && docs.splice(docs.indexOf(newdoc), 1));
                     const insertInd = dropInd === -1 ? docs.length : dropInd + dropAfter;
                     const offset = newDocs.reduce((off, ndoc) => (this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off), 0);
                     newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
                     docs.splice(insertInd - offset, 0, ...newDocs);
                 }
+                return true;
             }
         } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
             const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
-            this.props.addDocument?.(source);
+            if (!this.props.addDocument?.(source)) e.preventDefault();
             de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
-        } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+            return true;
+        } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) {
+            return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+        }
+        e.preventDefault();
         return false;
     };
 
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 6be9cb72d..ebb4ba5a1 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -94,6 +94,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
         this.props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
+        return true;
     });
     getValue = (value: string): any => {
         const parsed = parseInt(value);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 539dde7e0..39fb2db1e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -185,12 +185,14 @@ export function CollectionSubView<X>(moreProps?: X) {
         @undoBatch
         protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {}
 
-        protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
+        protected onInternalPreDrop(e: Event, de: DragManager.DropEvent) {
             if (de.complete.docDragData) {
-                // if dropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
-                // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys)
-                if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
-                    de.complete.docDragData.dropAction = targetAction;
+                // override the dropEvent's dropAction
+                const dropAction = this.layoutDoc.dropAction as dropActionType;
+                // if the dropEvent's dragAction is, say 'embed', but we're just dragging within a collection, we may not actually want to make an embedding.
+                // so we check if our collection has a dropAction set on it and if so, we use that instead.
+                if (dropAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
+                    de.complete.docDragData.dropAction = dropAction;
                 }
                 e.stopPropagation();
             }
@@ -218,16 +220,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                         ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                         added = addedDocs.length ? this.addDocument(addedDocs) : true;
                     }
-                    added && e.stopPropagation();
-                    return added;
                 } else {
                     ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
+                    !added && alert('You cannot perform this move');
                 }
-                !added && alert('You cannot perform this move');
+                !added && e.preventDefault();
                 e.stopPropagation();
                 return added;
             } else if (de.complete.annoDragData) {
+                e.stopPropagation();
                 const dropCreator = de.complete.annoDragData.dropDocCreator;
                 de.complete.annoDragData.dropDocCreator = () => {
                     const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7598bb753..4cd3885f5 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -140,11 +140,16 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
     };
 
-    protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+    protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent) => {
+        const dropAction = this.layoutDoc.dropAction as dropActionType;
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d));
-            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
+            const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.rootDoc) ? true : false;
+            const isAlreadyInTree = () => sameTree || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d));
+            if (isAlreadyInTree() !== sameTree) {
+                console.log('WHAAAT');
+            }
+            dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
         }
     };
 
@@ -394,6 +399,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 <div
                     className="collectionTreeView-contents"
                     key="tree"
+                    ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}
                     style={{
                         ...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
                         overflow: 'auto',
@@ -417,8 +423,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                                 minHeight: '100%',
                             }}
                             onWheel={e => e.stopPropagation()}
-                            onDrop={this.onTreeDrop}
-                            ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
+                            onDrop={this.onTreeDrop}>
                             <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
                         </div>
                     </div>
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 91ae2b3cc..8d8d895c6 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -347,7 +347,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
     deleteItem = () => this.props.removeDoc?.(this.doc);
 
-    preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+    preTreeDrop = (e: Event, de: DragManager.DropEvent) => {
         const dragData = de.complete.docDragData;
         dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction);
     };
@@ -355,7 +355,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @undoBatch
     treeDrop = (e: Event, de: DragManager.DropEvent) => {
         const pt = [de.x, de.y];
-        if (!this._header.current) return;
+        if (!this._header.current) return false;
         const rect = this._header.current.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
         const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
@@ -364,14 +364,25 @@ export class TreeView extends React.Component<TreeViewProps> {
             const destDoc = this.doc;
             DocUtils.MakeLink(sourceDoc, destDoc, { link_relationship: 'tree link' });
             e.stopPropagation();
+            return true;
         }
         const docDragData = de.complete.docDragData;
         if (docDragData && pt[0] < rect.left + rect.width) {
             if (docDragData.draggedDocuments[0] === this.doc) return true;
-            if (this.dropDocuments(docDragData.droppedDocuments, before, inside, docDragData.dropAction, docDragData.removeDocument, docDragData.moveDocument, docDragData.treeViewDoc === this.props.treeView.props.Document)) {
-                e.stopPropagation();
-            }
+            const added = this.dropDocuments(
+                docDragData.droppedDocuments, //
+                before,
+                inside,
+                docDragData.dropAction,
+                docDragData.removeDocument,
+                docDragData.moveDocument,
+                docDragData.treeViewDoc === this.props.treeView.props.Document
+            );
+            e.stopPropagation();
+            !added && e.preventDefault();
+            return added;
         }
+        return false;
     };
 
     dropping: boolean = false;
@@ -924,7 +935,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 PanelHeight={return18}
                 contextMenuItems={this.contextMenuItems}
                 renderDepth={1}
-                isContentActive={this.props.isContentActive}
+                isContentActive={emptyFunction} //this.props.isContentActive}
                 isDocumentActive={this.props.isContentActive}
                 focus={this.refocus}
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d7e073c5f..e6f8f3071 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -418,15 +418,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @undoBatch
     internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
         if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
-            // dragged document is a child of this collection
-            if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.embedContainer !== this.props.Document) {
-                // if the source doc view's embedContainer isn't this same freeformcollectionlinkDragData.dragDocument.embedContainer === this.props.Document
+            let added = false;
+            //  do nothing if link is dropped into any freeform view parent of dragged document
+            if (!linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc) {
                 const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
-                this.props.addDocument?.(source);
+                added = this.props.addDocument?.(source) ? true : false;
                 de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
             }
-            e.stopPropagation(); //  do nothing if link is dropped into any freeform view parent of dragged document
-            return true;
+            e.stopPropagation();
+            !added && e.preventDefault();
+            return added;
         }
         return false;
     }
@@ -1297,8 +1298,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 childFilters={this.childDocFilters}
                 childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
-                isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction}
+                isDocumentActive={this.props.childDocumentsActive?.() || this.rootDoc._isGroup ? this.props.isDocumentActive : this.isContentActive}
+                isContentActive={this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction}
                 focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
                 addDocTab={this.addDocTab}
                 addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 898452ccb..5febbe83e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -385,6 +385,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
+        newCollection._dragWhenActive = makeGroup;
         newCollection.forceActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 32b42223b..6dcd2d422 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -220,6 +220,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                         }
                     })
                 );
+                return true;
             }
         }
         return false;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index fa3a9f352..4df5dad5a 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -220,6 +220,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                         }
                     })
                 );
+                return true;
             }
         }
         return false;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 06b227b4b..ee5bf82ed 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -434,7 +434,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.columnDragData) {
-            e.stopPropagation();
             const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0];
             const index = this.findDropIndex(mouseX);
             this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex);
@@ -448,6 +447,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 });
             });
 
+            e.stopPropagation();
             return true;
         }
         const draggedDocs = de.complete.docDragData?.draggedDocuments;
@@ -462,7 +462,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                 if (draggedView) DocumentManager.Instance.RemoveView(draggedView);
                 DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true));
             });
-            e.stopPropagation();
             return true;
         }
         return false;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index d747c4527..2290e0711 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -42,13 +42,14 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     };
 
     @undoBatch
-    private internalDrop = (event: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
+    private internalDrop = (e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
         if (dropEvent.complete.docDragData) {
-            event.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
             const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
-            const res = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
-            res && (droppedDocs.lastElement().embedContainer = this.dataDoc);
-            return res;
+            const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
+            droppedDocs.lastElement().embedContainer = this.dataDoc;
+            !added && e.preventDefault();
+            e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
+            return added;
         }
     };
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fbc84389d..b6f1626f8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -356,9 +356,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     componentDidMount() {
         this.setupHandlers();
     }
-    preDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
-        if (de.complete.docDragData && this.isContentActive()) {
-            targetAction && (de.complete.docDragData.dropAction = targetAction);
+    preDropFunc = (e: Event, de: DragManager.DropEvent) => {
+        const dropAction = this.layoutDoc.dropAction as dropActionType;
+        if (de.complete.docDragData && this.isContentActive() && !this.props.treeViewDoc) {
+            dropAction && (de.complete.docDragData.dropAction = dropAction);
             e.stopPropagation();
         }
     };
@@ -492,8 +493,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     }
                     preventDefault = false;
                 }
-
-                this._singleClickFunc = clickFunc ?? (() => this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? this.props.select(e.ctrlKey || e.metaKey || e.shiftKey));
+                const sendToBack = e.altKey;
+                this._singleClickFunc =
+                    clickFunc ??
+                    (() =>
+                        sendToBack
+                            ? this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.bringToFront(this.rootDoc, true)
+                            : this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? this.props.select(e.ctrlKey || e.metaKey || e.shiftKey));
                 const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
                 if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
                     this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
@@ -541,7 +547,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
 
                 // listen to move events if document content isn't active or document is draggable
-                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || this.props.dragAction || this.rootDoc.dragAction)) {
+                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || BoolCast(this.rootDoc._dragWhenActive))) {
                     document.addEventListener('pointermove', this.onPointerMove);
                 }
             }
@@ -617,15 +623,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
         if (this.props.Document === Doc.ActiveDashboard) {
             e.stopPropagation();
-            alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
             e.preventDefault();
+            alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
             return true;
         }
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
         if (linkdrag) {
             linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
             if (linkdrag.linkSourceDoc) {
-                e.stopPropagation();
                 if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
                     de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
                 }
@@ -633,6 +638,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                 }
+                e.stopPropagation();
                 return true;
             }
         }
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 8a88bede6..61711417f 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -80,9 +80,10 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
     @undoBatch
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData?.droppedDocuments.length) {
+            const added = de.complete.docDragData.droppedDocuments.reduce((res, doc) => res && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, doc), true);
+            !added && e.preventDefault();
             e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
-            de.complete.docDragData.droppedDocuments.map(doc => Doc.AddDocToList(this.dataDoc, this.props.fieldKey, doc));
-            return false;
+            return added;
         }
         return false;
     };
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 64d6814df..f67930a3f 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -135,19 +135,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData) {
+            let added = true;
             const targetIsBullseye = (ele: HTMLElement): boolean => {
                 if (!ele) return false;
                 if (ele === this._overlayIconRef.current) return true;
                 return targetIsBullseye(ele.parentElement as HTMLElement);
             };
             if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) {
-                de.complete.docDragData.droppedDocuments.forEach(
-                    action((drop: Doc) => {
-                        Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
-                        this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
-                        e.stopPropagation();
-                    })
-                );
+                added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => {
+                    this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
+                    return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
+                }, true);
             } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                 const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
                 const targetField = Doc.LayoutFieldKey(layoutDoc);
@@ -156,10 +154,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
                     Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
                     Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
-                    e.stopPropagation();
                 }
             }
+            !added && e.preventDefault();
+            e.stopPropagation();
+            return added;
         }
+        return false;
     };
 
     @undoBatch
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 52f3575cb..4439be0cd 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -74,7 +74,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         if (docDragData && missingParams?.includes((e.target as any).textContent)) {
             this.paramsDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) => (d.onDragStart ? docDragData.draggedDocuments[i] : d)));
             e.stopPropagation();
+            return true;
         }
+        return false;
     };
 
     @observable _mouseOver = false;
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 37fda14fc..3ad3c911d 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -270,8 +270,12 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     // sets field of the corresponding field key (param name) to be dropped document
     @action
     onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => {
-        Doc.SetInPlace(this.rootDoc, fieldKey, de.complete.docDragData?.droppedDocuments[0], true);
-        e.stopPropagation();
+        if (de.complete.docDragData) {
+            de.complete.docDragData.droppedDocuments.forEach(doc => Doc.SetInPlace(this.rootDoc, fieldKey, doc, true));
+            e.stopPropagation();
+            return true;
+        }
+        return false;
     };
 
     // deletes a param from all areas in which it is stored
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2e8f6c696..da0fc9ffb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -531,36 +531,50 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
+        if (de.complete.annoDragData) {
+            de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
+            e.stopPropagation();
+            return true;
+        }
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0];
-            // replace text contents whend dragging with Alt
-            if (draggedDoc && draggedDoc.type === DocumentType.RTF && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) {
-                if (draggedDoc.data instanceof RichTextField) {
-                    Doc.GetProto(this.dataDoc)[this.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
-                    e.stopPropagation();
-                }
-                // embed document when dragg marked as embed
-            } else if (de.embedKey) {
-                const target = dragData.droppedDocuments[0];
-                const node = schema.nodes.dashDoc.create({
-                    width: target[Width](),
-                    height: target[Height](),
-                    title: 'dashDoc',
-                    docId: target[Id],
-                    float: 'unset',
-                });
-                if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
-                    dragData.removeDocument?.(dragData.draggedDocuments[0]);
+            const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+            const effectiveAcl = GetEffectiveAcl(dataDoc);
+            let added = [AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl);
+            const draggedDoc = dragData.draggedDocuments.lastElement();
+            if (added) {
+                // replace text contents when dragging with Alt
+                if (de.altKey) {
+                    const fieldKey = Doc.LayoutFieldKey(draggedDoc);
+                    if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.props.Document)) {
+                        Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
+                    }
+
+                    // embed document when drag marked as embed
+                } else if (de.embedKey) {
+                    const node = schema.nodes.dashDoc.create({
+                        width: draggedDoc[Width](),
+                        height: draggedDoc[Height](),
+                        title: 'dashDoc',
+                        docId: draggedDoc[Id],
+                        float: 'unset',
+                    });
+                    if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
+                        added = dragData.removeDocument?.(draggedDoc) ? true : false;
+                    }
+                    if (added) {
+                        draggedDoc._freeform_fitContentsToBox = true;
+                        draggedDoc.embedContainer = this.rootDoc;
+                        const view = this._editorView!;
+                        view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
+                    }
                 }
-                target._freeform_fitContentsToBox = true;
-                target.embedContainer = this.rootDoc;
-                const view = this._editorView!;
-                view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
-                e.stopPropagation();
             } // otherwise, fall through to outer collection to handle drop
+            !added && e.preventDefault();
+            e.stopPropagation();
+            return added;
         }
+        return false;
     };
 
     getNodeEndpoints(context: Node, node: Node): { from: number; to: number } | null {
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 55d1d9ea4..c076f5fe1 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -1,5 +1,5 @@
 import { Deserializable } from '../client/util/SerializationHelper';
-import { FieldWaiting, Opt } from './Doc';
+import { Field, FieldWaiting, Opt } from './Doc';
 import { primitive, serializable } from 'serializr';
 import { observable, action, runInAction, computed } from 'mobx';
 import { DocServer } from '../client/DocServer';
@@ -39,7 +39,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
     }
 
     [ToScriptString]() {
-        return 'invalid';
+        return Field.toScriptString(this[ToValue](undefined)?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
     }
     [ToString]() {
         return 'ProxyField';
diff --git a/src/fields/util.ts b/src/fields/util.ts
index e3d1abc53..e439768ee 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -433,7 +433,8 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               },
                               prop: 'assign list',
-                          }
+                          },
+                    diff?.items
                 );
         }
         target[Update](op);
-- 
cgit v1.2.3-70-g09d2


From 02926d42bd7dfefbf87709abb45f1b359e4bfcdf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 2 Jul 2023 12:36:37 -0400
Subject: made multicolumn/row views fit contents to panel and center.  changed
 collection drop to propagate when embed is not enabled.  fixed image box drop
 .  made goldenlayout tabs selectable. turned off leaving pushpins on drag
 out.  fixed long press to disable on drag.

---
 src/client/views/DocComponent.tsx                       |  2 +-
 src/client/views/collections/CollectionSubView.tsx      | 11 ++++++-----
 src/client/views/collections/TabDocView.tsx             |  1 +
 .../CollectionMulticolumnView.scss                      |  9 ++++++---
 .../collectionMulticolumn/CollectionMulticolumnView.tsx | 17 +++++++++++------
 .../collectionMulticolumn/CollectionMultirowView.scss   | 10 +++++++---
 .../collectionMulticolumn/CollectionMultirowView.tsx    | 17 +++++++++++------
 src/client/views/nodes/DocumentView.tsx                 |  1 +
 src/client/views/nodes/ImageBox.tsx                     |  6 +++---
 9 files changed, 47 insertions(+), 27 deletions(-)

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

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 3de40a640..70d208a0b 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -173,7 +173,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const first = doc instanceof Doc ? doc : doc[0];
             if (!first?._dragOnlyWithinContainer && addDocument !== returnFalse) {
-                return this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey);
+                return this.removeDocument(doc, annotationKey, false) && addDocument(doc, annotationKey);
             }
             return false;
         };
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 39fb2db1e..78789247f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -205,7 +205,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
             const docDragData = de.complete.docDragData;
             if (docDragData) {
-                let added = false;
+                let added = undefined;
                 const dropAction = docDragData.dropAction || docDragData.userDropAction;
                 const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
                 const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
@@ -215,7 +215,8 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
                         const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
-                        added = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
+                        const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
+                        added = canAdd || moved ? moved : undefined;
                     } else {
                         ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                         added = addedDocs.length ? this.addDocument(addedDocs) : true;
@@ -225,9 +226,9 @@ export function CollectionSubView<X>(moreProps?: X) {
                     added = this.addDocument(docDragData.droppedDocuments);
                     !added && alert('You cannot perform this move');
                 }
-                !added && e.preventDefault();
-                e.stopPropagation();
-                return added;
+                added === false && !this.props.isAnnotationOverlay && e.preventDefault();
+                added === true && e.stopPropagation();
+                return added ? true : false;
             } else if (de.complete.annoDragData) {
                 e.stopPropagation();
                 const dropCreator = de.complete.annoDragData.dropDocCreator;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 0dcf17b48..118b216bf 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -335,6 +335,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     private onActiveContentItemChanged(contentItem: any) {
         if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
             this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
+            if (!this._view) setTimeout(() => SelectionManager.SelectView(this._view, false));
             !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
         }
     }
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index 821c8d804..f87a06033 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -8,6 +8,11 @@
         display: flex;
         flex-direction: column;
         width: 100%;
+        align-items: center;
+
+        .contentFittingDocumentView {
+            width: unset;
+        }
 
         .label-wrapper {
             display: flex;
@@ -15,7 +20,6 @@
             justify-content: center;
             height: 20px;
         }
-
     }
 
     .multiColumnResizer {
@@ -30,5 +34,4 @@
             transition: 0.5s background-color ease;
         }
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 6dcd2d422..10532b9d9 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -238,7 +238,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             ? true
             : undefined;
     };
-    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
+    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => {
         return (
             <DocumentView
                 Document={layout}
@@ -250,6 +250,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 renderDepth={this.props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
+                shouldNotScale={shouldNotScale}
                 rootSelected={this.rootSelected}
                 dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
@@ -287,15 +288,19 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
+            const aspect = Doc.NativeAspect(layout, undefined, true);
+            const width = () => this.lookupPixels(layout);
+            const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
+            const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(height() * aspect, width()));
+            const docheight = () => Math.min(docwidth() / aspect, height());
             const dxf = () =>
                 this.lookupIndividualTransform(layout)
-                    .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin))
+                    .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin))
                     .scale(this.props.NativeDimScaling?.() || 1);
-            const width = () => this.lookupPixels(layout);
-            const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
+            const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
             collector.push(
-                <div className={'document-wrapper'} key={'wrapper' + i} style={{ width: width() }}>
-                    {this.getDisplayDoc(layout, dxf, width, height)}
+                <div className="document-wrapper" key={'wrapper' + i} style={{ width: width() }}>
+                    {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
                     <WidthLabel layout={layout} collectionDoc={Document} />
                 </div>,
                 <ResizeBar
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index 79fb195e8..ec7200a03 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -9,6 +9,12 @@
         display: flex;
         flex-direction: row;
         height: 100%;
+        align-items: center;
+        margin: auto;
+
+        .contentFittingDocumentView {
+            height: unset;
+        }
 
         .label-wrapper {
             display: flex;
@@ -16,7 +22,6 @@
             justify-content: center;
             height: 20px;
         }
-
     }
 
     .multiRowResizer {
@@ -31,5 +36,4 @@
             transition: 0.5s background-color ease;
         }
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4df5dad5a..04cfc5456 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -238,7 +238,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             ? true
             : undefined;
     };
-    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
+    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => {
         return (
             <DocumentView
                 Document={layout}
@@ -250,6 +250,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 renderDepth={this.props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
+                shouldNotScale={shouldNotScale}
                 rootSelected={this.rootSelected}
                 dropAction={StrCast(this.rootDoc.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
@@ -287,15 +288,19 @@ export class CollectionMultirowView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
+            const aspect = Doc.NativeAspect(layout, undefined, true);
+            const height = () => this.lookupPixels(layout);
+            const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
+            const docheight = () => Math.min(width() / aspect, height());
+            const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(width(), docheight() * aspect));
             const dxf = () =>
                 this.lookupIndividualTransform(layout)
-                    .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin))
+                    .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin) - (height() - docheight()) / 2)
                     .scale(this.props.NativeDimScaling?.() || 1);
-            const height = () => this.lookupPixels(layout);
-            const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
+            const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
             collector.push(
-                <div className={'document-wrapper'} style={{ height: height() }} key={'wrapper' + i}>
-                    {this.getDisplayDoc(layout, dxf, width, height)}
+                <div className="document-wrapper" style={{ height: height() }} key={'wrapper' + i}>
+                    {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
                     <HeightLabel layout={layout} collectionDoc={Document} />
                 </div>,
                 <ResizeBar
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 733ee1ed1..c10c0005d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -561,6 +561,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
             this.cleanupPointerEvents();
+            this._longPressSelector && clearTimeout(this._longPressSelector);
             this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dragAction || this.props.dragAction || undefined) as dropActionType));
         }
     };
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f67930a3f..0bbdff719 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -135,7 +135,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData) {
-            let added = true;
+            let added: boolean | undefined = undefined;
             const targetIsBullseye = (ele: HTMLElement): boolean => {
                 if (!ele) return false;
                 if (ele === this._overlayIconRef.current) return true;
@@ -156,8 +156,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
                 }
             }
-            !added && e.preventDefault();
-            e.stopPropagation();
+            added === false && e.preventDefault();
+            added !== undefined && e.stopPropagation();
             return added;
         }
         return false;
-- 
cgit v1.2.3-70-g09d2

