diff options
| author | bobzel <zzzman@gmail.com> | 2021-01-19 11:31:02 -0500 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2021-01-19 11:31:02 -0500 |
| commit | 22a2462a6854f31f6f546d56258aec2042073d4b (patch) | |
| tree | 60a35a6fd5704ececa538a7cf0e54bb5dde23edb /src/client/views/nodes | |
| parent | 90c8fe926efa245f256c7e0a496beaecf0572294 (diff) | |
more cleanup of marquee annotator. added marquee annotator to images
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/ImageBox.scss | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 43 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.tsx | 72 |
3 files changed, 68 insertions, 57 deletions
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index c1b95b308..41055e2db 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -5,6 +5,16 @@ position: relative; transform-origin: top left; + + .imageBox-annotationLayer { + position: absolute; + transform-origin: left top; + top: 0; + width: 100%; + pointer-events: none; + mix-blend-mode: multiply; // bcz: makes text fuzzy! + } + .imageBox-fader { pointer-events: inherit; } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index b0e7f4ce5..e202749aa 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -27,6 +27,10 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); import { StyleProp } from '../StyleProvider'; +import { PDFMenu } from '../pdf/PDFMenu'; +import { Dictionary } from 'typescript-collections'; +import { MarqueeAnnotator } from '../MarqueeAnnotator'; +import { Annotation } from '../pdf/Annotation'; const path = require('path'); const { Howl } = require('howler'); @@ -63,7 +67,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject<HTMLImageElement> = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; - private _pathDisposer?: IReactionDisposer; + private _disposers: { [name: string]: IReactionDisposer } = {}; @observable private _audioState = 0; @observable static _showControls: boolean; @observable uploadIcon = uploadIcons.idle; @@ -74,7 +78,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD } componentDidMount() { - this._pathDisposer = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }), + this._disposers.selection = reaction(() => this.props.isSelected(), + selected => { + if (!selected) { + this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); + this._savedAnnotations.clear(); + PDFMenu.Instance.fadeOut(true); + } + }, + { fireImmediately: true }); + this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }), action(({ nativeSize, width }) => { if (!this.layoutDoc._height) { this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth; @@ -84,7 +97,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD } componentWillUnmount() { - this._pathDisposer?.(); + Object.values(this._disposers).forEach(disposer => disposer?.()); } @undoBatch @@ -356,7 +369,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`; } - return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget}> + return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}> <div className="imageBox-fader" > <img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys src={srcpath} @@ -402,11 +415,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.ycenter); contentFunc = () => [this.content]; + private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); + private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); + @observable _marqueeing: number[] | undefined; + @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); + @computed get annotationLayer() { + return <div className="imageBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer} />; + } + @action + marqueeDown = (e: React.PointerEvent) => { + if (!e.altKey && e.button === 0 && this.active(true)) this._marqueeing = [e.clientX, e.clientY]; + } + @action + finishMarquee = () => { + this._marqueeing = undefined; + this.props.select(true); + } + render() { TraceMobx(); const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad; - return (<div className={`imageBox`} onContextMenu={this.specificContextMenu} + return (<div className={`imageBox`} onContextMenu={this.specificContextMenu} ref={this._mainCont} style={{ width: this.props.PanelWidth() ? undefined : `100%`, height: this.props.PanelWidth() ? undefined : `100%`, @@ -437,6 +467,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD whenActiveChanged={this.whenActiveChanged}> {this.contentFunc} </CollectionFreeFormView> + {this.annotationLayer} + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : + <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} </div >); } } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index f9e71bc92..ca3d4448d 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -14,7 +14,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { WebField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { addStyleSheet, clearStyleSheetRules, emptyFunction, OmitKeys, returnOne, smoothScroll, Utils } from "../../../Utils"; +import { emptyFunction, OmitKeys, returnOne, smoothScroll, Utils } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; @@ -38,26 +38,21 @@ const WebDocument = makeInterface(documentSchema); @observer export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) { private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); - static _annotationStyle: any = addStyleSheet(); public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); - private _downX: number = 0; - private _downY: number = 0; - @observable private _marqueeing: boolean = false; + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + private _disposers: { [name: string]: IReactionDisposer } = {}; + private _longPressSecondsHack?: NodeJS.Timeout; + private _outerRef = React.createRef<HTMLDivElement>(); + private _iframeIndicatorRef = React.createRef<HTMLDivElement>(); + private _iframeDragRef = React.createRef<HTMLDivElement>(); + @observable private _marqueeing: number[] | undefined; @observable private _url: string = "hello"; @observable private _pressX: number = 0; @observable private _pressY: number = 0; @observable private _iframe: HTMLIFrameElement | null = null; @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); - private _selectionReactionDisposer?: IReactionDisposer; - private _scrollReactionDisposer?: IReactionDisposer; - private _scrollTopReactionDisposer?: IReactionDisposer; - private _moveReactionDisposer?: IReactionDisposer; - private _longPressSecondsHack?: NodeJS.Timeout; - private _outerRef = React.createRef<HTMLDivElement>(); - private _iframeIndicatorRef = React.createRef<HTMLDivElement>(); - private _iframeDragRef = React.createRef<HTMLDivElement>(); - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + get scrollHeight() { return this.webpage?.scrollHeight || 1000; } get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; } set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; } @@ -91,8 +86,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this.webpage.scrollLeft = NumCast(this.layoutDoc._scrollLeft); } } - this._scrollReactionDisposer?.(); - this._scrollReactionDisposer = reaction(() => ({ scrollY: this.layoutDoc._scrollY, scrollX: this.layoutDoc._scrollX }), + this._disposers.scrollReaction?.(); + this._disposers.scrollReaction = reaction(() => ({ scrollY: this.layoutDoc._scrollY, scrollX: this.layoutDoc._scrollX }), ({ scrollY, scrollX }) => { const delay = this._outerRef.current ? 0 : 250; // wait for mainCont and try again to scroll const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/); @@ -110,7 +105,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum }, { fireImmediately: true } ); - this._scrollTopReactionDisposer = reaction(() => this.layoutDoc._scrollTop, + this._disposers.scrollTop = reaction(() => this.layoutDoc._scrollTop, scrollTop => { const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/); const duration = durationStr ? Number(durationStr[1]) : 1000; @@ -158,14 +153,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField); runInAction(() => this._url = urlField?.url.toString() || ""); - this._moveReactionDisposer = reaction(() => this.layoutDoc.x || this.layoutDoc.y, + this._disposers.scrollMove = reaction(() => this.layoutDoc.x || this.layoutDoc.y, () => this.updateScroll(this.layoutDoc._scrollLeft, this.layoutDoc._scrollTop)); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), + this._disposers.selection = reaction(() => this.props.isSelected(), selected => { if (!selected) { this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, [])); + this._savedAnnotations.clear(); PDFMenu.Instance.fadeOut(true); } }, @@ -194,10 +189,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } componentWillUnmount() { - this._moveReactionDisposer?.(); - this._selectionReactionDisposer?.(); - this._scrollTopReactionDisposer?.(); - this._scrollReactionDisposer?.(); + Object.values(this._disposers).forEach(disposer => disposer?.()); document.removeEventListener("pointerup", this.onLongPressUp); document.removeEventListener("pointermove", this.onLongPressMove); this._iframe?.removeEventListener('wheel', this.iframeWheel); @@ -471,39 +463,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @action onMarqueeDown = (e: React.PointerEvent) => { - this._downX = e.clientX; - this._downY = e.clientY; if (!e.altKey && e.button === 0 && this.active(true)) { - // clear out old marquees and initialize menu for new selection - PDFMenu.Instance.Status = "pdf"; - PDFMenu.Instance.fadeOut(true); - this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, [])); - if ((e.target as any)?.parentElement.className === "textLayer") { - // start selecting text if mouse down on textLayer spans - } - else this._marqueeing = true; - document.addEventListener("pointermove", this.onSelectMove); - document.addEventListener("pointerup", this.onSelectEnd); + this._marqueeing = [e.clientX, e.clientY]; } } @action - onSelectMove = (e: PointerEvent): void => { - if (e.target && (e.target as any).parentElement === this._mainCont.current) e.stopPropagation(); - } - - @action - finishMarquee = () => { this._marqueeing = false; } - - @action - onSelectEnd = (e: PointerEvent): void => { - clearStyleSheetRules(WebBox._annotationStyle); + finishMarquee = () => { + this._marqueeing = undefined; this.props.select(true); - this._savedAnnotations.clear(); - - document.removeEventListener("pointermove", this.onSelectMove); - document.removeEventListener("pointerup", this.onSelectEnd); } scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop)); @@ -562,7 +530,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum </div> {this.annotationLayer} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - <MarqueeAnnotator rootDoc={this.rootDoc} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} clientX={this._downX} clientY={this._downY} mainCont={this._mainCont.current} />} + <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} </div > {this.props.isSelected() ? this.editToggleBtn() : null} </div>); |
