aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2023-10-19 00:58:50 -0400
committerSophie Zhang <sophie_zhang@brown.edu>2023-10-19 00:58:50 -0400
commit1efba5a6a80cba09633426fc8cb42be4bf9b2e74 (patch)
treeabd78b4818115b300fb934e343f41417ac13e19f /src/client/views/collections/collectionFreeForm
parent612f3d05927113c7b010861c17765fcead4752e5 (diff)
parentabf40af6dd617de6486a97e8b5f276db232119ed (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx56
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx9
3 files changed, 43 insertions, 23 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 15b6e1d37..c1c01eacb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -6,7 +6,6 @@ import { RefField } from '../../../../fields/RefField';
import { listSpec } from '../../../../fields/Schema';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { aggregateBounds } from '../../../../Utils';
-import { ColorScheme } from '../../../util/SettingsManager';
import React = require('react');
export interface ViewDefBounds {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 676e96714..bfc61f601 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
import { Bezier } from 'bezier-js';
import { Colors } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { DateField } from '../../../../fields/DateField';
@@ -59,7 +59,7 @@ export type collectionFreeformViewProps = {
originTopLeft?: boolean;
annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
viewDefDivClick?: ScriptField;
- childPointerEvents?: string;
+ childPointerEvents?: () => string | undefined;
viewField?: string;
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
engineProps?: any;
@@ -109,6 +109,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private get panYFieldKey() {
return (this.props.viewField ?? '') + '_freeform_panY';
}
+ private get autoResetFieldKey() {
+ return (this.props.viewField ?? '') + '_freeform_autoReset';
+ }
private get borderWidth() {
return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
}
@@ -1082,7 +1085,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case freeformScrollMode.Pan:
// if ctrl is selected then zoom
if (!e.ctrlKey && this.props.isContentActive(true)) {
- this.scrollPan({ deltaX: -e.deltaX, deltaY: e.shiftKey ? 0 : -Math.max(-1, Math.min(1, e.deltaY)) });
+ this.scrollPan({ deltaX: -e.deltaX * this.getTransform().Scale, deltaY: e.shiftKey ? 0 : -e.deltaY * this.getTransform().Scale });
break;
}
default:
@@ -1292,7 +1295,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
const pointerEvents = DocumentView.Interacting
? 'none'
- : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
+ : this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
return pointerEvents;
}
pointerEvents = () => this._pointerEvents;
@@ -1508,9 +1511,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@observable _numLoaded = 1;
+ _lastPoolSize = 0;
get doLayoutComputation() {
const { newPool, computedElementData } = this.doInternalLayoutComputation;
const array = Array.from(newPool.entries());
+ let somethingChanged = array.length !== this._lastPoolSize;
+ this._lastPoolSize = array.length;
runInAction(() => {
for (const entry of array) {
const lastPos = this._cachedPool.get(entry[0]); // last computed pos
@@ -1528,12 +1534,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newPos.transition !== lastPos.transition
) {
this._layoutPoolData.set(entry[0], newPos);
+ somethingChanged = true;
}
if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
+ somethingChanged = true;
}
}
});
+ if (!somethingChanged) return undefined;
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
const elements = computedElementData.slice();
@@ -1609,10 +1618,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.layoutComputation = reaction(
() => this.doLayoutComputation,
- elements => (this._layoutElements = elements || []),
+ elements => {
+ if (elements !== undefined) this._layoutElements = elements || [];
+ },
{ fireImmediately: true, name: 'doLayout' }
);
+ this._disposers.active = reaction(
+ () => this.isContentActive(),
+ active => this.rootDoc[this.autoResetFieldKey] && !active && this.resetView()
+ );
+
this._disposers.fitContent = reaction(
() => this.rootDoc.fitContentOnce,
fitContentOnce => {
@@ -1768,9 +1784,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
///
@undoBatch
resetView = () => {
- if (!this.props.Document._isGroup) {
- this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0;
- this.props.Document[this.scaleFieldKey] = 1;
+ this.rootDoc[this.panXFieldKey] = NumCast(this.rootDoc[this.panXFieldKey + '_reset']);
+ this.props.Document[this.panYFieldKey] = NumCast(this.rootDoc[this.panYFieldKey + '_reset']);
+ this.rootDoc[this.scaleFieldKey] = NumCast(this.rootDoc[this.scaleFieldKey + '_reset'], 1);
+ };
+ ///
+ /// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS
+ /// the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
+ ///
+ @undoBatch
+ toggleResetView = () => {
+ this.rootDoc[this.autoResetFieldKey] = !this.rootDoc[this.autoResetFieldKey];
+ if (this.rootDoc[this.autoResetFieldKey]) {
+ this.rootDoc[this.panXFieldKey + '_reset'] = this.rootDoc[this.panXFieldKey];
+ this.rootDoc[this.panYFieldKey + '_reset'] = this.rootDoc[this.panYFieldKey];
+ this.rootDoc[this.scaleFieldKey + '_reset'] = this.rootDoc[this.scaleFieldKey];
}
};
@@ -1780,6 +1808,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
!this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+ !this.props.Document._isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
!Doc.noviceMode &&
appearanceItems.push({
description: 'Toggle auto arrange',
@@ -1816,14 +1845,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!Doc.noviceMode &&
optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
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' });
- this.props.renderDepth &&
- optionItems.push({
- description: 'Fit Content Once',
- event: () => {
- this.fitContentOnce();
- },
- icon: 'object-group',
- });
+ this.props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
if (!Doc.noviceMode) {
optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
}
@@ -1917,7 +1939,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
<MarqueeView
{...this.props}
ref={this._marqueeViewRef}
- ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
+ ungroup={this.rootDoc._isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
slowLoadDocuments={this.slowLoadDocuments}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4c502021d..5c053fefc 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -6,10 +6,9 @@ import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkField, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
-import { distributeAcls, GetEffectiveAcl, SharingPermissions } from '../../../../fields/util';
+import { GetEffectiveAcl } from '../../../../fields/util';
import { intersectRect, lightOrDark, returnFalse, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
@@ -24,10 +23,10 @@ import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
import { SubCollectionViewProps } from '../CollectionSubView';
import { TabDocView } from '../TabDocView';
-import { TreeView } from '../TreeView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
import React = require('react');
+import { freeformScrollMode } from '../../../util/SettingsManager';
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -223,7 +222,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
// allow marquee if right click OR alt+left click OR in adding presentation slide & left key drag mode
- if (e.button === 2 || (e.button === 0 && e.altKey)) {
+ if (e.button === 2 || (e.button === 0 && (e.altKey || (this.props.isContentActive?.(true) && Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan)))) {
// if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
this.setPreviewCursor(e.clientX, e.clientY, true, false, this.props.Document);
// (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.