diff options
| author | Sophie Zhang <sophie_zhang@brown.edu> | 2023-10-19 00:58:50 -0400 |
|---|---|---|
| committer | Sophie Zhang <sophie_zhang@brown.edu> | 2023-10-19 00:58:50 -0400 |
| commit | 1efba5a6a80cba09633426fc8cb42be4bf9b2e74 (patch) | |
| tree | abd78b4818115b300fb934e343f41417ac13e19f /src/client/views/collections/collectionFreeForm | |
| parent | 612f3d05927113c7b010861c17765fcead4752e5 (diff) | |
| parent | abf40af6dd617de6486a97e8b5f276db232119ed (diff) | |
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
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. |
