aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DocumentDecorations.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-06-26 10:53:54 -0400
committerbobzel <zzzman@gmail.com>2025-06-26 10:53:54 -0400
commitbaae27b205356898c5866a0f095e4ec056e02459 (patch)
tree1b62de5579b8de8be81b6d342a9767f0f379bb91 /src/client/views/DocumentDecorations.tsx
parentccfdf905400cd4b81d8cde0f16bb0e15cd65621b (diff)
parent0093370a04348ef38b91252d02ab850f25d753b2 (diff)
Merge branch 'master' into agent-paper-main
Diffstat (limited to 'src/client/views/DocumentDecorations.tsx')
-rw-r--r--src/client/views/DocumentDecorations.tsx119
1 files changed, 50 insertions, 69 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ab665e984..2b7050cf0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,7 +1,7 @@
+import { IconButton } from '@dash/components';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { IconButton } from '@dash/components';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -27,15 +27,13 @@ import './DocumentDecorations.scss';
import { InkStrokeProperties } from './InkStrokeProperties';
import { InkingStroke } from './InkingStroke';
import { ObservableReactComponent } from './ObservableReactComponent';
+import { TagsView } from './TagsView';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { Colors } from './global/globalEnums';
import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
import { DocumentView } from './nodes/DocumentView';
-import { ImageBox } from './nodes/ImageBox';
import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import { TagsView } from './TagsView';
interface DocumentDecorationsProps {
PanelWidth: number;
@@ -58,7 +56,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';
@@ -349,13 +347,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 +364,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 +373,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');
@@ -393,17 +389,20 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
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,34 +410,17 @@ 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
);
};
@@ -446,7 +428,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
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)
+ .filter(dv => e.shiftKey && dv.ComponentView?.isOutpaintable?.())
.forEach(dv => {
dv.Document[dv.ComponentView!.fieldKey + '_outpaintOriginalWidth'] = NumCast(dv.Document._width);
dv.Document[dv.ComponentView!.fieldKey + '_outpaintOriginalHeight'] = NumCast(dv.Document._height);
@@ -502,7 +484,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
this._interactionLock = true;
this._snapPt = thisPt;
- const outpainted = e.shiftKey ? DocumentView.Selected().filter(dv => dv.ComponentView instanceof ImageBox) : [];
+ const outpainted = e.shiftKey ? DocumentView.Selected().filter(dv => dv.ComponentView?.isOutpaintable?.()) : [];
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)
@@ -524,7 +506,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
resizeViewForOutpainting = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; shiftKey: boolean }) => {
const doc = docView.Document;
- if (doc.isGroup) {
+ if (Doc.IsFreeformGroup(doc)) {
DocListCast(doc.data)
.map(member => DocumentView.getDocumentView(member, docView)!)
.forEach(member => this.resizeViewForOutpainting(member, refPt, scale, opts));
@@ -598,14 +580,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; 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));
@@ -623,8 +605,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
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 ((horizontalReflow || cornerReflow) && 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);
if (setData) Doc.SetNativeWidth(doc[DocData], NumCast(doc.nativeWidth));
@@ -632,25 +616,23 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF
}
}
- if ((verticalReflow || cornerReflow) && Doc.NativeHeight(doc)) {
+ 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
}
}
}
@@ -695,7 +677,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;
@@ -737,7 +720,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 =
@@ -765,7 +748,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
const rotation = DocumentView.Selected().length === 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0;
// Radius constants
- const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
+ const useRounding = seldocview.ComponentView?.showBorderRounding?.();
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 maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
@@ -923,16 +906,14 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
<div
style={{
position: 'absolute',
- transform: `rotate(${rotation}deg)`,
- width: this.Bounds.r - this.Bounds.x + 'px',
- height: this.Bounds.b - this.Bounds.y + 'px',
- left: this.Bounds.x,
- top: this.Bounds.y,
+ transform: `translate(${bounds.x - this._resizeBorderWidth}px, ${bounds.y - this._resizeBorderWidth}px) rotate(${rotation}deg)`,
+ width: bounds.r - bounds.x + 2 * this._resizeBorderWidth + 'px',
+ height: bounds.b - bounds.y + 2 * this._resizeBorderWidth + 'px',
pointerEvents: 'none',
}}>
{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>
@@ -941,7 +922,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()}
/>