aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DocumentDecorations.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-05-22 11:25:32 -0400
committerbobzel <zzzman@gmail.com>2023-05-22 11:25:32 -0400
commitbed3309e1fda6597b2a8fea10ad82cd3a0402051 (patch)
treefe599bbdc5fca2c221e1e0f7a60995b7cd39f870 /src/client/views/DocumentDecorations.tsx
parent887a4f7e0fc25fde87b20a5de2e7b0aee561cc78 (diff)
parent3d26d5b2654841a9b92f3d66b28d1dc8e36cca6a (diff)
merged physics with master
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
-rw-r--r--src/client/views/DocumentDecorations.tsx332
1 files changed, 176 insertions, 156 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d1f0bf2ac..72c6d449a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,18 +7,20 @@ import { observer } from 'mobx-react';
import { FaUndo } from 'react-icons/fa';
import { DateField } from '../../fields/DateField';
import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
-import { Document } from '../../fields/documentSchemas';
import { InkField } from '../../fields/InkField';
+import { RichTextField } from '../../fields/RichTextField';
import { ScriptField } from '../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
+import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
import { Docs } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
+import { DocumentManager } from '../util/DocumentManager';
import { DragManager } from '../util/DragManager';
+import { LinkFollower } from '../util/LinkFollower';
import { SelectionManager } from '../util/SelectionManager';
import { SnappingManager } from '../util/SnappingManager';
-import { undoBatch, UndoManager } from '../util/UndoManager';
+import { UndoManager } from '../util/UndoManager';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { DocumentButtonBar } from './DocumentButtonBar';
@@ -31,6 +33,7 @@ import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { ImageBox } from './nodes/ImageBox';
import React = require('react');
+import _ = require('lodash');
@observer
export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -66,14 +69,25 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
DocumentDecorations.Instance = this;
reaction(
() => SelectionManager.Views().slice(),
- action(docs => (this._editingTitle = false))
+ action(docs => {
+ this._showNothing = !DocumentView.LongPress && docs.length === 1; // show decorations if multiple docs are selected or we're long pressing
+ this._editingTitle = false;
+ })
+ );
+ document.addEventListener(
+ // show decorations whenever pointer moves outside of selection bounds.
+ 'pointermove',
+ action(e => {
+ if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) {
+ this._showNothing = false;
+ }
+ })
);
}
- @observable overrideBounds = false;
@computed
get Bounds() {
- if (this.overrideBounds) return { x: 0, y: 0, r: 0, b: 0 };
+ if (LinkFollower.IsFollowing) return { x: 0, y: 0, r: 0, b: 0 };
const views = SelectionManager.Views();
return views
.filter(dv => dv.props.renderDepth > 0)
@@ -105,7 +119,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
titleFieldKey &&
SelectionManager.Views().forEach(d => {
if (titleFieldKey === 'title') {
- d.dataDoc['title-custom'] = !this._accumulatedTitle.startsWith('-');
+ d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
Doc.RemoveDocFromList(Doc.MyPublishedDocs, undefined, d.rootDoc);
}
@@ -115,22 +129,26 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
//@ts-ignore
const titleField = +this._accumulatedTitle == this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle;
- Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
- if (d.rootDoc.syncLayoutFieldWithTitle) {
- const title = titleField.toString();
+ if (titleField.toString().startsWith('<this>')) {
+ const title = titleField.toString().replace(/<this>\.?/, '');
const curKey = Doc.LayoutFieldKey(d.rootDoc);
- if (curKey !== title && d.dataDoc[title] === undefined) {
- d.rootDoc.layout = FormattedTextBox.LayoutString(title);
- setTimeout(() => {
- const val = d.dataDoc[curKey];
- d.dataDoc[curKey] = undefined;
- d.dataDoc[title] = val;
- });
+ if (curKey !== title) {
+ if (title) {
+ if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') {
+ d.rootDoc.layout_fieldKey = `layout_${title}`;
+ d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title);
+ d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0;
+ }
+ } else {
+ d.rootDoc.layout_fieldKey = undefined;
+ }
}
+ } else {
+ Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
}
}),
- 'title blur'
+ 'edit title'
);
}
};
@@ -171,7 +189,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@action
onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
const dragDocView = SelectionManager.Views()[0];
- if (DocListCast(Doc.MyOverlayDocs.data).includes(dragDocView.rootDoc)) return false;
+ const containers = new Set<Doc | undefined>();
+ SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer)));
+ if (containers.size > 1) return false;
const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 };
const dragData = new DragManager.DocumentDragData(
SelectionManager.Views().map(dv => dv.props.Document),
@@ -182,7 +202,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
dragData.removeDocument = dragDocView.props.removeDocument;
dragData.isDocDecorationMove = true;
dragData.canEmbed = dragTitle;
- this._hidden = this.Interacting = true;
+ this._hidden = true;
DragManager.StartDocumentDrag(
SelectionManager.Views().map(dv => dv.ContentDiv!),
dragData,
@@ -191,7 +211,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
{
dragComplete: action(e => {
dragData.canEmbed && SelectionManager.DeselectAll();
- this._hidden = this.Interacting = false;
+ this._hidden = false;
}),
hideSource: true,
}
@@ -207,11 +227,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
.filter(v => v && v.props.renderDepth > 0);
if (forceDeleteOrIconify === false && this._iconifyBatch) return;
this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
- if (!this._iconifyBatch) {
- this._iconifyBatch = UndoManager.StartBatch('iconifying');
- } else {
- forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
- }
var iconifyingCount = views.length;
const finished = action((force?: boolean) => {
if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
@@ -230,6 +245,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
this._iconifyBatch = undefined;
}
});
+ if (!this._iconifyBatch) {
+ this._iconifyBatch = UndoManager.StartBatch(forceDeleteOrIconify ? 'delete selected docs' : 'iconifying');
+ } else {
+ forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
+ }
+
if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
};
@@ -252,28 +273,28 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const selectedDocs = SelectionManager.Views();
if (selectedDocs.length) {
if (e.ctrlKey) {
- // open an alias in a new tab with Ctrl Key
- CollectionDockingView.AddSplit(Doc.BestAlias(selectedDocs[0].props.Document), OpenWhereMod.right);
+ // open an embedding in a new tab with Ctrl Key
+ CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].rootDoc), OpenWhereMod.right);
} else if (e.shiftKey) {
// open centered in a new workspace with Shift Key
- const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
- alias.context = undefined;
- alias.x = -alias[WidthSym]() / 2;
- alias.y = -alias[HeightSym]() / 2;
- CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: 'Tab for ' + alias.title }), OpenWhereMod.right);
+ const embedding = Doc.MakeEmbedding(selectedDocs[0].rootDoc);
+ embedding.embedContainer = undefined;
+ embedding.x = -embedding[WidthSym]() / 2;
+ embedding.y = -embedding[HeightSym]() / 2;
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right);
} else if (e.altKey) {
// open same document in new tab
- CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, OpenWhereMod.right);
+ CollectionDockingView.ToggleSplit(selectedDocs[0].rootDoc, OpenWhereMod.right);
} else {
- var openDoc = selectedDocs[0].props.Document;
- if (openDoc.layoutKey === 'layout_icon') {
- openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+ var openDoc = selectedDocs[0].rootDoc;
+ if (openDoc.layout_fieldKey === 'layout_icon') {
+ openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
Doc.deiconifyView(openDoc);
}
LightboxView.SetLightboxDoc(
openDoc,
undefined,
- selectedDocs.slice(1).map(view => view.props.Document)
+ selectedDocs.slice(1).map(view => view.rootDoc)
);
}
}
@@ -285,15 +306,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
SelectionManager.DeselectAll();
};
- onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
-
+ onSelectorClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false);
/**
* Handles setting up events when user clicks on the border radius editor
* @param e PointerEvent
*/
@action
onRadiusDown = (e: React.PointerEvent): void => {
- this._isRounding = true;
+ this._isRounding = DocumentDecorations.Instance.Interacting = true;
this._resizeUndo = UndoManager.StartBatch('DocDecs set radius');
// Call util move event function
setupMoveUpEvents(
@@ -316,10 +336,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
return false;
}, // moveEvent
action(e => {
- this._isRounding = false;
+ DocumentDecorations.Instance.Interacting = this._isRounding = false;
this._resizeUndo?.end();
}), // upEvent
- e => {} // clickEvent
+ e => {}, // clickEvent,
+ true
);
};
@@ -348,8 +369,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]);
const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2];
const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI);
- seldocview.rootDoc.rotateCenterX = final.x / NumCast(seldocview.layoutDoc._width);
- seldocview.rootDoc.rotateCenterY = final.y / NumCast(seldocview.layoutDoc._height);
+ seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width);
+ seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height);
};
@action
@@ -366,8 +387,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
action(action(() => (this._isRotating = false))), // upEvent
action((e, doubleTap) => {
if (doubleTap) {
- seldocview.rootDoc.rotateCenterX = 0.5;
- seldocview.rootDoc.rotateCenterY = 0.5;
+ seldocview.rootDoc.rotation_centerX = 0.5;
+ seldocview.rootDoc.rotation_centerY = 0.5;
}
})
);
@@ -377,7 +398,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
onRotateDown = (e: React.PointerEvent): void => {
this._isRotating = true;
const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] };
- const rotateUndo = UndoManager.StartBatch('rotatedown');
+ const rotateUndo = UndoManager.StartBatch('drag rotation');
const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
const centerPoint = this.rotCenter.slice();
const infos = new Map<Doc, { unrotatedDocPos: { x: number; y: number }; startRotCtr: { x: number; y: number }; accumRot: number }>();
@@ -439,24 +460,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@action
onPointerDown = (e: React.PointerEvent): void => {
- const views = SelectionManager.Views().map(dv => dv.rootDoc);
- this._inkDragDocs = views
- .filter(doc => doc.type === DocumentType.INK)
- .map(doc => {
- if (InkStrokeProperties.Instance._lock) {
- Doc.SetNativeHeight(doc, NumCast(doc._height));
- Doc.SetNativeWidth(doc, NumCast(doc._width));
- }
- return { doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) };
- });
-
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
this._resizeHdlId = e.currentTarget.className;
const bounds = e.currentTarget.getBoundingClientRect();
this._offX = this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX;
this._offY = this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY;
- this._resizeUndo = UndoManager.StartBatch('DocDecs resize');
+ this._resizeUndo = UndoManager.StartBatch('drag resizing');
this._snapX = e.pageX;
this._snapY = e.pageY;
const ffviewSet = new Set<CollectionFreeFormView>();
@@ -473,10 +483,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
if (!first) return false;
let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
var fixedAspect = Doc.NativeAspect(first.layoutDoc);
- InkStrokeProperties.Instance._lock &&
- SelectionManager.Views()
- .filter(dv => dv.rootDoc.type === DocumentType.INK)
- .forEach(dv => (fixedAspect = Doc.NativeAspect(dv.rootDoc)));
const resizeHdl = this._resizeHdlId.split(' ')[0];
if (fixedAspect && (resizeHdl === 'documentDecorations-bottomRightResizer' || resizeHdl === 'documentDecorations-topLeftResizer')) {
@@ -505,73 +511,86 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
dragRight = false,
dragBotRight = false,
dragTop = false;
- let dX = 0,
- dY = 0,
- dW = 0,
- dH = 0;
+ let dXin = 0,
+ dYin = 0,
+ dWin = 0,
+ dHin = 0;
switch (this._resizeHdlId.split(' ')[0]) {
case '':
break;
case 'documentDecorations-topLeftResizer':
- dX = -1;
- dY = -1;
- dW = -move[0];
- dH = -move[1];
+ dXin = -1;
+ dYin = -1;
+ dWin = -move[0];
+ dHin = -move[1];
break;
case 'documentDecorations-topRightResizer':
- dW = move[0];
- dY = -1;
- dH = -move[1];
+ dWin = move[0];
+ dYin = -1;
+ dHin = -move[1];
break;
case 'documentDecorations-topResizer':
- dY = -1;
- dH = -move[1];
+ dYin = -1;
+ dHin = -move[1];
dragTop = true;
break;
case 'documentDecorations-bottomLeftResizer':
- dX = -1;
- dW = -move[0];
- dH = move[1];
+ dXin = -1;
+ dWin = -move[0];
+ dHin = move[1];
break;
case 'documentDecorations-bottomRightResizer':
- dW = move[0];
- dH = move[1];
+ dWin = move[0];
+ dHin = move[1];
dragBotRight = true;
break;
case 'documentDecorations-bottomResizer':
- dH = move[1];
+ dHin = move[1];
dragBottom = true;
break;
case 'documentDecorations-leftResizer':
- dX = -1;
- dW = -move[0];
+ dXin = -1;
+ dWin = -move[0];
break;
case 'documentDecorations-rightResizer':
- dW = move[0];
+ dWin = move[0];
dragRight = true;
break;
}
- SelectionManager.Views().forEach(
+ const isGroup = first.rootDoc._isGroup ? first.rootDoc : undefined;
+ const scaleViews = isGroup ? DocListCast(isGroup.data).map(doc => DocumentManager.Instance.getFirstDocumentView(doc)!) : SelectionManager.Views();
+ const aggBounds = aggregateBounds(scaleViews.map(view => view.rootDoc) as any, 0, 0);
+ const refWidth = aggBounds.r - aggBounds.x;
+ const refHeight = aggBounds.b - aggBounds.y;
+ const scaleRefPt = first.props
+ .ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(
+ NumCast(isGroup?._xPadding) + (dXin ? refWidth : 0), //
+ NumCast(isGroup?._yPadding) + (dYin ? refHeight : 0)
+ );
+ scaleViews.forEach(
action((docView: DocumentView) => {
if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
- if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
- const doc = Document(docView.rootDoc);
- if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight)) {
+ if (dXin !== 0 || dYin !== 0 || dWin !== 0 || dHin !== 0) {
+ const doc = docView.rootDoc;
+ const refCent = docView.props.ScreenToLocalTransform().transformPoint(scaleRefPt[0], scaleRefPt[1]);
+
+ if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight) && doc._nativeWidth !== undefined) {
doc._nativeHeight = (NumCast(doc._height) / NumCast(doc._width, 1)) * docView.nativeWidth;
}
const nwidth = docView.nativeWidth;
const nheight = docView.nativeHeight;
- let docheight = doc._height || 0;
- let docwidth = doc._width || 0;
- const width = docwidth;
- let height = docheight || (nheight / nwidth) * width;
- height = !height || isNaN(height) ? 20 : height;
+ const docwidth = NumCast(doc._width);
+ let docheight = (hgt => (!hgt || isNaN(hgt) ? 20 : hgt))(NumCast(doc._height) || (nheight / nwidth) * docwidth);
+ let dW = docwidth * (dWin / refWidth);
+ let dH = docheight * (dHin / refHeight);
const scale = docView.props.ScreenToLocalTransform().Scale;
- const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
+ const modifyNativeDim = (e.ctrlKey || doc.layout_forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
if (nwidth && nheight) {
- if (nwidth / nheight !== width / height && !dragBottom && !dragTop) {
- height = (nheight / nwidth) * width;
+ if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) {
+ docheight = (nheight / nwidth) * docwidth;
}
if (modifyNativeDim && !dragBottom && !dragTop) {
// ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
@@ -579,55 +598,64 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
else dW = (dH * nwidth) / nheight;
}
}
- let actualdW = Math.max(width + dW * scale, 20);
- let actualdH = Math.max(height + dH * scale, 20);
- const fixedAspect = nwidth && nheight && (!doc._fitWidth || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
+ let actualdW = Math.max(docwidth + dW * scale, 20);
+ let actualdH = Math.max(docheight + dH * scale, 20);
+ let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
+ let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
+ const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
+ const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
if (fixedAspect) {
if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
if (dragRight && modifyNativeDim) {
if (Doc.NativeWidth(doc)) {
- doc._nativeWidth = (actualdW / (doc._width || 1)) * Doc.NativeWidth(doc);
+ doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc);
}
} else {
- if (!doc._fitWidth) {
+ if (!doc._layout_fitWidth || preserveNativeDim) {
actualdH = (nheight / nwidth) * actualdW;
doc._height = actualdH;
- } else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
+ } else if (!modifyNativeDim || dragBotRight) {
+ doc._height = actualdH;
+ }
}
doc._width = actualdW;
} else {
- if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) {
- // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
- doc._nativeHeight = (actualdH / (doc._height || 1)) * Doc.NativeHeight(doc);
- doc._autoHeight = false;
+ if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._layout_fitWidth))) {
+ // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight
+ // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match
+ // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
+ doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc);
+ doc._layout_autoHeight = false;
} else {
- if (!doc._fitWidth) {
+ if (!doc._layout_fitWidth || preserveNativeDim) {
actualdW = (nwidth / nheight) * actualdH;
doc._width = actualdW;
- } else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
+ } else if (!modifyNativeDim || dragBotRight) {
+ doc._width = actualdW;
+ }
}
if (!modifyNativeDim) {
- actualdH = Math.min((nheight / nwidth) * NumCast(doc._width), actualdH);
- doc._height = actualdH;
- } else doc._height = actualdH;
+ actualdH = Math.min((nheight / nwidth) * docwidth, actualdH);
+ }
+ doc._height = actualdH;
}
} else {
- const rotCtr = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
+ const rotCtr = [docwidth / 2, docheight / 2];
const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
- const maxHeight = doc.nativHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+ const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling();
dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
dW && (doc._width = actualdW);
- dH && (doc._autoHeight = false);
+ dH && (doc._layout_autoHeight = false);
const rotCtr2 = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
const tlRotated2 = Utils.rotPt(-rotCtr2[0], -rotCtr2[1], (NumCast(doc._rotation) / 180) * Math.PI);
doc.x = NumCast(doc.x) + tlRotated.x + rotCtr[0] - (tlRotated2.x + rotCtr2[0]); // doc shifts by amount topleft moves because rotation is about center of doc
doc.y = NumCast(doc.y) + tlRotated.y + rotCtr[1] - (tlRotated2.y + rotCtr2[1]);
}
- doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
- doc.y = (doc.y || 0) + (dragBottom ? 0 : dY * (actualdH - docheight));
- doc._lastModified = new DateField();
+ doc.x = NumCast(doc.x) + dX;
+ doc.y = NumCast(doc.y) + dY;
+ doc._layout_modificationDate = new DateField();
}
const val = this._dragHeights.get(docView.layoutDoc);
if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
@@ -643,11 +671,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
this._resizeUndo?.end();
SnappingManager.clearSnapLines();
- // detect autoHeight gesture and apply
+ // detect layout_autoHeight gesture and apply
SelectionManager.Views()
.map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
.filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
- .forEach(pair => (pair.doc._autoHeight = true));
+ .forEach(pair => (pair.doc._layout_autoHeight = true));
//need to change points for resize, or else rotation/control points will fail.
this._inkDragDocs
.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
@@ -686,7 +714,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
@computed get hasIcons() {
- return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === 'layout_icon');
+ return SelectionManager.Views().some(docView => docView.rootDoc.layout_fieldKey === 'layout_icon');
}
@observable _showRotCenter = false;
@@ -695,8 +723,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
if (SelectionManager.Views().length) {
const seldocview = SelectionManager.Views()[0];
const loccenter = Utils.rotPt(
- NumCast(seldocview.rootDoc.rotateCenterX) * NumCast(seldocview.layoutDoc._width),
- NumCast(seldocview.rootDoc.rotateCenterY) * NumCast(seldocview.layoutDoc._height),
+ NumCast(seldocview.rootDoc.rotation_centerX) * NumCast(seldocview.layoutDoc._width),
+ NumCast(seldocview.rootDoc.rotation_centerY) * NumCast(seldocview.layoutDoc._height),
(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI
);
return seldocview.props
@@ -707,17 +735,20 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
return this._rotCenter;
}
+ @observable _showNothing = true;
+
render() {
const { b, r, x, y } = this.Bounds;
const bounds = { b, r, x, y };
- const seldocview = SelectionManager.Views().slice(-1)[0];
+ const seldocview = SelectionManager.Views().lastElement();
if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+ setTimeout(action(() => (this._showNothing = true)));
return null;
}
// hide the decorations if the parent chooses to hide it or if the document itself hides it
const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
- const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating;
- const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
+ const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+ const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
// if multiple documents have been opened at the same time, then don't show open button
const hideOpenButton =
@@ -734,26 +765,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
seldocview.props.hideDeleteButton ||
seldocview.rootDoc.hideDeleteButton ||
SelectionManager.Views().some(docView => {
- const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
- return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+ const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
+ return (docView.rootDoc.stayInCollection && !docView.rootDoc.timelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
});
const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
<Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
- <div
- className={`documentDecorations-${key}Button`}
- onContextMenu={e => e.preventDefault()}
- onPointerDown={
- pointerDown ??
- (e =>
- setupMoveUpEvents(
- this,
- e,
- returnFalse,
- emptyFunction,
- undoBatch(e => click!(e))
- ))
- }>
+ <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => click!(e)))}>
<FontAwesomeIcon icon={icon as any} />
</div>
</Tooltip>
@@ -771,12 +789,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const useLock = bounds.r - bounds.x > 135 && seldocview.props.CollectionFreeFormDocumentView;
const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.props.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
- const rotation = NumCast(seldocview.rootDoc._rotation);
+ const rotation = SelectionManager.Views().length == 1 ? NumCast(seldocview.rootDoc._rotation) : 0;
const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
// Radius constants
- const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox;
+ const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
@@ -809,7 +827,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
</div>
);
return (
- <div className={`documentDecorations${colorScheme}`}>
+ <div className={`documentDecorations${colorScheme}`} style={{ opacity: this._showNothing ? 0.1 : undefined }}>
<div
className="documentDecorations-background"
style={{
@@ -818,7 +836,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2,
pointerEvents: DocumentDecorations.Instance.AddToSelection || this.Interacting ? 'none' : 'all',
- display: SelectionManager.Views().length <= 1 ? 'none' : undefined,
+ display: SelectionManager.Views().length <= 1 || hideDecorations ? 'none' : undefined,
}}
onPointerDown={this.onBackgroundDown}
onContextMenu={e => {
@@ -837,11 +855,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px',
}}>
- <div className="documentDecorations-topbar" onPointerDown={this.onContainerDown}>
- {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
- {hideResizers || hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
+ <div className="documentDecorations-topbar" style={{ display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined }} onPointerDown={this.onContainerDown}>
+ {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
+ {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
{hideTitle ? null : titleArea}
- {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+ {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as new embedding, shift: in new collection)')}
</div>
{hideResizers ? null : (
<>
@@ -855,7 +873,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
<div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
<div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
- {seldocview.props.renderDepth <= 1 || !seldocview.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
+ {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
</>
)}
{useRounding && (
@@ -896,9 +914,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
pointerEvents: 'none',
}}>
{this._isRotating ? null : (
- <div className="documentDecorations-rotation" style={{ pointerEvents: 'all' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
- <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} />
- </div>
+ <Tooltip enterDelay={750} title={<div className="dash-tooltip">tap to set rotate center, drag to rotate</div>}>
+ <div className="documentDecorations-rotation" style={{ pointerEvents: 'all' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
+ <IconButton icon={<FaUndo />} color={Colors.LIGHT_GRAY} />
+ </div>
+ </Tooltip>
)}
</div>
{!this._showRotCenter ? null : (