diff options
author | bobzel <zzzman@gmail.com> | 2025-04-28 12:52:29 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-04-28 12:52:29 -0400 |
commit | a25dc02334de3f5b58aff1911bdae30d49a1d26b (patch) | |
tree | 6fdc319fa86edb5aa1b20f98a5351648646d239b | |
parent | b60b1863be5ecf987f3d3f2483e28e74f634853f (diff) |
cleaned up outpainting and Doc resize code. fixes problems with doc resizing of text boxes with ctrl-key.
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 106 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 | ||||
-rw-r--r-- | src/server/ApiManagers/FireflyManager.ts | 8 |
5 files changed, 39 insertions, 89 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e694419a4..a4a668085 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -535,8 +535,6 @@ export class DocumentOptions { * The list of embedded Doc instances in each Scrapbook slot */ scrapbookContents?: List<Doc>; - - _outpaintingMetadata?: STRt = new StrInfo('serialized JSON metadata needed for image outpainting', false); } export const DocOptions = new DocumentOptions(); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3f11a4713..2d39b827d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -445,6 +445,12 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora @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._outpaintingOriginalWidth = NumCast(dv.Document._width); + dv.Document._outpaintingOriginalHeight = NumCast(dv.Document._height); + }); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); e.stopPropagation(); const id = (this._resizeHdlId = e.currentTarget.className); @@ -484,50 +490,31 @@ 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 () => { + 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; - // 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; + const notOutpainted = DocumentView.Selected().filter(dv => !e.shiftKey || !(dv.ComponentView instanceof ImageBox)); + // Special handling for shift-drag resize (outpainting of Images) + DocumentView.Selected() + .filter(dv => !notOutpainted.includes(dv)) + .forEach(dv => this.resizeViewForOutpainting(dv, refPt, scale, { dragHdl, shiftKey: e.shiftKey })); // Adjust only the document dimensions without scaling internal content - // 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 })); - } + // Regular resize behavior for docs not being outpainted + 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 })); - await new Promise<void>(res => { - setTimeout(() => { - res((this._interactionLock = undefined)); - }); - }); + new Promise<void>(res => setTimeout(() => res((this._interactionLock = undefined)))); }); return false; @@ -545,11 +532,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora return; } - if (!doc._outpaintingOriginalWidth || !doc._outpaintingOriginalHeight) { - doc._outpaintingOriginalWidth = NumCast(doc._width); - doc._outpaintingOriginalHeight = NumCast(doc._height); - } - // Calculate new boundary dimensions const originalWidth = NumCast(doc._width); const originalHeight = NumCast(doc._height); @@ -566,38 +548,17 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora 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, - originalHeight: doc._outpaintingOriginalHeight, - newWidth, - newHeight, - scaleX: scale.x, - scaleY: scale.y, - anchorHandle: opts.dragHdl, - }); }; @action - onPointerUp = (): void => { + onPointerUp = (e: PointerEvent): 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; - } - }); + DocumentView.Selected() + .filter(dv => e.shiftKey && dv.ComponentView instanceof ImageBox) + .forEach(view => (view.ComponentView as ImageBox).processOutpainting()); this._resizeHdlId = ''; this._resizeUndo?.end(); @@ -647,7 +608,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora // // 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) { DocListCast(doc.data) @@ -660,25 +621,24 @@ 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 (['right', 'left', 'bottomRight'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) { + if ((horizontalReflow || cornerReflow) && Doc.NativeWidth(doc)) { 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', 'bottomRight'].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)) { + 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)); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 999d7089b..197ef0998 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -170,14 +170,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }, { fireImmediately: true } ); - this._disposers.outpainting = reaction( - () => this.Document?._needsOutpainting, - needsOutpainting => { - if (needsOutpainting && this.Document?._outpaintingResize) { - this.processOutpainting(); - } - } - ); } componentWillUnmount() { @@ -421,7 +413,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const response = await Networking.PostToServer('/outpaintImage', { imageUrl: currentPath, prompt: customPrompt, - originalDimensions: { width: origWidth, height: origHeight }, + originalDimensions: { width: Math.min(newWidth, origWidth), height: Math.min(newHeight, origHeight) }, newDimensions: { width: newWidth, height: newHeight }, }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 164c64107..98e461a52 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1149,7 +1149,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB // Since we also monitor all component height changes, this will update the document's height. resetNativeHeight = action((scrollHeight: number) => { this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight; - if (!this.layoutDoc.isTemplateForField) this.layoutDoc._nativeHeight = scrollHeight; + if (!this.layoutDoc.isTemplateForField && NumCast(this.layoutDoc._nativeHeight)) this.layoutDoc._nativeHeight = scrollHeight; }); addPlugin = (plugin: Plugin) => { diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts index 5311ca643..63581d3b3 100644 --- a/src/server/ApiManagers/FireflyManager.ts +++ b/src/server/ApiManagers/FireflyManager.ts @@ -343,10 +343,10 @@ export default class FireflyManager extends ApiManager { numVariations: 1, placement: { inset: { - left: 0, - top: 0, - right: Math.round(req.body.newDimensions.width - req.body.originalDimensions.width), - bottom: Math.round(req.body.newDimensions.height - req.body.originalDimensions.height), + left: Math.round(req.body.newDimensions.width - req.body.originalDimensions.width) / 2, + top: Math.round(req.body.newDimensions.height - req.body.originalDimensions.height) / 2, + right: Math.round(req.body.newDimensions.width - req.body.originalDimensions.width) / 2, + bottom: Math.round(req.body.newDimensions.height - req.body.originalDimensions.height) / 2, }, alignment: { horizontal: 'center', |