diff options
-rw-r--r-- | src/client/views/DocComponent.tsx | 15 | ||||
-rw-r--r-- | src/client/views/GestureOverlay.tsx | 454 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 110 | ||||
-rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/ScriptingBox.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 2 |
10 files changed, 35 insertions, 564 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 57cea77c9..fa43b86bb 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -8,9 +8,8 @@ import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util'; import { returnFalse } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; -import { InteractionUtils } from '../util/InteractionUtils'; import { DocumentView } from './nodes/DocumentView'; -import { Touchable } from './Touchable'; +import * as React from 'react'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) export interface DocComponentProps { @@ -20,7 +19,7 @@ export interface DocComponentProps { LayoutTemplateString?: string; } export function DocComponent<P extends DocComponentProps>() { - class Component extends Touchable<P> { + class Component extends React.Component<React.PropsWithChildren<P>> { //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed get Document() { return this.props.Document; @@ -41,8 +40,6 @@ export function DocComponent<P extends DocComponentProps>() { @computed get fieldKey() { return this.props.fieldKey; } - - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } return Component; } @@ -59,7 +56,7 @@ interface ViewBoxBaseProps { rootSelected: (outsideReaction?: boolean) => boolean; } export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { - class Component extends Touchable<P> { + class Component extends React.Component<React.PropsWithChildren<P>> { //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then //@computed get Document(): T { return schemaCtor(this.props.Document); } @@ -79,8 +76,6 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { @computed get fieldKey() { return this.props.fieldKey; } - - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } return Component; } @@ -100,7 +95,7 @@ export interface ViewBoxAnnotatableProps { isAnnotationOverlay?: boolean; } export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() { - class Component extends Touchable<P> { + class Component extends React.Component<React.PropsWithChildren<P>> { @observable _annotationKeySuffix = () => 'annotations'; @observable _isAnyChildContentActive = false; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @@ -127,8 +122,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() isAnyChildContentActive = () => this._isAnyChildContentActive; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : ''); } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index caabdb09b..db6c00426 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -30,17 +30,14 @@ import { SetActiveInkColor, SetActiveInkWidth, } from './InkingStroke'; -import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu'; -import { Touchable } from './Touchable'; import { checkInksToGroup } from './global/globalScripts'; import { DocumentView } from './nodes/DocumentView'; -import { RadialMenu } from './nodes/RadialMenu'; interface GestureOverlayProps { isActive: boolean; } @observer -export class GestureOverlay extends Touchable<GestureOverlayProps> { +export class GestureOverlay extends React.Component<React.PropsWithChildren<GestureOverlayProps>> { static Instance: GestureOverlay; static Instances: GestureOverlay[] = []; @@ -85,10 +82,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> { private _inkToTextDoc: Doc | undefined; private thumbIdentifier?: number; private pointerIdentifier?: number; - private _hands: Map<number, React.Touch[]> = new Map<number, React.Touch[]>(); - private _holdTimer: NodeJS.Timeout | undefined; - - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; constructor(props: any) { super(props); @@ -104,415 +97,12 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> { GestureOverlay.Instance = this; }; - // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group - - /** - * Ignores all touch events that belong to a hand being held down. - */ - getNewTouches(e: React.TouchEvent | TouchEvent) { - const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches); - const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches); - const nt: (React.Touch | Touch)[] = Array.from(e.touches); - this._hands.forEach(hand => { - for (let i = 0; i < e.targetTouches.length; i++) { - const pt = e.targetTouches.item(i); - if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { - ntt.splice(ntt.indexOf(pt), 1); - } - } - - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); - if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { - nct.splice(nct.indexOf(pt), 1); - } - } - - for (let i = 0; i < e.touches.length; i++) { - const pt = e.touches.item(i); - if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { - nt.splice(nt.indexOf(pt), 1); - } - } - }); - return { ntt, nct, nt }; - } - - onReactTouchStart = (te: React.TouchEvent) => { - document.removeEventListener('touchmove', this.onReactHoldTouchMove); - document.removeEventListener('touchend', this.onReactHoldTouchEnd); - if (RadialMenu.Instance?._display === true) { - te.preventDefault(); - te.stopPropagation(); - RadialMenu.Instance.closeMenu(); - return; - } - - // this chunk adds new touch targets to a map of pointer events; this helps us keep track of individual fingers - // so that we can know, for example, if two fingers are pinching out or in. - const actualPts: React.Touch[] = []; - for (let i = 0; i < te.touches.length; i++) { - const pt: any = te.touches.item(i); - actualPts.push(pt); - // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) - // and this seems to be the only way of differentiating pen and touch on touch events - if (pt.radiusX > 1 && pt.radiusY > 1) { - InkTranscription.Instance.createInkGroup(); - Doc.ActiveTool = InkTool.None; - this.prevPoints.set(pt.identifier, pt); - } - } - - const ptsToDelete: number[] = []; - this.prevPoints.forEach(pt => { - if (!actualPts.includes(pt)) { - ptsToDelete.push(pt.identifier); - } - }); - - ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); - const nts = this.getNewTouches(te); - // if there are fewer than five touch events, handle as a touch event - if (nts.nt.length < 5) { - const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); - target?.dispatchEvent( - new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>('dashOnTouchStart', { - bubbles: true, - detail: { - fingers: this.prevPoints.size, - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct, - touchEvent: te, - }, - }) - ); - if (nts.nt.length === 1) { - // -- radial menu code -- - this._holdTimer = setTimeout(() => { - const target = document.elementFromPoint(te.changedTouches?.item(0).clientX, te.changedTouches?.item(0).clientY); - const pt: any = te.touches[te.touches?.length - 1]; - if (nts.nt.length === 1 && pt.radiusX > 1 && pt.radiusY > 1) { - target?.dispatchEvent( - new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>('dashOnTouchHoldStart', { - bubbles: true, - detail: { - fingers: this.prevPoints.size, - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct, - touchEvent: te, - }, - }) - ); - this._holdTimer = undefined; - document.removeEventListener('touchmove', this.onReactTouchMove); - document.removeEventListener('touchend', this.onReactTouchEnd); - document.removeEventListener('touchmove', this.onReactHoldTouchMove); - document.removeEventListener('touchend', this.onReactHoldTouchEnd); - document.addEventListener('touchmove', this.onReactHoldTouchMove); - document.addEventListener('touchend', this.onReactHoldTouchEnd); - } - }, 500); - } else { - this._holdTimer && clearTimeout(this._holdTimer); - } - document.removeEventListener('touchmove', this.onReactTouchMove); - document.removeEventListener('touchend', this.onReactTouchEnd); - document.addEventListener('touchmove', this.onReactTouchMove); - document.addEventListener('touchend', this.onReactTouchEnd); - } - // otherwise, handle as a hand event - else { - this.handleHandDown(te); - document.removeEventListener('touchmove', this.onReactTouchMove); - document.removeEventListener('touchend', this.onReactTouchEnd); - } - }; - - onReactTouchMove = (e: TouchEvent) => { - const nts: any = this.getNewTouches(e); - this._holdTimer && clearTimeout(this._holdTimer); - this._holdTimer = undefined; - - document.dispatchEvent( - new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchMove', { - bubbles: true, - detail: { - fingers: this.prevPoints.size, - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct, - touchEvent: e, - }, - }) - ); - }; - - onReactTouchEnd = (e: TouchEvent) => { - const nts: any = this.getNewTouches(e); - this._holdTimer && clearTimeout(this._holdTimer); - this._holdTimer = undefined; - - document.dispatchEvent( - new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchEnd', { - bubbles: true, - detail: { - fingers: this.prevPoints.size, - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct, - touchEvent: e, - }, - }) - ); - - // cleanup any lingering pointers - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); - if (pt) { - if (this.prevPoints.has(pt.identifier)) { - this.prevPoints.delete(pt.identifier); - } - } - } - - if (this.prevPoints.size === 0) { - document.removeEventListener('touchmove', this.onReactTouchMove); - document.removeEventListener('touchend', this.onReactTouchEnd); - } - e.stopPropagation(); - }; - - handleHandDown = async (e: React.TouchEvent) => { - this._holdTimer && clearTimeout(this._holdTimer); - - // this chunk of code helps us keep track of which touch events are associated with a hand event - // so that if a hand is held down, but a second hand is interacting with dash, the second hand's events - // won't interfere with the first hand's events. - const fingers = new Array<React.Touch>(); - for (let i = 0; i < e.touches.length; i++) { - const pt: any = e.touches.item(i); - if (pt.radiusX > 1 && pt.radiusY > 1) { - for (let j = 0; j < e.targetTouches.length; j++) { - const tPt = e.targetTouches.item(j); - if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { - if (pt && this.prevPoints.has(pt.identifier)) { - fingers.push(pt); - } - } - } - } - } - - // this chunk of code determines whether this is a left hand or a right hand, as well as which pointer is the thumb and pointer - const thumb = fingers.reduce((a, v) => (a.clientY > v.clientY ? a : v), fingers[0]); - const rightMost = Math.max(...fingers.map(f => f.clientX)); - const leftMost = Math.min(...fingers.map(f => f.clientX)); - let pointer: React.Touch | undefined; - // left hand - if (thumb.clientX === rightMost) { - pointer = fingers.reduce((a, v) => (a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v)); - } - // right hand - else if (thumb.clientX === leftMost) { - pointer = fingers.reduce((a, v) => (a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v)); - } - this.pointerIdentifier = pointer?.identifier; - - runInAction(() => { - this._pointerY = pointer?.clientY; - if (thumb.identifier === this.thumbIdentifier) { - this._thumbX = thumb.clientX; - this._thumbY = thumb.clientY; - this._hands.set(thumb.identifier, fingers); - return; - } - }); - - this.thumbIdentifier = thumb?.identifier; - this._hands.set(thumb.identifier, fingers); - - this.removeMoveListeners(); - document.removeEventListener('touchmove', this.handleHandMove); - document.addEventListener('touchmove', this.handleHandMove); - document.removeEventListener('touchend', this.handleHandUp); - document.addEventListener('touchend', this.handleHandUp); - }; - - @action - handleHandMove = (e: TouchEvent) => { - // update pointer trackers - const fingers = new Array<React.Touch>(); - for (let i = 0; i < e.touches.length; i++) { - const pt: any = e.touches.item(i); - if (pt.radiusX > 1 && pt.radiusY > 1) { - for (let j = 0; j < e.targetTouches.length; j++) { - const tPt = e.targetTouches.item(j); - if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { - if (pt && this.prevPoints.has(pt.identifier)) { - this._hands.forEach(hand => - hand.some(f => { - if (f.identifier === pt.identifier) { - fingers.push(pt); - } - }) - ); - } - } - } - } - } - // update hand trackers - const thumb = fingers.reduce((a, v) => (a.clientY > v.clientY ? a : v), fingers[0]); - if (thumb?.identifier && thumb?.identifier === this.thumbIdentifier) { - this._hands.set(thumb.identifier, fingers); - } - - // loop through every changed pointer - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); - // if the thumb was moved - if (pt && pt.identifier === this.thumbIdentifier && this._thumbY) { - if (this._thumbX && this._thumbY) { - // moving a thumb horiz. changes the palette collection selection, moving vert. changes the selection of any menus on the current palette item - const yOverX = Math.abs(pt.clientX - this._thumbX) < Math.abs(pt.clientY - this._thumbY); - if ((yOverX && this._inkToTextDoc) || this._selectedIndex > -1) { - if (Math.abs(pt.clientY - this._thumbY) > 10 * window.devicePixelRatio) { - this._selectedIndex = Math.min(Math.max(-1, -Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1), this._possibilities.length - 1); - } - } - } - } - // if the pointer finger was moved - if (pt && pt.identifier === this.pointerIdentifier) { - this._pointerY = pt.clientY; - } - } - }; - - @action - handleHandUp = (e: TouchEvent) => { - // sometimes, users may lift up their thumb or index finger if they can't stretch far enough to scroll an entire menu, - // so we don't want to just remove the palette when that happens - if (e.touches.length < 3) { - if (this.thumbIdentifier) this._hands.delete(this.thumbIdentifier); - this._palette = undefined; - this.thumbIdentifier = undefined; - - // this chunk of code is for handling the ink to text toolglass - let scriptWorked = false; - if (NumCast(this._inkToTextDoc?.selectedIndex) > -1) { - // if there is a text option selected, activate it - const selectedButton = this._possibilities[this._selectedIndex]; - if (selectedButton) { - selectedButton.props.onClick(); - scriptWorked = true; - } - } - // if there isn't a text option selected, dry the ink strokes into ink documents - if (!scriptWorked) { - this._strokes.forEach(s => { - this.dispatchGesture(GestureUtils.Gestures.Stroke, s); - }); - } - - this._strokes = []; - this._points.length = 0; - this._possibilities = []; - document.removeEventListener('touchend', this.handleHandUp); - } - }; - - /** - * Code for radial menu - */ - onReactHoldTouchMove = (e: TouchEvent) => { - document.removeEventListener('touchmove', this.onReactTouchMove); - document.removeEventListener('touchend', this.onReactTouchEnd); - document.removeEventListener('touchmove', this.onReactHoldTouchMove); - document.removeEventListener('touchend', this.onReactHoldTouchEnd); - document.addEventListener('touchmove', this.onReactHoldTouchMove); - document.addEventListener('touchend', this.onReactHoldTouchEnd); - const nts: any = this.getNewTouches(e); - if (this.prevPoints.size === 1 && this._holdTimer) { - clearTimeout(this._holdTimer); - } - document.dispatchEvent( - new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchHoldMove', { - bubbles: true, - detail: { - fingers: this.prevPoints.size, - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct, - touchEvent: e, - }, - }) - ); - }; - - /** - * Code for radial menu - */ - onReactHoldTouchEnd = (e: TouchEvent) => { - const nts: any = this.getNewTouches(e); - if (this.prevPoints.size === 1 && this._holdTimer) { - clearTimeout(this._holdTimer); - this._holdTimer = undefined; - } - document.dispatchEvent( - new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchHoldEnd', { - bubbles: true, - detail: { - fingers: this.prevPoints.size, - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct, - touchEvent: e, - }, - }) - ); - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); - if (pt) { - if (this.prevPoints.has(pt.identifier)) { - this.prevPoints.delete(pt.identifier); - } - } - } - - document.removeEventListener('touchmove', this.onReactHoldTouchMove); - document.removeEventListener('touchend', this.onReactHoldTouchEnd); - - e.stopPropagation(); - }; - @action onPointerDown = (e: React.PointerEvent) => { - if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - setupMoveUpEvents( - this, - e, - returnFalse, - returnFalse, - action((e: PointerEvent, doubleTap?: boolean) => { - if (doubleTap) { - InkTranscription.Instance.createInkGroup(); - Doc.ActiveTool = InkTool.None; - return; - } - }) - ); - } if (!(e.target as any)?.className?.toString().startsWith('lm_')) { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - Doc.ActiveTool = InkTool.Write; - } + if ([InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { this._points.push({ X: e.clientX, Y: e.clientY }); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); - // if (Doc.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)"); } } }; @@ -553,41 +143,8 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); - const initialPoint = this._points[0]; - const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height; - const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER); - - // if a toolglass is selected and the stroke starts within the toolglass boundaries - if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) { - switch (this.Tool) { - case ToolglassTools.InkToText: - this._strokes.push(this._points.slice()); - CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then(results => { - const wordResults = results.filter((r: any) => r.category === 'line'); - const possibilities: string[] = []; - for (const wR of wordResults) { - if (wR?.recognizedText) { - possibilities.push(wR?.recognizedText); - } - possibilities.push(...wR?.alternates?.map((a: any) => a.recognizedString)); - } - const r = Math.max(this.svgBounds.right, ...this._strokes.map(s => GestureOverlay.getBounds(s).right)); - const l = Math.min(this.svgBounds.left, ...this._strokes.map(s => GestureOverlay.getBounds(s).left)); - const t = Math.min(this.svgBounds.top, ...this._strokes.map(s => GestureOverlay.getBounds(s).top)); - - // if we receive any word results from cognitive services, display them - runInAction(() => { - this._possibilities = possibilities.map(p => <TouchScrollableMenuItem text={p} onClick={() => GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: l, Y: t }], p)} />); - }); - }); - break; - case ToolglassTools.IgnoreGesture: - this.dispatchGesture(GestureUtils.Gestures.Stroke); - break; - } - } //if any of the shape is activated in the CollectionFreeFormViewChrome - else if (this.InkShape) { + if (this.InkShape) { this.makeBezierPolygon(this.InkShape, false); this.dispatchGesture(this.InkShape); this.primCreated(); @@ -945,7 +502,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> { render() { return ( - <div className="gestureOverlay-cont" style={{ pointerEvents: this.props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}> + <div className="gestureOverlay-cont" style={{ pointerEvents: this.props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown}> {this.showMobileInkOverlay ? <MobileInkOverlay /> : null} {this.elements} @@ -955,7 +512,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> { height: this.height, width: this.height, pointerEvents: this._clipboardDoc ? 'unset' : 'none', - touchAction: this._clipboardDoc ? 'unset' : 'none', + touchAction: 'none', transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height} px)`, }}> {this._clipboardDoc} @@ -971,7 +528,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> { display: this.showBounds ? 'unset' : 'none', }} /> - <TouchScrollableMenu options={this._possibilities} bounds={this.svgBounds} selectedIndex={this._selectedIndex} x={this._menuX} y={this._menuY} /> </div> ); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7c8b2f195..f70c85dcf 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -35,19 +35,16 @@ export function CollectionSubView<X>(moreProps?: X) { class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() { private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; protected _mainCont?: HTMLDivElement; @observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it @observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it protected createDashEventsTarget = (ele: HTMLDivElement | null) => { this.dropDisposer?.(); this.gestureDisposer?.(); - this._multiTouchDisposer?.(); if (ele) { this._mainCont = ele; this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this)); this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); - this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); } }; protected CreateDropTarget(ele: HTMLDivElement) { @@ -57,7 +54,6 @@ export function CollectionSubView<X>(moreProps?: X) { componentWillUnmount() { this.gestureDisposer?.(); - this._multiTouchDisposer?.(); } @computed get dataDoc() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 29abff3a4..389a9a534 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -80,8 +80,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab private reactionDisposer: IReactionDisposer | undefined; @observable _isContentActive: boolean | undefined; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - constructor(props: any) { super(props); runInAction(() => (this._annotationKeySuffix = returnEmptyString)); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e3b0d88e9..26e682989 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -21,7 +21,6 @@ import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { InteractionUtils } from '../../../util/InteractionUtils'; import { FollowLinkScript } from '../../../util/LinkFollower'; import { ReplayMovements } from '../../../util/ReplayMovements'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; @@ -477,9 +476,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }, -1); } - tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) { + tryDragCluster(e: PointerEvent, cluster: number) { if (cluster !== -1) { - const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0); + const ptsParent = e; if (ptsParent) { const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster); const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!); @@ -620,29 +619,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; @action - onPenUp = (e: PointerEvent): void => { - if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - document.removeEventListener('pointerup', this.onPenUp); - const currentCol = DocListCast(this.rootDoc.currentInkDoc); - const rootDocList = DocListCast(this.rootDoc.data); - currentCol.push(rootDocList[rootDocList.length - 1]); - - this._batch?.end(); - } - }; - - @action onPointerDown = (e: React.PointerEvent): void => { this._downX = this._lastX = e.pageX; this._downY = this._lastY = e.pageY; this._downTime = Date.now(); const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode; if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this.props.isContentActive(true)) { - if ( - !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag - !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && - !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) - ) { + if (!this.props.Document._isGroup) { + // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag // prettier-ignore switch (Doc.ActiveTool) { case InkTool.Highlighter: break; @@ -663,29 +647,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } }; - @action - handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => { - // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); - const pt = me.changedTouches[0]; - if (pt) { - this._hitCluster = this.pickCluster(this.screenToLocalXf.transformPoint(pt.clientX, pt.clientY)); - if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) { - this.removeMoveListeners(); - this.addMoveListeners(); - this.removeEndListeners(); - this.addEndListeners(); - if (Doc.ActiveTool === InkTool.None) { - this._lastX = pt.pageX; - this._lastY = pt.pageY; - e.preventDefault(); - e.stopPropagation(); - } else { - e.preventDefault(); - } - } - } - }; - public unprocessedDocs: Doc[] = []; public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>(); @undoBatch @@ -819,20 +780,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; @action - onPointerMove = (e: PointerEvent): boolean => { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false; - if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - Doc.ActiveTool = InkTool.None; - } else { - if (this.tryDragCluster(e, this._hitCluster)) { - e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over - return true; - } - // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan) - if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.zoomScaling()) { - this.pan(e); - e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning - } + onPointerMove = (e: PointerEvent) => { + if (this.tryDragCluster(e, this._hitCluster)) { + e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over + return true; + } + // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan) + if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.zoomScaling()) { + this.pan(e); + e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning } return false; }; @@ -970,11 +926,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return tVals; }; - cleanUpInteractions = () => { - this.removeMoveListeners(); - this.removeEndListeners(); - }; - @action zoom = (pointX: number, pointY: number, deltaY: number): void => { if (this.Document._isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return; @@ -1699,7 +1650,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' }); this.props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' }); - this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' }); + this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' }); !Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null; !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null; @@ -1723,16 +1674,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @undoBatch @action - transcribeStrokes = (math: boolean) => { + transcribeStrokes = () => { if (this.props.Document._isGroup && this.props.Document.transcription) { - if (!math) { - const text = StrCast(this.props.Document.transcription); - - const lines = text.split('\n'); - const height = 30 + 15 * lines.length; + const text = StrCast(this.props.Document.transcription); + const lines = text.split('\n'); + const height = 30 + 15 * lines.length; - this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height })); - } + this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height })); } }; @@ -1804,7 +1752,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection brushedView = () => this._brushedView; gridColor = () => DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor))) - .fade(0.6) + .fade(0.5) .toString(); @computed get backgroundGrid() { return ( @@ -1879,17 +1827,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection nativeDim = () => this.nativeDimScaling; @action - brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => { + brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number = 2500) => { this._brushtimer1 && clearTimeout(this._brushtimer1); this._brushtimer && clearTimeout(this._brushtimer); this._brushedView = undefined; this._brushtimer1 = setTimeout( action(() => { this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 }; - this._brushtimer = setTimeout( - action(() => (this._brushedView = undefined)), - 2500 - ); + this._brushtimer = setTimeout(action(() => (this._brushedView = undefined)), holdTime); // prettier-ignore }), transTime + 1 ); @@ -1966,17 +1911,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } } -interface CollectionFreeFormOverlayViewProps { - elements: () => ViewDefResult[]; -} - @observer -class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps> { +class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> { render() { - return this.props - .elements() - .filter(ele => ele.bounds?.z) - .map(ele => ele.ele); + return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore } } diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index b09fcd882..ff394e5f5 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -20,7 +20,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } - protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined; private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined]; @observable _animating = ''; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 005ec6019..4bfb19f3f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -116,7 +116,7 @@ export interface DocComponentView { getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) restoreView?: (viewSpec: Doc) => boolean; scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus - brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void; // highlight a region of a view (used by freeforms) + brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms) getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections) @@ -265,8 +265,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps private _mainCont = React.createRef<HTMLDivElement>(); private _titleRef = React.createRef<EditableView>(); private _dropDisposer?: DragManager.DragDropDisposer; - private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class @observable _animateScaleTime: Opt<number>; // milliseconds for animating between views. defaults to 300 if not uset @@ -396,15 +394,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this.cleanupHandlers(false); if (this._mainCont.current) { this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document, this.preDropFunc); - this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); - this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); } } @action cleanupHandlers(unbrush: boolean) { this._dropDisposer?.(); - this._multiTouchDisposer?.(); - this._holdDisposer?.(); unbrush && Doc.UnBrushDoc(this.props.Document); Object.values(this._disposers).forEach(disposer => disposer?.()); } @@ -593,7 +587,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps }; cleanupPointerEvents = () => { - this.cleanUpInteractions(); document.removeEventListener('pointermove', this.onPointerMove); document.removeEventListener('pointerup', this.onPointerUp); }; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index f7dee1cc1..d74fb3d79 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -50,7 +50,6 @@ const uploadIcons = { @observer export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { - protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 7c8a1849e..197c520c7 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -27,7 +27,6 @@ const _global = (window /* browser */ || global) /* node */ as any; @observer export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { private dropDisposer?: DragManager.DragDropDisposer; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 180fa9f5d..f3f07cc21 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -611,7 +611,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { changed = true; const computedScale = NumCast(activeItem.config_zoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height); activeItem.presentation_movement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale); - dv.ComponentView?.brushView?.(viewport, transTime); + dv.ComponentView?.brushView?.(viewport, transTime, 2500); } } else { if (bestTarget._freeform_panX !== activeItem.config_panX || bestTarget._freeform_panY !== activeItem.config_panY || bestTarget._freeform_scale !== activeItem.config_viewScale) { |