diff options
author | bobzel <zzzman@gmail.com> | 2024-01-03 13:23:14 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-01-03 13:23:14 -0500 |
commit | 3f7b73ee8a3f75cd688ee7ddc7b54d90b00bee67 (patch) | |
tree | 7334d2d4ce886b4ba990cab45d3945fc62502eef | |
parent | af42d3061bd85907e3d987f0d794c250cfaded1c (diff) |
fixed marquee annotator for lightbox with pdf and rotated documents.
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 2 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 14 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 29 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 8 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 17 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 5 |
10 files changed, 43 insertions, 40 deletions
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 63d2a5e08..e6d53e727 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -305,7 +305,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( if (this._dragRef.current) { const dragDocView = this.view0!; const dragData = new DragManager.DocumentDragData([dragDocView.Document]); - const [left, top] = dragDocView.screenToLocalTransform().inverse().transformPoint(0, 0); + const [left, top] = dragDocView.screenToNativeLocalTransform().inverse().transformPoint(0, 0); dragData.defaultDropAction = 'embed'; dragData.canEmbed = true; DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false }); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 51fd33222..583a7c447 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -77,7 +77,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const center = {x: (this.Bounds.x+this.Bounds.r)/2, y: (this.Bounds.y+this.Bounds.b)/2}; const {x,y} = Utils.rotPt(e.clientX - center.x, e.clientY - center.y, - NumCast(SelectionManager.Views.lastElement()?.screenToLocalTransform().Rotate)); + NumCast(SelectionManager.Views.lastElement()?.screenToNativeLocalTransform().Rotate)); (this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && // (this.Bounds.x > center.x+x || this.Bounds.r < center.x+x || this.Bounds.y > center.y+y || this.Bounds.b < center.y+y ))); @@ -208,7 +208,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora SelectionManager.Views.map(dv => dv.Document), dragDocView._props.dropAction ); - dragData.offset = dragDocView.screenToLocalTransform().transformDirection(e.x - left, e.y - top); + dragData.offset = dragDocView.screenToNativeLocalTransform().transformDirection(e.x - left, e.y - top); dragData.moveDocument = dragDocView._props.moveDocument; dragData.removeDocument = dragDocView._props.removeDocument; dragData.isDocDecorationMove = true; @@ -353,7 +353,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { - const newloccentern = seldocview.screenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]); + const newloccentern = seldocview.screenToNativeLocalTransform().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); seldocview.Document.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); @@ -462,7 +462,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); return [a[0] + atob[0] * t, a[1] + atob[1] * t]; }; - const tl = docView.screenToLocalTransform().inverse().transformPoint(0, 0); + const tl = docView.screenToNativeLocalTransform().inverse().transformPoint(0, 0); return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); }; onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { @@ -530,7 +530,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora doc.xPadding = NumCast(doc.xPadding) * scale.x; doc.yPadding = NumCast(doc.yPadding) * scale.y; } else { - const refCent = docView.screenToLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) + const refCent = docView.screenToNativeLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) const [nwidth, nheight] = [docView.nativeWidth, docView.nativeHeight]; const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)]; @@ -632,7 +632,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora @computed get rotCenter() { const lastView = SelectionManager.Views.lastElement(); if (lastView) { - const invXf = lastView.screenToLocalTransform().inverse(); + const invXf = lastView.screenToNativeLocalTransform().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); return invXf.transformPoint(loccenter.x + NumCast(seldoc._width) / 2, loccenter.y + NumCast(seldoc._height) / 2); @@ -691,7 +691,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const bounds = this.ClippedBounds; const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView; const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? - const rotation = SelectionManager.Views.length == 1 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0; + const rotation = SelectionManager.Views.length == 1 ? seldocview.screenToNativeLocalTransform().inverse().RotateDeg : 0; // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 3eb43dacf..9f3786d8e 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -22,11 +22,12 @@ export interface MarqueeAnnotatorProps { Document: Doc; down?: number[]; scrollTop: number; + isNativeScaled?: boolean; scaling?: () => number; annotationLayerScaling?: () => number; annotationLayerScrollTop: number; containerOffset?: () => number[]; - mainCont: HTMLDivElement; + marqueeContainer: HTMLDivElement; docView: () => DocumentView; savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>; selectionText: () => string; @@ -147,20 +148,28 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP savedAnnotations.set(page, savedPage ?? [div]); }); + // this transforms a screen point into a local coordinate subject. It's complicated by documents that are rotated + // since the DOM's bounding rectangle is not rotated and Dash's ScreenToLocalTransform carries along a rotation value, but doesn't + // use it when transforming points. + // So the idea here is to reconstruct a local point by unrotating the screen point about the center of the bounding box. The approach is: + // 1) Get vector from the screen point to the center of the rotated bounding box in screens space + // 2) unrotate that vector in screen space + // 3) localize the unrotated vector by scaling into the marquee container's coordinates + // 4) reattach the vector to the center of the bounding box getTransformedScreenPt = (down: number[]) => { - const boundingRect = this.props.mainCont.getBoundingClientRect(); + const marqueeContainer = this.props.marqueeContainer; + const containerXf = this.props.isNativeScaled ? this.props.docView().screenToNativeLocalTransform() : this.props.docView().props.ScreenToLocalTransform(); + const boundingRect = marqueeContainer.getBoundingClientRect(); const center = { x: boundingRect.x + boundingRect.width / 2, y: boundingRect.y + boundingRect.height / 2 }; - const downPt = Utils.rotPt(down[0] - center.x, down[1] - center.y, NumCast(this.props.docView().screenToLocalTransform().Rotate)); - const scale = this.props.docView().props.ScreenToLocalTransform().Scale; - const scalex = this.props.mainCont.offsetWidth / NumCast(this.props.Document.width); - const scaley = this.props.mainCont.offsetHeight / NumCast(this.props.Document.height); - // set marquee x and y positions to the spatially transformed position - return { x: scalex * (downPt.x + NumCast(this.props.Document.width) / scale / 2) * scale, - y: scaley * (downPt.y + NumCast(this.props.Document.height) / scale / 2) * scale + this.props.annotationLayerScrollTop }; // prettier-ignore + const downVec = Utils.rotPt(down[0] - center.x, + down[1] - center.y, NumCast(containerXf.Rotate)); // prettier-ignore + return { x: downVec.x * containerXf.Scale + marqueeContainer.offsetWidth /2, + y: downVec.y * containerXf.Scale + marqueeContainer.offsetHeight/2 + this.props.annotationLayerScrollTop }; // prettier-ignore }; @action public onInitiateSelection(down: number[]) { + console.log('DOWN = ' + down[0] + ' ' + down[1]); this._width = this._height = 0; this._start = this.getTransformedScreenPt(down); @@ -242,7 +251,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP @action onSelectEnd = (e: PointerEvent) => { e.stopPropagation(); - const marquees = this.props.mainCont.getElementsByClassName('marqueeAnnotator-dragBox'); + const marquees = this.props.marqueeContainer.getElementsByClassName('marqueeAnnotator-dragBox'); const marqueeStyle = (Array.from(marquees).lastElement() as HTMLDivElement)?.style; if (!this.isEmpty && marqueeStyle) { // configure and show the annotation/link menu if a the drag region is big enough diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 548734dab..324a4b8d1 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -229,7 +229,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF @action public float = () => { const topDoc = this.Document; const containerDocView = this._props.docViewPath().lastElement(); - const screenXf = containerDocView?.screenToLocalTransform(); + const screenXf = containerDocView?.screenToNativeLocalTransform(); if (screenXf) { SelectionManager.DeselectAll(); if (topDoc.z) { diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index ff0e3271d..76cc010f6 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -425,7 +425,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { savedAnnotations={this.savedAnnotations} selectionText={returnEmptyString} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} + marqueeContainer={this._mainCont.current} anchorMenuCrop={this.crop} /> )} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d07824099..7ec0382e4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1433,7 +1433,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> { @computed get hideLinkButton() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HideLinkBtn + (this.IsSelected ? ':selected' : '')); } - hideLinkCount = () => this._props.renderDepth === -1 || (this.IsSelected && this._props.renderDepth) || !this._isHovering || this.hideLinkButton; + hideLinkCount = () => false; // this._props.renderDepth === -1 || (this.IsSelected && this._props.renderDepth) || !this._isHovering || this.hideLinkButton; @computed get linkCountView() { return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />; } @@ -1566,7 +1566,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> { }; layout_fitWidthFunc = (doc: Doc) => BoolCast(this.layout_fitWidth); - scaleToScreenSpace = () => (1 / (this._props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale; + scaleToScreenSpace = () => this._props.ScreenToLocalTransform().Scale; docViewPathFunc = () => this.docViewPath; isSelected = () => this.IsSelected; select = (extendSelection: boolean, focusSelection?: boolean) => { @@ -1589,7 +1589,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> { PanelHeight = () => this.panelHeight; NativeDimScaling = () => this.nativeScaling; selfView = () => this; - screenToLocalTransform = () => + screenToNativeLocalTransform = () => this._props .ScreenToLocalTransform() .translate(-this.centeringX, -this.centeringY) @@ -1676,7 +1676,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> { isSelected={this.isSelected} select={this.select} layout_fitWidth={this.layout_fitWidthFunc} - ScreenToLocalTransform={this.screenToLocalTransform} + ScreenToLocalTransform={this.screenToNativeLocalTransform} focus={this._props.focus || emptyFunction} ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} /> diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 876f13370..28c614786 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -458,7 +458,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp savedAnnotations={this.savedAnnotations} selectionText={returnEmptyString} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} + marqueeContainer={this._mainCont.current} highlightDragSrcColor={''} anchorMenuCrop={this.crop} /> diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index f257d5769..7ab954b3f 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1109,7 +1109,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { savedAnnotations={this.savedAnnotations} selectionText={returnEmptyString} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} + marqueeContainer={this._mainCont.current} anchorMenuCrop={this.crop} /> )} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 758c919d2..a61837dad 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -250,7 +250,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action createTextAnnotation = (sel: Selection, selRange: Range | undefined) => { if (this._mainCont.current && selRange) { - if (this.dataDoc[this._props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`; + if (this.dataDoc[this._props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToNativeLocalTransform().RotateDeg)}deg)`; const clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { const rect = clientRects.item(i); @@ -399,17 +399,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action iframeDown = (e: PointerEvent) => { this._props.select(false); - const locpt = { - x: (e.clientX / NumCast(this.Document.nativeWidth)) * this._props.PanelWidth(), - y: ((e.clientY - NumCast(this.layoutDoc.layout_scrollTop))/ NumCast(this.Document.nativeHeight)) * this._props.PanelHeight() }; // prettier-ignore - const scrclick = this._props.DocumentView?.()._props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!; - const scrcent = this._props - .DocumentView?.() - ._props.ScreenToLocalTransform() + const theclick = this.props + .ScreenToLocalTransform() .inverse() - .transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2)!; - const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this._props.ScreenToLocalTransform().Rotate); - const theclick = [theclickoff.x + scrcent[0], theclickoff.y + scrcent[1]]; + .transformPoint(e.clientX, e.clientY - NumCast(this.layoutDoc.layout_scrollTop)); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); const word = getWordAtPoint(e.target, e.clientX, e.clientY); if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) { @@ -1121,7 +1114,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps savedAnnotations={this.savedAnnotationsCreator} selectionText={this.selectionText} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} + marqueeContainer={this._mainCont.current} /> </div> )} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b6d027d30..f41094010 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -419,7 +419,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { @action createTextAnnotation = (sel: Selection, selRange: Range) => { if (this._mainCont.current) { - this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`; + this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToNativeLocalTransform().RotateDeg)}deg)`; const boundingRect = this._mainCont.current.getBoundingClientRect(); const clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { @@ -585,6 +585,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { getPageFromScroll={this.getPageFromScroll} anchorMenuClick={this._props.anchorMenuClick} scrollTop={0} + isNativeScaled={true} annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)} addDocument={this.addDocumentWrapper} docView={this._props.DocumentView!} @@ -592,7 +593,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { savedAnnotations={this.savedAnnotations} selectionText={this.selectionText} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} + marqueeContainer={this._mainCont.current} anchorMenuCrop={this._textSelecting ? undefined : this.crop} /> )} |