diff options
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 165 |
1 files changed, 82 insertions, 83 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d7ff0d06a..7424aaf2c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,7 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { DimensionField } from '../../fields/DimensionField'; import { IconButton } from '@dash/components'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -50,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; @@ -99,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; } @@ -128,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); @@ -321,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`; }); @@ -389,7 +388,7 @@ 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 }); @@ -491,51 +490,52 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora 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; - - // Special handling for shift+click (outpainting mode) - if (e.shiftKey && DocumentView.Selected().some(dv => dv.ComponentView instanceof ImageBox)) { - DocumentView.Selected().forEach(docView => { - if (docView.ComponentView instanceof ImageBox) { - // Set flag for outpainting mode - docView.Document._outpaintingResize = true; - - // Adjust only the document dimensions without scaling internal content - this.resizeViewForOutpainting(docView, refPt, scale, { dragHdl, shiftKey: e.shiftKey }); - } - else { - // Handle regular resize for non-image components - e.ctrlKey && !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions(); - const hasFixedAspect = this.hasFixedAspect(docView.Document); - const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y}; - this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey: e.ctrlKey }); - } + !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; + + // Special handling for shift+click (outpainting mode) + if (e.shiftKey && DocumentView.Selected().some(dv => dv.ComponentView instanceof ImageBox)) { + DocumentView.Selected().forEach(docView => { + if (docView.ComponentView instanceof ImageBox) { + // Set flag for outpainting mode + docView.Document._outpaintingResize = true; + + // Adjust only the document dimensions without scaling internal content + this.resizeViewForOutpainting(docView, refPt, scale, { dragHdl, shiftKey: e.shiftKey }); + } else { + // Handle regular resize for non-image components + e.ctrlKey && !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions(); + const hasFixedAspect = this.hasFixedAspect(docView.Document); + const scaleAspect = { x: scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y }; + this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey: e.ctrlKey }); + } + }); + } else { + // Regular resize behavior (existing code) + 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 })); + } + + await new Promise<void>(res => { + setTimeout(() => { + res((this._interactionLock = undefined)); + }); }); - } else { - // Regular resize behavior (existing code) - 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 })); - } - - await 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 } - ) => { + resizeViewForOutpainting = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; shiftKey: boolean }) => { const doc = docView.Document; - + if (doc.isGroup) { DocListCast(doc.data) .map(member => DocumentView.getDocumentView(member, docView)!) @@ -544,35 +544,32 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora doc.yPadding = NumCast(doc.yPadding) * scale.y; return; } - + if (!doc._outpaintingOriginalWidth || !doc._outpaintingOriginalHeight) { doc._outpaintingOriginalWidth = NumCast(doc._width); doc._outpaintingOriginalHeight = NumCast(doc._height); - - // Initialize or update the _originalDims ObjectField correctly - doc._originalDims = new DimensionField(doc._outpaintingOriginalWidth, doc._outpaintingOriginalHeight); } - + // 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(); - + // Trigger outpainting doc._needsOutpainting = true; - + // Store metadata needed for outpainting doc._outpaintingMetadata = JSON.stringify({ originalWidth: doc._outpaintingOriginalWidth, @@ -581,7 +578,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora newHeight, scaleX: scale.x, scaleY: scale.y, - anchorHandle: opts.dragHdl + anchorHandle: opts.dragHdl, }); }; @@ -589,19 +586,19 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora onPointerUp = (): void => { SnappingManager.SetIsResizing(undefined); SnappingManager.clearSnapLines(); - + // Check if any outpainting needs to be processed DocumentView.Selected().forEach(view => { if (view.Document._needsOutpainting && view.ComponentView instanceof ImageBox) { // Trigger outpainting process in the ImageBox component (view.ComponentView as ImageBox).processOutpainting(); - + // Clear flags view.Document._needsOutpainting = false; view.Document._outpaintingResize = false; } }); - + this._resizeHdlId = ''; this._resizeUndo?.end(); @@ -613,7 +610,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora 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( + 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, @@ -672,7 +669,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora scale.x === 1 ? (scale.x = scale.y) : (scale.y = scale.x); } - if (['right', 'left'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) { + if (['right', 'left', 'bottomRight'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) { const setData = Doc.NativeWidth(doc[DocData]) === doc.nativeWidth; doc.nativeWidth = scale.x * Doc.NativeWidth(doc); if (setData) Doc.SetNativeWidth(doc[DocData], NumCast(doc.nativeWidth)); @@ -680,7 +677,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF } } - if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) { + if (['bottom', 'top', 'bottomRight'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) { const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight && (!doc.layout_reflowVertical || opts.ctrlKey); doc._nativeHeight = scale.y * Doc.NativeHeight(doc); if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight)); @@ -815,7 +812,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); @@ -869,7 +866,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" /> @@ -886,10 +883,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', @@ -903,10 +900,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" @@ -914,8 +911,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> @@ -928,7 +927,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} /> </> )} @@ -944,12 +943,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> @@ -957,10 +956,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> |