diff options
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 291 |
1 files changed, 163 insertions, 128 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 54e050f9f..69c2467a3 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -49,7 +49,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora static Instance: DocumentDecorations; private _resizeHdlId = ''; private _keyinput = React.createRef<HTMLInputElement>(); - private _resizeBorderWidth = 16; + private _resizeBorderWidth = 8; private _linkBoxHeight = 20 + 3; // link button height + margin private _titleHeight = 20; private _resizeUndo?: UndoManager.Batch; @@ -58,7 +58,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora private _inkDragDocs: { doc: Doc; x: number; y: number; width: number; height: number }[] = []; private _interactionLock?: boolean; - @observable _showNothing = true; + @observable private _showNothing = true; @observable private _forceRender = 0; @observable private _accumulatedTitle = ''; @observable private _titleControlString: string = '$title'; @@ -98,11 +98,11 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const bounds = { ...this.Bounds }; const leftBounds = this._props.boundsLeft; const topBounds = DocumentView.LightboxDoc() ? 0 : this._props.boundsTop; - bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2; - bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight; + bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth) + this._resizeBorderWidth; + bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth - this._titleHeight) + this._resizeBorderWidth + this._titleHeight; const borderRadiusDraggerWidth = 15; - bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth)); - bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight)); + bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth) - this._resizeBorderWidth - borderRadiusDraggerWidth)); + bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth + this._linkBoxHeight) - this._resizeBorderWidth - this._linkBoxHeight)); return bounds; } @@ -127,7 +127,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora } else if (this._titleControlString.startsWith('$')) { if (this._accumulatedTitle.startsWith('-->#')) { DocumentView.SelectedDocs().forEach(doc => { - doc[DocData].onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`); + doc.$onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`); }); } const titleFieldKey = this._titleControlString.substring(1); @@ -240,7 +240,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora } else { // if Doc is in the sticker palette, remove the flag indicating that it's saved const dragFactory = DocCast(iconView.Document.dragFactory); - if (dragFactory && DocCast(dragFactory.cloneOf).savedAsSticker) DocCast(dragFactory.cloneOf).savedAsSticker = undefined; + if (dragFactory && DocCast(dragFactory.cloneOf)?.savedAsSticker) DocCast(dragFactory.cloneOf)!.savedAsSticker = undefined; // if this is a face Annotation doc, then just hide it. if (iconView.Document.annotationOn && iconView.Document.face) iconView.Document.hidden = true; @@ -320,7 +320,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const dist = moveEv.clientX < x && moveEv.clientY < y ? 0 : Math.sqrt((moveEv.clientX - x) * (moveEv.clientX - x) + (moveEv.clientY - y) * (moveEv.clientY - y)); DocumentView.SelectedDocs().forEach(doc => { - const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); + const docMax = Math.min(NumCast(doc._width) / 2, NumCast(doc._height) / 2); const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box doc._layout_borderRounding = `${radius}px`; }); @@ -349,13 +349,14 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora e.stopPropagation(); }; - setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { + setDocRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { const selDoc = seldocview.Document; const newloccentern = seldocview.screenToViewTransform().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.Document._rotation) / 180) * Math.PI); selDoc._rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); selDoc._rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height); + return false; }; @action @@ -365,10 +366,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents( this, e, - (moveEv: PointerEvent, down: number[], delta: number[]) => { - this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]); - return false; - }, + (moveEv) => this.setDocRotateCenter(seldocview, [moveEv.clientX, moveEv.clientY]), action(() => { this._isRotating = false; }), // upEvent action(() => { seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0; }), true @@ -377,7 +375,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; @action - onRotateDown = (e: React.PointerEvent): void => { + onRotateHdlDown = (e: React.PointerEvent): void => { this._isRotating = true; const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] }; const rotateUndo = UndoManager.StartBatch('drag rotation'); @@ -388,22 +386,25 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora DocumentView.Selected().forEach(dv => { const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI; const localRotCtr = dv.screenToViewTransform().transformPoint(rcScreen.X, rcScreen.Y); - const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2]; + const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document._width) / 2, localRotCtr[1] - NumCast(dv.Document._height) / 2]; const startRotCtr = Utils.rotPt(localRotCtrOffset[0], localRotCtrOffset[1], -accumRot); const unrotatedDocPos = { x: NumCast(dv.Document.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.Document.y) + localRotCtrOffset[1] - startRotCtr.y }; infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot }); }); - const infoRot = (angle: number, isAbs = false) => { - DocumentView.Selected().forEach( - action(dv => { - const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!; - const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle); - infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle }); - dv.Document.x = infos.get(dv.Document)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x); - dv.Document.y = infos.get(dv.Document)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y); - dv.Document._rotation = ((isAbs ? 0 : NumCast(dv.Document._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360 - }) - ); + const rotateDocs = (angle: number, isAbs = false) => { + if (selectedInk.length) { + InkStrokeProperties.Instance.rotateInk(selectedInk, angle, rcScreen); // rotate ink + return this.setDocRotateCenter(seldocview, centerPoint); + } + DocumentView.Selected().forEach(action(dv => { + const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!; + const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle); + infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle }); + dv.Document.x = infos.get(dv.Document)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x); + dv.Document.y = infos.get(dv.Document)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y); + dv.Document._rotation = ((isAbs ? 0 : NumCast(dv.Document._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360 + })); // prettier-ignore + return false; }; setupMoveUpEvents( this, @@ -411,40 +412,29 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora (moveEv: PointerEvent, down: number[], delta: number[]) => { const previousPoint = { X: moveEv.clientX, Y: moveEv.clientY }; const movedPoint = { X: moveEv.clientX - delta[0], Y: moveEv.clientY - delta[1] }; - const deltaAng = InkStrokeProperties.angleChange(movedPoint, previousPoint, rcScreen); - if (selectedInk.length) { - deltaAng && InkStrokeProperties.Instance.rotateInk(selectedInk, deltaAng, rcScreen); - this.setRotateCenter(seldocview, centerPoint); - } else { - infoRot(deltaAng); - } - return false; + return rotateDocs(InkStrokeProperties.angleChange(movedPoint, previousPoint, rcScreen)); }, // moveEvent action(() => { const oldRotation = NumCast(seldocview.Document._rotation); - const diff = oldRotation - Math.round(oldRotation / 45) * 45; - if (Math.abs(diff) < 5) { - if (selectedInk.length) { - InkStrokeProperties.Instance.rotateInk(selectedInk, ((Math.round(oldRotation / 45) * 45 - oldRotation) / 180) * Math.PI, rcScreen); - } else { - infoRot(((Math.round(oldRotation / 45) * 45) / 180) * Math.PI, true); - } - } - if (selectedInk.length) { - this.setRotateCenter(seldocview, centerPoint); - } + if (Math.abs(oldRotation - Math.round(oldRotation / 45) * 45) < 5) { // rptation witihin 5deg of a 45deg angle multiple + rotateDocs(((Math.round(oldRotation / 45) * 45) / 180) * Math.PI, true); + } // prettier-ignore this._isRotating = false; rotateUndo?.end(); }), // upEvent - action(() => { - this._showRotCenter = !this._showRotCenter; - }) // clickEvent + action(() => (this._showRotCenter = !this._showRotCenter)) // clickEvent ); }; @action onPointerDown = (e: React.PointerEvent): void => { SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them + DocumentView.Selected() + .filter(dv => e.shiftKey && dv.ComponentView instanceof ImageBox) + .forEach(dv => { + dv.Document[dv.ComponentView!.fieldKey + '_outpaintOriginalWidth'] = NumCast(dv.Document._width); + dv.Document[dv.ComponentView!.fieldKey + '_outpaintOriginalHeight'] = NumCast(dv.Document._height); + }); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); e.stopPropagation(); const id = (this._resizeHdlId = e.currentTarget.className); @@ -473,6 +463,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const tl = docView.screenToContentsTransform().inverse().transformPoint(0, 0); return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); }; + + // Modify the onPointerMove method to handle shift+click during resize onPointerMove = (e: PointerEvent): boolean => { const first = DocumentView.Selected()[0]; const effectiveAcl = GetEffectiveAcl(first.Document); @@ -482,26 +474,92 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const dragHdl = this._resizeHdlId.split(' ')[0].replace('documentDecorations-', '').replace('Resizer', ''); const thisPt = // do snapping of drag point - fixedAspect && (dragHdl === 'bottomRight' || dragHdl === 'topLeft') + fixedAspect && (dragHdl === 'bottomRight' || dragHdl === 'topLeft') && e.ctrlKey ? DragManager.snapDragAspect(this.projectDragToAspect(e, first, fixedAspect), fixedAspect) : DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); const { scale, refPt } = this.getResizeVals(thisPt, dragHdl); - !this._interactionLock && runInAction(async () => { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) - this._interactionLock = true; - this._snapPt = thisPt; - e.ctrlKey && (DocumentView.Selected().forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions())); - const hasFixedAspect = DocumentView.Selected().map(dv => dv.Document).some(this.hasFixedAspect); - const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y}; - DocumentView.Selected().forEach(docView => - this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore - await new Promise<void>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); - }); // prettier-ignore + !this._interactionLock && + runInAction(() => { + // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) + this._interactionLock = true; + this._snapPt = thisPt; + + const outpainted = e.shiftKey ? DocumentView.Selected().filter(dv => dv.ComponentView instanceof ImageBox) : []; + const notOutpainted = e.shiftKey ? DocumentView.Selected().filter(dv => !outpainted.includes(dv)) : DocumentView.Selected(); + + // Special handling for shift-drag resize (outpainting of Images by resizing without scaling content - fill in with firefly GAI) + e.shiftKey && outpainted.forEach(dv => this.resizeViewForOutpainting(dv, refPt, scale, { dragHdl, shiftKey: e.shiftKey })); + + // Special handling for not outpainted Docs when ctrl-resizing (setup native dimesions for modification) + e.ctrlKey && notOutpainted.forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions()); + + const hasFixedAspect = notOutpainted.map(dv => dv.Document).some(this.hasFixedAspect); + const scaleAspect = { x: scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y }; + notOutpainted.forEach(docView => this.resizeView(docView, refPt, scaleAspect, { dragHdl, freezeNativeDims: e.ctrlKey })); + + new Promise<void>(res => setTimeout(() => res((this._interactionLock = undefined)))); + }); return false; }; + resizeViewForOutpainting = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; shiftKey: boolean }) => { + const doc = docView.Document; + + if (Doc.IsFreeformGroup(doc)) { + DocListCast(doc.data) + .map(member => DocumentView.getDocumentView(member, docView)!) + .forEach(member => this.resizeViewForOutpainting(member, refPt, scale, opts)); + doc.xPadding = NumCast(doc.xPadding) * scale.x; + doc.yPadding = NumCast(doc.yPadding) * scale.y; + return; + } + + // Calculate new boundary dimensions + const originalWidth = NumCast(doc._width); + const originalHeight = NumCast(doc._height); + const newWidth = Math.max(NumCast(doc._width_min, 25), originalWidth * scale.x); + const newHeight = Math.max(NumCast(doc._height_min, 10), originalHeight * scale.y); + + // Apply new dimensions + doc._width = newWidth; + doc._height = newHeight; + + const refCent = docView.screenToViewTransform().transformPoint(refPt[0], refPt[1]); + const { deltaX, deltaY } = this.realignRefPt(doc, refCent, originalWidth, originalHeight); + doc.x = NumCast(doc.x) + deltaX; + doc.y = NumCast(doc.y) + deltaY; + doc._layout_modificationDate = new DateField(); + }; + + @action + onPointerUp = () => { + SnappingManager.SetIsResizing(undefined); + SnappingManager.clearSnapLines(); + + this._resizeHdlId = ''; + this._resizeUndo?.end(); + + // detect layout_autoHeight gesture and apply + DocumentView.Selected().forEach(view => { + NumCast(view.Document._height) < 20 && (view.layoutDoc._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 || [] })) + .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { + doc.$data = new InkField(inkPts.map( + (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth) + X: NumCast(doc.x) - x + (NumCast(doc._width) * ipt.X) / width, + Y: NumCast(doc.y) - y + (NumCast(doc._height) * ipt.Y) / height, + }))); // prettier-ignore + Doc.SetNativeWidth(doc, undefined); + Doc.SetNativeHeight(doc, undefined); + }); + }; + // // determines how much to resize, and determines the resize reference point // @@ -524,14 +582,14 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora // // determines if anything being dragged directly or via a group has a fixed aspect ratio (in which case we resize uniformly) // - hasFixedAspect = (doc: Doc): boolean => (doc.isGroup ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc._layout_nativeDimEditable)); + hasFixedAspect = (doc: Doc): boolean => (Doc.IsFreeformGroup(doc) ? DocListCast(doc.data).some(this.hasFixedAspect) : !BoolCast(doc._layout_nativeDimEditable)); // // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc // - resizeView = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; ctrlKey: boolean }) => { + resizeView = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; freezeNativeDims: boolean }) => { const doc = docView.Document; - if (doc.isGroup) { + if (Doc.IsFreeformGroup(doc)) { DocListCast(doc.data) .map(member => DocumentView.getDocumentView(member, docView)!) .forEach(member => this.resizeView(member, refPt, scale, opts)); @@ -542,42 +600,41 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const [nwidth, nheight] = [docView.nativeWidth, docView.nativeHeight]; const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)]; - const modifyNativeDim = - (opts.ctrlKey && doc._layout_nativeDimEditable) || // e.g., PDF or web page - (doc._layout_reflowHorizontal && opts.dragHdl !== 'bottom' && opts.dragHdl !== 'top') || // eg rtf or some web pages - (doc._layout_reflowVertical && (opts.dragHdl === 'bottom' || opts.dragHdl === 'top' || opts.ctrlKey)); // eg rtf, web, pdf - if (nwidth && nheight && !modifyNativeDim) { - // eg., dragging right resizer on PDF -- enforce native dimensions because not expliclty overridden with ctrl or bottom resize drag + const cornerReflow = !opts.freezeNativeDims && doc._layout_reflowHorizontal && doc._layout_reflowVertical && ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'].includes(opts.dragHdl); + const horizontalReflow = !opts.freezeNativeDims && doc._layout_reflowHorizontal && ['left', 'right'].includes(opts.dragHdl); // eg rtf or some web pages + const verticalReflow = !opts.freezeNativeDims && doc._layout_reflowVertical && ['bottom', 'top'].includes(opts.dragHdl); // eg rtf, web, pdf + // preserve aspect ratio if Doc has a native ize and drag won't cause reflow (eg, ctrl-dragging a Doc's corner doesn't allow reflow, or dragging right side of a PDF which isn't horizontally reflowable) + if (nwidth && nheight && !cornerReflow && !horizontalReflow && !verticalReflow) { scale.x === 1 ? (scale.x = scale.y) : (scale.y = scale.x); } + if (NumCast(doc._height) * scale.y < NumCast(doc._height_min, 10)) scale.y = NumCast(doc._height_min, 10) / NumCast(doc._height); + if (NumCast(doc._width) * scale.x < NumCast(doc._width_min, 25)) scale.x = NumCast(doc._width_min, 25) / NumCast(doc._width); - if (['right', 'left'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) { + if ((horizontalReflow || cornerReflow) && Doc.NativeWidth(doc) && scale.x > 0) { const setData = Doc.NativeWidth(doc[DocData]) === doc.nativeWidth; - doc.nativeWidth = scale.x * Doc.NativeWidth(doc); + doc._nativeWidth = scale.x * Doc.NativeWidth(doc); if (setData) Doc.SetNativeWidth(doc[DocData], NumCast(doc.nativeWidth)); if (doc._layout_reflowVertical && !NumCast(doc.nativeHeight)) { doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF } } - if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) { - const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight && (!doc.layout_reflowVertical || opts.ctrlKey); + if ((verticalReflow || cornerReflow) && Doc.NativeHeight(doc) && scale.y > 0) { + const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight && !doc.layout_reflowVertical; doc._nativeHeight = scale.y * Doc.NativeHeight(doc); if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight)); } - doc._width = Math.max(NumCast(doc._width_min, 25), NumCast(doc._width) * scale.x); - doc._height = Math.max(NumCast(doc._height_min, 10), NumCast(doc._height) * scale.y); + doc._width = NumCast(doc._width) * scale.x; + doc._height = NumCast(doc._height) * scale.y; const { deltaX, deltaY } = this.realignRefPt(doc, refCent, initWidth || 1, initHeight || 1); doc.x = NumCast(doc.x) + deltaX; doc.y = NumCast(doc.y) + deltaY; doc._layout_modificationDate = new DateField(); - if (scale.y !== 1) { - const docLayout = docView.layoutDoc; - docLayout._layout_autoHeight = undefined; - if (docView.layoutDoc._layout_autoHeight) { - // if autoHeight is still on because of a prototype - docLayout._layout_autoHeight = false; // then don't inherit, but explicitly set it to false + if (scale.y !== 1 && !opts.freezeNativeDims) { + doc._layout_autoHeight = undefined; + if (doc._layout_autoHeight) { + doc._layout_autoHeight = false; // set explicitly to false if inherited from parent of layout } } } @@ -606,31 +663,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; }; - @action - onPointerUp = (): void => { - SnappingManager.SetIsResizing(undefined); - SnappingManager.clearSnapLines(); - this._resizeHdlId = ''; - this._resizeUndo?.end(); - - // detect layout_autoHeight gesture and apply - DocumentView.Selected().forEach(view => { - NumCast(view.Document._height) < 20 && (view.layoutDoc._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 || [] })) - .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { - doc[DocData].data = new InkField(inkPts.map( - (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth) - X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width, - Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height, - }))); // prettier-ignore - Doc.SetNativeWidth(doc, undefined); - Doc.SetNativeHeight(doc, undefined); - }); - }; - @computed get selectionTitle(): string { if (DocumentView.Selected().length === 1) { const selected = DocumentView.Selected()[0]; @@ -647,7 +679,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora if (lastView) { const invXf = lastView.screenToViewTransform().inverse(); const seldoc = lastView.layoutDoc; - const loccenter = Utils.rotPt(NumCast(seldoc._rotation_centerX) * NumCast(seldoc._width), NumCast(seldoc._rotation_centerY) * NumCast(seldoc._height), invXf.Rotate); + const rcent = this._showRotCenter ? [NumCast(seldoc._rotation_centerX), NumCast(seldoc._rotation_centerY)] : [0, 0]; + const loccenter = Utils.rotPt(rcent[0] * NumCast(seldoc._width), rcent[1] * NumCast(seldoc._height), invXf.Rotate); return invXf.transformPoint(loccenter.x + NumCast(seldoc._width) / 2, loccenter.y + NumCast(seldoc._height) / 2); } return this._rotCenter; @@ -689,7 +722,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora hideDecorations || seldocview._props.hideOpenButton || seldocview.Document.layout_hideOpenButton || - DocumentView.Selected().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || + DocumentView.Selected().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -719,7 +752,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; const borderRadius = numberValue(Cast(seldocview.Document.layout_borderRounding, 'string', null)); - const docMax = Math.min(NumCast(seldocview.Document.width) / 2, NumCast(seldocview.Document.height) / 2); + const docMax = Math.min(NumCast(seldocview.Document._width) / 2, NumCast(seldocview.Document._height) / 2); const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const radiusHandle = (borderRadius / docMax) * maxDist; const radiusHandleLocation = Math.min(radiusHandle, maxDist); @@ -773,7 +806,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora </span> )} {sharingMenu} - {!useLock ? null : ( + {!useLock || hideTitle ? null : ( <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top"> <div className="documentDecorations-lock" style={{ color: seldocview.Document._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}> <FontAwesomeIcon size="sm" icon="lock" /> @@ -790,10 +823,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora <div className="documentDecorations-background" style={{ - width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', - height: bounds.b - bounds.y + this._resizeBorderWidth + 'px', - left: bounds.x - this._resizeBorderWidth / 2, - top: bounds.y - this._resizeBorderWidth / 2, + width: bounds.r - bounds.x + 2 * this._resizeBorderWidth + 'px', + height: bounds.b - bounds.y + 2 * this._resizeBorderWidth + 'px', + left: bounds.x - this._resizeBorderWidth, + top: bounds.y - this._resizeBorderWidth, transformOrigin, background: SnappingManager.ShiftKey ? undefined : 'yellow', pointerEvents: SnappingManager.ShiftKey || SnappingManager.IsResizing ? 'none' : 'all', @@ -807,10 +840,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora <div className={`documentDecorations-container ${this._showNothing ? 'showNothing' : ''}`} style={{ - transform: `translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`, + transform: `translate(${bounds.x - this._resizeBorderWidth}px, ${bounds.y - this._resizeBorderWidth - this._titleHeight}px) rotate(${rotation}deg)`, transformOrigin, - width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', - height: bounds.b - bounds.y + this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px', + width: bounds.r - bounds.x + 2 * this._resizeBorderWidth + 'px', + height: bounds.b - bounds.y + 2 * this._resizeBorderWidth + (this._showNothing ? 0 : this._titleHeight) + 'px', }}> <div className="documentDecorations-topbar" @@ -818,8 +851,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined, }} onPointerDown={this.onContainerDown}> - {hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')} - {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')} + <div className="documentDecorations-closeWrapper"> + {hideDeleteButton ? null : topBtn('close', 'times', undefined, () => this.onCloseClick(true), 'Close')} + {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, () => this.onCloseClick(undefined), 'Minimize')} + </div> {titleArea} {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection, opption: in editor view)')} </div> @@ -832,7 +867,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora <div key="c" className="documentDecorations-centerCont" /> <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} /> <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} /> - <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} /> + {seldocview.TagPanelHeight !== 0 || seldocview.TagPanelEditing ? null : <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} />} <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} /> </> )} @@ -848,12 +883,12 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora /> )} - {hideDocumentButtonBar || this._showNothing ? null : ( + {seldocview.TagPanelEditing || hideDocumentButtonBar || this._showNothing ? null : ( <div className="link-button-container" style={{ - top: DocumentView.Selected().length > 1 ? 0 : `${seldocview.showTags ? 4 + seldocview.TagPanelHeight : 4}px`, - transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, + top: DocumentView.Selected().length > 1 || !seldocview.showTags ? 0 : `${seldocview.TagPanelHeight}px`, + transform: `translate(${-this._resizeBorderWidth + 10}px, ${(seldocview.TagPanelHeight === 0 ? 2 * this._resizeBorderWidth : this._resizeBorderWidth) + bounds.b - bounds.y + this._titleHeight}px) `, }}> <DocumentButtonBar views={() => DocumentView.Selected()} /> </div> @@ -861,10 +896,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora <div className="documentDecorations-tagsView" style={{ - top: 30, // offset by height of documentButtonBar so that items can be clicked without overlap interference - transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, + display: DocumentView.Selected().length > 1 || !seldocview.showTags ? 'none' : undefined, + transform: `translate(${-this._resizeBorderWidth + 10}px, ${2 * this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, }}> - {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} /> : null} + {DocumentView.Selected().length > 1 ? <TagsView Views={DocumentView.Selected()} background={SnappingManager.userBackgroundColor ?? ''} /> : null} </div> </div> @@ -882,7 +917,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }}> {this._isRotating ? null : ( <Tooltip enterDelay={750} title={<div className="dash-tooltip">tap to set rotate center, drag to rotate</div>}> - <div className="documentDecorations-rotation" onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}> + <div className="documentDecorations-rotation" onPointerDown={this.onRotateHdlDown} onContextMenu={e => e.preventDefault()}> <IconButton icon={<FaUndo />} color={SettingsManager.userColor} /> </div> </Tooltip> @@ -891,7 +926,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora {!this._showRotCenter ? null : ( <div className="documentDecorations-rotationCenter" - style={{ transform: `translate(${this.rotCenter[0] - 3}px, ${this.rotCenter[1] - 3}px)` }} + style={{ transform: `translate(${this.rotCenter[0]}px, ${this.rotCenter[1]}px)` }} onPointerDown={this.onRotateCenterDown} onContextMenu={e => e.preventDefault()} /> |
