aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx128
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx10
2 files changed, 43 insertions, 95 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 57c943479..cafa8b38f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -15,7 +15,7 @@ import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -36,7 +36,7 @@ import { GestureOverlay } from '../../GestureOverlay';
import { CtrlKey } from '../../GlobalKeyHandler';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
-import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
+import { CollectionFreeFormDocumentViewWrapper, CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
@@ -45,7 +45,6 @@ import { CreateImage } from '../../nodes/WebBoxRenderer';
import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
-import { TabDocView } from '../TabDocView';
import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents';
@@ -86,9 +85,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _hitCluster: number = -1;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
- private _layoutPoolData = observable.map<string, PoolData>();
- private _layoutSizeData = observable.map<string, { width?: number; height?: number }>();
- private _cachedPool: Map<string, PoolData> = new Map();
private _batch: UndoManager.Batch | undefined = undefined;
private _brushtimer: any;
private _brushtimer1: any;
@@ -109,7 +105,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (this.props.viewField ?? '') + '_freeform_autoReset';
}
- @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
+ @computed get _layoutElements(): ViewDefResult[] {
+ const computation = this.doInternalLayoutComputation;
+ return this.doLayoutComputation(computation.newPool, computation.computedElementData);
+ }
@observable _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _firstRender = false; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. could be used for performance improvement
@observable _showAnimTimeline = false;
@@ -120,7 +119,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region
@observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @computed get views() {
+ @computed get contentViews() {
const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele);
if (viewsMask.length) renderableEles.push(<div className={`collectionfreeformview-mask${this._layoutElements.some(ele => (ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}</div>);
@@ -637,7 +636,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
this._downTime = Date.now();
- if (e.button === 0 && !e.altKey && (!(e.ctrlKey && !e.metaKey) || Doc.UserDoc().freeformScrollMode !== freeformScrollMode.Pan) && this.props.isContentActive(true)) {
+ const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
+ if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this.props.isContentActive(true)) {
if (
!this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
@@ -1052,7 +1052,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
const measuredDocs = docs
- .map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ''), size: this.childSizeProviderUnmemoized(doc, '') }))
+ .map(doc => ({ pos: { x: NumCast(doc.x), y: NumCast(doc.y) }, size: { width: NumCast(doc.width), height: NumCast(doc.height) } }))
.filter(({ pos, size }) => pos && size)
.map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
@@ -1247,7 +1247,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.isContentActive() === false
? 'none'
: this.props.pointerEvents?.());
- console.log(`${this.rootDoc.title} pe = ` + pointerevents);
return pointerevents;
}
@@ -1258,16 +1257,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
return (
- <CollectionFreeFormDocumentView
+ <CollectionFreeFormDocumentViewWrapper
+ {...OmitKeys(entry, ['replica', 'pair']).omit}
key={childLayout[Id] + (entry.replica || '')}
DataDoc={childData}
Document={childLayout}
+ dragStarting={this.dragStarting}
+ dragEnding={this.dragEnding}
isGroupActive={this.props.isGroupActive}
renderDepth={this.props.renderDepth + 1}
- replica={entry.replica}
hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
suppressSetHeight={this.layoutEngine ? true : false}
- renderCutoffProvider={this.renderCutoffProvider}
+ RenderCutoffProvider={this.renderCutoffProvider}
CollectionFreeFormView={this}
LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
@@ -1295,8 +1296,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
docViewPath={this.props.docViewPath}
styleProvider={this.clusterStyleProvider}
dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
- dataProvider={this.childDataProvider}
- sizeProvider={this.childSizeProvider}
bringToFront={this.bringToFront}
layout_showTitle={this.props.childlayout_showTitle}
dontRegisterView={this.props.dontRegisterView}
@@ -1425,20 +1424,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}.bind(this)
);
- childPositionProviderUnmemoized = (doc: Doc, replica: string) => this._layoutPoolData.get(doc[Id] + (replica || ''));
- childDataProvider = computedFn(
- function childDataProvider(this: any, doc: Doc, replica: string) {
- return this.childPositionProviderUnmemoized(doc, replica);
- }.bind(this)
- );
-
- childSizeProviderUnmemoized = (doc: Doc, replica: string) => this._layoutSizeData.get(doc[Id] + (replica || ''));
- childSizeProvider = computedFn(
- function childSizeProvider(this: any, doc: Doc, replica: string) {
- return this.childSizeProviderUnmemoized(doc, replica);
- }.bind(this)
- );
-
doEngineLayout(
poolData: Map<string, PoolData>,
engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
@@ -1467,46 +1452,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
- @observable _numLoaded = 1;
@action
doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
- const array = Array.from(newPool.entries());
- for (const entry of array) {
- const lastPos = this._cachedPool.get(entry[0]); // last computed pos
- const newPos = entry[1];
- if (
- !lastPos ||
- newPos.color !== lastPos.color ||
- newPos.backgroundColor !== lastPos.backgroundColor ||
- newPos.opacity !== lastPos.opacity ||
- newPos.x !== lastPos.x ||
- newPos.y !== lastPos.y ||
- newPos.z !== lastPos.z ||
- newPos.rotation !== lastPos.rotation ||
- newPos.zIndex !== lastPos.zIndex ||
- newPos.transition !== lastPos.transition ||
- newPos.pointerEvents !== lastPos.pointerEvents
- ) {
- this._layoutPoolData.set(entry[0], newPos);
- }
- if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
- this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
- }
- }
- this._cachedPool.clear();
- Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
const elements = computedElementData.slice();
Array.from(newPool.entries())
.filter(entry => this.isCurrent(entry[1].pair.layout))
- .forEach((entry, i) => {
- const childData: ViewDefBounds = this.childDataProvider(entry[1].pair.layout, entry[1].replica);
- const childSize = this.childSizeProvider(entry[1].pair.layout, entry[1].replica);
+ .forEach(entry =>
elements.push({
ele: this.getChildDocView(entry[1]),
- bounds: childData.opacity === 0 ? { ...childData, width: 0, height: 0 } : { ...childData, width: childSize.width, height: childSize.height },
+ bounds: (entry[1].opacity === 0 ? { ...entry[1], width: 0, height: 0 } : { ...entry[1] }) as any,
inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
- });
- });
+ })
+ );
this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
return elements;
@@ -1580,12 +1537,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
{ fireImmediately: true }
);
- this._disposers.layoutComputation = reaction(
- () => this.doInternalLayoutComputation,
- ({ newPool, computedElementData }) => (this._layoutElements = this.doLayoutComputation(newPool, computedElementData)),
- { fireImmediately: true, name: 'layoutComputationReaction' }
- );
-
this._disposers.active = reaction(
() => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
@@ -1752,7 +1703,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return;
}
!Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
- appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
+ appearanceItems.push({ description: `Pin View`, event: () => this.props.pinToPres(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
!Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
this.props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
@@ -1799,13 +1750,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SnappingManager.clearSnapLines();
};
@action
- dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean, visited = new Set<Doc>()) => {
+ dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => {
if (visited.has(this.rootDoc)) return;
visited.add(this.rootDoc);
showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup));
- if (this.rootDoc._isGroup && this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
- this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.dragStarting(snapToDraggedDoc, false, visited);
- }
const activeDocs = this.getActiveDocuments();
const size = this.screenToLocalXf.transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
@@ -1813,13 +1761,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
- activeDocs.forEach(
- doc =>
- doc._isGroup &&
- SnappingManager.GetIsResizing() !== doc &&
- !DragManager.docsBeingDragged.includes(doc) &&
- (DocumentManager.Instance.getDocumentView(doc)?.ComponentView as CollectionFreeFormView)?.dragStarting(snapToDraggedDoc, false, visited)
- );
+ activeDocs
+ .filter(doc => doc._isGroup && SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))
+ .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
const horizLines: number[] = [];
const vertLines: number[] = [];
@@ -1850,10 +1794,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
});
- get children() {
- this.incrementalRender();
- const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : this.props.children ? [this.props.children] : [];
- return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />];
+ // if a freeform view has any children, then the children will likely consist of a single child
+ // which will be a DocumentView. In this sitation, this freeform views acts as an annotation overlay for
+ // the underlying DocumentView and will pan and scoll with the underlying Documen tView.
+ @computed get underlayViews() {
+ return this.props.children ? [this.props.children] : [];
}
@computed get placeholder() {
@@ -1889,6 +1834,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
@computed get pannableContents() {
+ this.incrementalRender();
return (
<CollectionFreeFormPannableContents
rootDoc={this.rootDoc}
@@ -1897,7 +1843,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
transform={this.PanZoomCenterXf}
transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
viewDefDivClick={this.props.viewDefDivClick}>
- {this.children}
+ {this.underlayViews}
+ {this.contentViews}
+ <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
</CollectionFreeFormPannableContents>
);
}
@@ -2048,11 +1996,11 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
if (!focused) {
const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
let containers = dv.props.docViewPath();
- let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ let parFfview = dv.CollectionFreeFormView;
for (var cont of containers) {
- parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ parFfview = parFfview ?? cont.CollectionFreeFormView;
}
- while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
ffview?.zoomSmoothlyAboutPt(ffview.screenToLocalXf.transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
Doc.linkFollowHighlight(dv?.props.Document, false);
@@ -2084,8 +2032,8 @@ ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
);
});
ScriptingGlobals.add(function bringToFront() {
- SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc));
+ SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc));
});
ScriptingGlobals.add(function sendToBack(doc: Doc) {
- SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true));
+ SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc, true));
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index f831478a7..23f9bc69e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -22,7 +22,6 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
import { SubCollectionViewProps } from '../CollectionSubView';
-import { TabDocView } from '../TabDocView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
import React = require('react');
@@ -212,14 +211,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// if (this.props.pointerEvents?.() === 'none') return;
this._downX = this._lastX = e.clientX;
this._downY = this._lastY = e.clientY;
+ const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
+
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
// allow marquee
if (
e.button === 2 || //if right drag
- e.altKey || // or alt drag
- e.metaKey || // or alt drag
- Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan // or pan mode
+ e.metaKey || // or meta drag
+ scrollMode === freeformScrollMode.Pan // or pan mode
) {
// if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
this.setPreviewCursor(e.clientX, e.clientY, true, false, this.props.Document);
@@ -403,7 +403,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@undoBatch
@action
pinWithView = () => {
- TabDocView.PinDoc(this.props.Document, { pinViewport: this.Bounds });
+ this.props.pinToPres(this.props.Document, { pinViewport: this.Bounds });
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
};