aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx215
1 files changed, 159 insertions, 56 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8af048d67..1033050b9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -7,7 +7,7 @@ import { Id } from "../../../../fields/FieldSymbols";
import { InkData, InkField, InkTool } from "../../../../fields/InkField";
import { List } from "../../../../fields/List";
import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, makeInterface } from "../../../../fields/Schema";
+import { createSchema, makeInterface, listSpec } from "../../../../fields/Schema";
import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { TraceMobx } from "../../../../fields/util";
@@ -33,7 +33,7 @@ import { ContextMenu } from "../../ContextMenu";
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentViewProps, DocAfterFocusFunc } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import { PresBox } from "../../nodes/PresBox";
@@ -46,6 +46,7 @@ import "./CollectionFreeFormView.scss";
import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
export const panZoomSchema = createSchema({
_panX: "number",
@@ -88,6 +89,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
private _clusterDistance: number = 75;
private _hitCluster = false;
private _layoutComputeReaction: IReactionDisposer | undefined;
+ private _boundsReaction: IReactionDisposer | undefined;
private _layoutPoolData = new ObservableMap<string, PoolData>();
private _layoutSizeData = new ObservableMap<string, { width?: number, height?: number }>();
private _cachedPool: Map<string, PoolData> = new Map();
@@ -105,27 +107,31 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; }
@computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
- @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
+ @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent ? this.props.ContentScaling() : 1; }
@computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); }
- @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth)); }
- @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight)); }
+ @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document)); }
+ @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document)); }
private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0;
- private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0;
- private zoomScaling = () => (this.fitToContentScaling / this.parentScaling) * (this.fitToContent ?
- Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y),
- this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) :
- NumCast(this.Document[this.scaleFieldKey], 1))
-
+ private panX = () => this.fitToContent && !this.props.isAnnotationOverlay ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0;
+ private panY = () => this.fitToContent && !this.props.isAnnotationOverlay ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0;
+ private zoomScaling = () => {
+ const mult = this.fitToContentScaling / this.parentScaling;
+ if (this.fitToContent) {
+ const zs = !this.childDocs.length ? 1 :
+ Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x));
+ return mult * zs;
+ }
+ return mult * NumCast(this.Document[this.scaleFieldKey], 1);
+ }
@computed get cachedCenteringShiftX(): number {
const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
- return !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / scaling : 0; // shift so pan position is at center of window for non-overlay collections
+ return !this.props.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / scaling : 0; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
- return !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / scaling : 0;// shift so pan position is at center of window for non-overlay collections
+ return !this.props.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / scaling : 0;// shift so pan position is at center of window for non-overlay collections
}
@computed get cachedGetLocalTransform(): Transform {
return Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
@@ -163,14 +169,23 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (newBox.activeFrame !== undefined) {
const x = newBox.x;
const y = newBox.y;
+ const w = newBox._width;
+ const h = newBox._height;
delete newBox["x-indexed"];
delete newBox["y-indexed"];
+ delete newBox["w-indexed"];
+ delete newBox["h-indexed"];
delete newBox["opacity-indexed"];
+ delete newBox._width;
+ delete newBox._height;
delete newBox.x;
delete newBox.y;
+ delete newBox.opacity;
delete newBox.activeFrame;
newBox.x = x;
newBox.y = y;
+ newBox._width = w;
+ newBox._height = h;
}
}
if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
@@ -197,13 +212,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
if (!super.onInternalDrop(e, de)) return false;
+ const refDoc = docDragData.droppedDocuments[0];
const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y);
- const z = NumCast(docDragData.droppedDocuments[0].z);
+ const z = NumCast(refDoc.z);
const x = (z ? xpo : xp) - docDragData.offset[0];
const y = (z ? ypo : yp) - docDragData.offset[1];
const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
- const dropPos = [NumCast(docDragData.droppedDocuments[0].x), NumCast(docDragData.droppedDocuments[0].y)];
+ const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
+ const dropPos = this.Document._currentFrame !== undefined ? [dvals.x, dvals.y] : [NumCast(refDoc.x), NumCast(refDoc.y)];
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
@@ -214,7 +231,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
}
- const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
+ const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
layoutDoc._width = NumCast(layoutDoc._width, 300);
layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
!d._isBackground && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
@@ -776,15 +793,19 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
- if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
- deltaScale = 1 / this.zoomScaling();
- }
if (deltaScale < 0) deltaScale = -deltaScale;
const [x, y] = this.getTransform().transformPoint(pointX, pointY);
+ const invTransform = this.getLocalTransform().inverse();
+ if (deltaScale * invTransform.Scale > 20) {
+ deltaScale = 20 / invTransform.Scale;
+ }
+ if (deltaScale * invTransform.Scale < 1 && this.isAnnotationOverlay) {
+ deltaScale = 1 / invTransform.Scale;
+ }
const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
if (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling()) {
- const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+ const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 20);
this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
}
@@ -792,7 +813,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay || this.props.Document.treeViewOutlineMode) return;
+ if (this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
}
@@ -828,7 +849,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
}
}
- if (!this.layoutDoc._lockedTransform || this.Document.inOverlay) {
+ if (!this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.Document)) {
this.Document._viewTransition = panType;
const scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
@@ -866,7 +887,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1];
}
- focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => {
+ focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, didFocus?: boolean) => {
const state = HistoryUtil.getState();
// TODO This technically isn't correct if type !== "doc", as
@@ -885,48 +906,72 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
SelectionManager.DeselectAll();
if (this.props.Document.scrollHeight) {
const annotOn = Cast(doc.annotationOn, Doc) as Doc;
+ let delay = 1000;
if (!annotOn) {
- this.props.focus(doc);
+ !dontCenter && this.props.focus(doc);
+ afterFocus && setTimeout(afterFocus, delay);
} else {
const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height);
- const offset = annotOn && (contextHgt / 2);
- this.props.Document._scrollY = NumCast(doc.y) - offset;
+ const curScroll = NumCast(this.props.Document._scrollTop);
+ let scrollTo = curScroll;
+ if (curScroll + contextHgt < NumCast(doc.y)) {
+ scrollTo = NumCast(doc.y) + Math.max(NumCast(doc._height), 100) - contextHgt;
+ } else if (curScroll > NumCast(doc.y)) {
+ scrollTo = Math.max(0, NumCast(doc.y) - 50);
+ }
+ if (curScroll !== scrollTo || this.props.Document._viewTransition) {
+ this.props.Document._scrollPreviewY = this.props.Document._scrollY = scrollTo;
+ delay = Math.abs(scrollTo - curScroll) > 5 ? 1000 : 0;
+ !dontCenter && this.props.focus(this.props.Document);
+ afterFocus && setTimeout(afterFocus, delay);
+ } else {
+ !dontCenter && delay && this.props.focus(this.props.Document);
+ afterFocus?.(!dontCenter && delay ? true : false);
+
+ }
}
- afterFocus && setTimeout(afterFocus, 1000);
} else {
const layoutdoc = Doc.Layout(doc);
- const newPanX = NumCast(doc.x) + NumCast(layoutdoc._width) / 2;
- const newPanY = NumCast(doc.y) + NumCast(layoutdoc._height) / 2;
+ const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
+
+ willZoom && this.setScaleToZoom(layoutdoc, scale);
+ const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeWidth(this.props.Document)) / 2 / this.zoomScaling() : 0);
+ const newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeHeight(this.props.Document)) / 2 / this.zoomScaling() : 0);
const newState = HistoryUtil.getState();
newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
- const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
-
- if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
+ if (DocListCast(this.dataDoc[this.props.annotationsKey || this.props.fieldKey]).includes(doc)) {
// glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
}
Doc.BrushDoc(this.props.Document);
- this.props.focus(this.props.Document);
- willZoom && this.setScaleToZoom(layoutdoc, scale);
- Doc.linkFollowHighlight(doc);
- afterFocus && setTimeout(() => {
- if (afterFocus?.()) {
- this.Document._panX = savedState.px;
- this.Document._panY = savedState.py;
- this.Document[this.scaleFieldKey] = savedState.s;
- this.Document._viewTransition = savedState.pt;
- }
- }, 500);
+ const newDidFocus = didFocus || (newPanX !== savedState.px || newPanY !== savedState.py);
+
+ const newAfterFocus = (didFocus: boolean) => {
+ afterFocus && setTimeout(() => {
+ // @ts-ignore
+ if (afterFocus?.(didFocus || (newPanX !== savedState.px || newPanY !== savedState.py))) {
+ this.Document._panX = savedState.px;
+ this.Document._panY = savedState.py;
+ this.Document[this.scaleFieldKey] = savedState.s;
+ this.Document._viewTransition = savedState.pt;
+ }
+ }, newPanX !== savedState.px || newPanY !== savedState.py ? 500 : 0);
+ return false;
+ };
+ this.props.focus(this.props.Document, undefined, undefined, newAfterFocus, undefined, newDidFocus);
+ Doc.linkFollowHighlight(doc);
}
}
setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
- this.Document[this.scaleFieldKey] = scale * Math.min(this.props.PanelWidth() / NumCast(doc._width), this.props.PanelHeight() / NumCast(doc._height));
+ const pw = this.isAnnotationOverlay ? Doc.NativeWidth(this.props.Document) : this.props.PanelWidth();
+ const ph = this.isAnnotationOverlay ? Doc.NativeHeight(this.props.Document) : this.props.PanelHeight();
+ pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height)));
}
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
@@ -949,7 +994,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate,
LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString,
FreezeDimensions: this.props.freezeChildDimensions,
- layoutKey: StrCast(this.props.Document.childLayoutKey),
setupDragLines: this.setupDragLines,
dontRegisterView: this.props.dontRegisterView,
rootSelected: childData ? this.rootSelected : returnFalse,
@@ -964,6 +1008,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.Document,
docFilters: this.docFilters,
+ docRangeFilters: this.docRangeFilters,
searchFilterDocs: this.searchFilterDocs,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
@@ -1105,7 +1150,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
});
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
- const elements: ViewDefResult[] = computedElementData.slice();
+ const elements = computedElementData.slice();
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
elements.push({
@@ -1126,8 +1171,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
}));
- if (this.props.isAnnotationOverlay) {
- this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
+ if (this.props.isAnnotationOverlay) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
+ if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, NumCast(this.props.Document[this.scaleFieldKey], 1));
+ else this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
}
this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
@@ -1140,12 +1186,22 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
(elements) => this._layoutElements = elements || [],
{ fireImmediately: true, name: "doLayout" });
+ if (!this.props.annotationsKey) {
+ this._boundsReaction = reaction(() => this.contentBounds,
+ bounds => (!this.fitToContent && this._layoutElements?.length) && setTimeout(() => {
+ const rbounds = Cast(this.Document._renderContentBounds, listSpec("number"), [0, 0, 0, 0]);
+ if (rbounds[0] !== bounds.x || rbounds[1] !== bounds.y || rbounds[2] !== bounds.r || rbounds[3] !== bounds.b) {
+ this.Document._renderContentBounds = new List<number>([bounds.x, bounds.y, bounds.r, bounds.b]);
+ }
+ }));
+ }
this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
}
componentWillUnmount() {
this._layoutComputeReaction?.();
+ this._boundsReaction?.();
this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
}
@@ -1154,7 +1210,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onCursorMove = (e: React.PointerEvent) => {
- super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
+ // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
}
@@ -1165,7 +1221,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
(e as any).handlePan = true;
- if (!this.props.Document._noAutoscroll && this._marqueeRef?.current) {
+ if (!this.props.Document._noAutoscroll && !this.props.renderDepth && this._marqueeRef?.current) {
const dragX = e.detail.clientX;
const dragY = e.detail.clientY;
const bounds = this._marqueeRef.current?.getBoundingClientRect();
@@ -1255,7 +1311,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
if (!Doc.UserDoc().noviceMode) {
- optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
+ optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" });
}
@@ -1381,6 +1437,44 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
return false;
});
+
+ chooseGridSpace = (gridSpace: number): number => {
+ const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3;
+ return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
+ }
+
+ @computed get grid() {
+ const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-spacing"], 50));
+ const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.panX() % gridSpace - gridSpace) * this.zoomScaling();
+ const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.panY() % gridSpace - gridSpace) * this.zoomScaling();
+ const renderGridSpace = gridSpace * this.zoomScaling();
+ const w = this.props.PanelWidth() + 2 * renderGridSpace;
+ const h = this.props.PanelHeight() + 2 * renderGridSpace;
+ return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+ ref={(el) => {
+ const ctx = el?.getContext('2d');
+ if (ctx) {
+ const Cx = this.centeringShiftX() % renderGridSpace;
+ const Cy = this.centeringShiftY() % renderGridSpace;
+ ctx.lineWidth = Math.min(1, Math.max(0.5, this.zoomScaling()));
+ ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
+ ctx.clearRect(0, 0, w, h);
+ if (ctx) {
+ ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
+ ctx.beginPath();
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
+ ctx.moveTo(x, Cy - h);
+ ctx.lineTo(x, Cy + h);
+ }
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.moveTo(Cx - w, y);
+ ctx.lineTo(Cx + w, y);
+ }
+ ctx.stroke();
+ }
+ }
+ }} />;
+ }
@computed get marqueeView() {
return <MarqueeView
{...this.props}
@@ -1394,6 +1488,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
<div ref={this._marqueeRef}>
+ {this.layoutDoc["_backgroundGrid-show"] ? this.grid : (null)}
<CollectionFreeFormViewPannableContents
centeringShiftX={this.centeringShiftX}
centeringShiftY={this.centeringShiftY}
@@ -1410,19 +1505,20 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
</MarqueeView>;
}
+
@computed get contentScaling() {
if (this.props.annotationsKey && !this.props.forceScaling) return 0;
- const nw = returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth));
- const nh = returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight));
+ const nw = returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document));
+ const nh = returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document));
const hscale = nh ? this.props.PanelHeight() / nh : 1;
const wscale = nw ? this.props.PanelWidth() / nw : 1;
return wscale < hscale ? wscale : hscale;
}
@computed get backgroundEvents() { return this.layoutDoc._isBackground && SnappingManager.GetIsDragging(); }
+
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
- !this.fitToContent && this._layoutElements?.length && setTimeout(() => this.Document._renderContentBounds = new List<number>([this.contentBounds.x, this.contentBounds.y, this.contentBounds.r, this.contentBounds.b]), 0);
return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
onPointerOver={this.onPointerOver}
onWheel={this.onPointerWheel}
@@ -1582,7 +1678,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
}
@computed get zoomProgressivize() {
- return PresBox.Instance && PresBox.Instance.activeItem && PresBox.Instance.activeItem.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
+ return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
}
@computed get progressivize() {
@@ -1613,6 +1709,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
</>;
}
+
render() {
// trace();
const freeformclass = "collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : "-none");
@@ -1622,6 +1719,12 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const pany = -this.props.panY();
const zoom = this.props.zoomScaling();
return <div className={freeformclass}
+ onScroll={e => {
+ const target = e.target as any;
+ if (getComputedStyle(target)?.overflow === "visible") { // if collection is visible, then scrolling will mess things up since there are no scroll bars
+ target.scrollTop = target.scrollLeft = 0;
+ }
+ }}
style={{
transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`,
transition: this.props.transition,