aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/InteractionUtils.ts50
-rw-r--r--src/client/views/CollectionLinearView.tsx4
-rw-r--r--src/client/views/GestureOverlay.tsx160
-rw-r--r--src/client/views/Touchable.tsx114
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx80
-rw-r--r--src/client/views/nodes/DocumentView.tsx32
-rw-r--r--src/pen-gestures/GestureUtils.ts2
9 files changed, 344 insertions, 112 deletions
diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts
index 76b43da3c..63ee2fb92 100644
--- a/src/client/util/InteractionUtils.ts
+++ b/src/client/util/InteractionUtils.ts
@@ -8,13 +8,53 @@ export namespace InteractionUtils {
const REACT_POINTER_PEN_BUTTON = 0;
const ERASER_BUTTON = 5;
+ export class MultiTouchEvent<T extends React.TouchEvent | TouchEvent> {
+ constructor(
+ readonly fingers: number,
+ readonly points: T extends React.TouchEvent ? React.TouchList : TouchList,
+ readonly touchEvent: T extends React.TouchEvent ? React.TouchEvent : TouchEvent
+ ) { }
+ }
+
+ export interface MultiTouchEventDisposer { (): void; }
+
+ export function MakeMultiTouchTarget(
+ element: HTMLElement,
+ startFunc: (e: Event, me: MultiTouchEvent<React.TouchEvent>) => void,
+ ): MultiTouchEventDisposer {
+ const onMultiTouchStartHandler = (e: Event) => startFunc(e, (e as CustomEvent<MultiTouchEvent<React.TouchEvent>>).detail);
+ // const onMultiTouchMoveHandler = moveFunc ? (e: Event) => moveFunc(e, (e as CustomEvent<MultiTouchEvent<TouchEvent>>).detail) : undefined;
+ // const onMultiTouchEndHandler = endFunc ? (e: Event) => endFunc(e, (e as CustomEvent<MultiTouchEvent<TouchEvent>>).detail) : undefined;
+ element.addEventListener("dashOnTouchStart", onMultiTouchStartHandler);
+ // if (onMultiTouchMoveHandler) {
+ // element.addEventListener("dashOnTouchMove", onMultiTouchMoveHandler);
+ // }
+ // if (onMultiTouchEndHandler) {
+ // element.addEventListener("dashOnTouchEnd", onMultiTouchEndHandler);
+ // }
+ return () => {
+ element.removeEventListener("dashOnTouchStart", onMultiTouchStartHandler);
+ // if (onMultiTouchMoveHandler) {
+ // element.removeEventListener("dashOnTouchMove", onMultiTouchMoveHandler);
+ // }
+ // if (onMultiTouchEndHandler) {
+ // element.removeEventListener("dashOnTouchend", onMultiTouchEndHandler);
+ // }
+ };
+ }
+
export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map<number, React.Touch>, ignorePen: boolean): React.Touch[] {
const myTouches = new Array<React.Touch>();
- for (let i = 0; i < e.targetTouches.length; i++) {
- const pt: any = e.targetTouches.item(i);
- if (pt && prevPoints.has(pt.identifier)) {
- if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) {
- myTouches.push(pt);
+ for (let i = 0; i < e.touches.length; i++) {
+ const pt: any = e.touches.item(i);
+ if (ignorePen || (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 && prevPoints.has(pt.identifier)) {
+ myTouches.push(pt);
+ }
+ }
}
}
}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index 5d6a58656..38f2decdd 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -36,7 +36,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
{ fireImmediately: true }
);
}
- protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this._dropDisposer && this._dropDisposer();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
@@ -55,7 +55,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
render() {
const guid = Utils.GenerateGuid();
return <div className="collectionLinearView-outer">
- <div className="collectionLinearView" ref={this.createDropAndGestureTarget} >
+ <div className="collectionLinearView" ref={this.createDashEventsTarget} >
<input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} />
<label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 830c06f1f..a5fba362c 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,7 +1,7 @@
import React = require("react");
import { Touchable } from "./Touchable";
import { observer } from "mobx-react";
-import "./GestureOverlay.scss"
+import "./GestureOverlay.scss";
import { computed, observable, action, runInAction } from "mobx";
import { CreatePolyline } from "./InkingStroke";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
@@ -16,6 +16,7 @@ import { Scripting } from "../util/Scripting";
import { FieldValue, Cast } from "../../new_fields/Types";
import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
import Palette from "./Palette";
+import { Utils } from "../../Utils";
@observer
export default class GestureOverlay extends Touchable {
@@ -28,6 +29,9 @@ export default class GestureOverlay extends Touchable {
private _d1: Doc | undefined;
private thumbIdentifier?: number;
+ private _hands: (React.Touch[])[] = [];
+
+ protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
constructor(props: Readonly<{}>) {
super(props);
@@ -35,11 +39,143 @@ export default class GestureOverlay extends Touchable {
GestureOverlay.Instance = this;
}
- @action
+ getNewTouches(e: React.TouchEvent | TouchEvent) {
+ const ntt: (React.Touch | Touch)[] = [];
+ const nct: (React.Touch | Touch)[] = [];
+ const nt: (React.Touch | Touch)[] = [];
+ 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.push(pt);
+ }
+ }
+
+ for (let i = 0; i < e.changedTouches.length; i++) {
+ const pt = e.changedTouches.item(i);
+ if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) {
+ nct.push(pt);
+ }
+ }
+
+ for (let i = 0; i < e.changedTouches.length; i++) {
+ const pt = e.touches.item(i);
+ if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) {
+ nt.push(pt);
+ }
+ }
+ });
+ return { ntt, nct, nt };
+ }
+
+ onReactTouchStart = (te: React.TouchEvent) => {
+ 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) {
+ // if (typeof pt.identifier !== "string") {
+ // pt.identifier = Utils.GenerateGuid();
+ // }
+ 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));
+
+ if (this.prevPoints.size && this.prevPoints.size < 5) {
+ const nts: any = this.getNewTouches(te);
+ 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,
+ points: te.targetTouches,
+ touchEvent: new React.TouchEvent("start", {
+ targetTouches: nts.ntt,
+ touches: nts.nt,
+ changedTouches: nts.nct
+ });
+ }
+ }
+ )
+ );
+ document.removeEventListener("touchmove", this.onReactTouchMove);
+ document.removeEventListener("touchend", this.onReactTouchEnd);
+ document.addEventListener("touchmove", this.onReactTouchMove);
+ document.addEventListener("touchend", this.onReactTouchEnd);
+ }
+ else {
+ this.handleHandDown(te);
+ document.removeEventListener("touchmove", this.onReactTouchMove);
+ document.removeEventListener("touchend", this.onReactTouchEnd);
+ }
+ }
+
+ onReactTouchMove = (e: TouchEvent) => {
+ const nts: any = this.getNewTouches(e);
+ document.dispatchEvent(
+ new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchMove",
+ {
+ bubbles: true,
+ detail: {
+ fingers: this.prevPoints.size,
+ points: e.touches,
+ touchEvent: new TouchEvent("move", {
+ targetTouches: nts.ntt,
+ changedTouches: nts.nct,
+ touches: nts.nt,
+ })
+ }
+ })
+ );
+ }
+
+ onReactTouchEnd = (e: TouchEvent) => {
+ document.dispatchEvent(
+ new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchEnd",
+ {
+ bubbles: true,
+ detail: {
+ fingers: this.prevPoints.size,
+ points: e.touches,
+ 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);
+ }
+ }
+ }
+
+ if (this.prevPoints.size === 0) {
+ document.removeEventListener("touchmove", this.onReactTouchMove);
+ document.removeEventListener("touchend", this.onReactTouchEnd);
+ }
+ e.stopPropagation();
+ }
+
handleHandDown = (e: React.TouchEvent) => {
const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
this.thumbIdentifier = thumb?.identifier;
+ fingers.forEach((f) => this.prevPoints.delete(f.identifier));
+ this._hands.push(fingers);
const others = fingers.filter(f => f !== thumb);
const minX = Math.min(...others.map(f => f.clientX));
const minY = Math.min(...others.map(f => f.clientY));
@@ -48,10 +184,12 @@ export default class GestureOverlay extends Touchable {
const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
if (thumbDoc) {
- this._palette = <Palette x={minX} y={minY} thumb={[thumb.clientX, thumb.clientY]} thumbDoc={thumbDoc} />;
+ runInAction(() => {
+ this._palette = <Palette x={minX} y={minY} thumb={[thumb.clientX, thumb.clientY]} thumbDoc={thumbDoc} />;
+ });
}
- document.removeEventListener("touchmove", this.onTouch);
+ this.removeMoveListeners();
document.removeEventListener("touchmove", this.handleHandMove);
document.addEventListener("touchmove", this.handleHandMove);
document.removeEventListener("touchend", this.handleHandUp);
@@ -69,11 +207,9 @@ export default class GestureOverlay extends Touchable {
@action
handleHandUp = (e: TouchEvent) => {
- this.onTouchEnd(e);
- if (this.prevPoints.size < 3) {
- this._palette = undefined;
- document.removeEventListener("touchend", this.handleHandUp);
- }
+ // this.onTouchEnd(e);
+ this._palette = undefined;
+ document.removeEventListener("touchend", this.handleHandUp);
}
@action
@@ -115,7 +251,7 @@ export default class GestureOverlay extends Touchable {
DocUtils.MakeLink({ doc: this._d1 }, { doc: doc });
actionPerformed = true;
}
- }
+ };
const ge = new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
{
bubbles: true,
@@ -181,7 +317,7 @@ export default class GestureOverlay extends Touchable {
}
}
)
- )
+ );
this._points = [];
}
}
@@ -215,7 +351,7 @@ export default class GestureOverlay extends Touchable {
render() {
return (
- <div className="gestureOverlay-cont" onPointerDown={this.onPointerDown} onTouchStart={this.onTouchStart}>
+ <div className="gestureOverlay-cont" onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
{this.props.children}
{this._palette}
{this.currentStroke}
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 2a7599fbf..a71015b05 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -8,8 +8,11 @@ const HOLD_DURATION = 1000;
export abstract class Touchable<T = {}> extends React.Component<T> {
//private holdTimer: NodeJS.Timeout | undefined;
- holdTimer: NodeJS.Timeout | undefined;
+ private holdTimer: NodeJS.Timeout | undefined;
+ private moveDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ private endDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ protected abstract multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _touchDrag: boolean = false;
protected prevPoints: Map<number, React.Touch> = new Map<number, React.Touch>();
@@ -22,16 +25,27 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
* When a touch even starts, we keep track of each touch that is associated with that event
*/
@action
- protected onTouchStart = (e: React.TouchEvent): void => {
+ protected onTouchStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): void => {
const actualPts: React.Touch[] = [];
- for (let i = 0; i < e.targetTouches.length; i++) {
- const pt: any = e.targetTouches.item(i);
+ const te = me.touchEvent;
+ // loop through all touches on screen
+ 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) {
+ if (this.prevPoints.has(pt.identifier)) {
this.prevPoints.set(pt.identifier, pt);
}
+ // only add the ones that are targeted on "this" element, but with the identifier that the screen touch gives
+ for (let j = 0; j < te.changedTouches.length; j++) {
+ const tPt = te.changedTouches.item(j);
+ if (pt.clientX === tPt.clientX && pt.clientY === tPt.clientY) {
+ // 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) {
+ this.prevPoints.set(pt.identifier, pt);
+ }
+ }
+ }
}
const ptsToDelete: number[] = [];
@@ -41,26 +55,30 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
});
+ // console.log(ptsToDelete.length);
ptsToDelete.forEach(pt => this.prevPoints.delete(pt));
+ // console.log(this.prevPoints.size);
if (this.prevPoints.size) {
switch (this.prevPoints.size) {
case 1:
- this.handle1PointerDown(e);
- e.persist();
+ this.handle1PointerDown(te);
+ te.persist();
// if (this.holdTimer) {
// clearTimeout(this.holdTimer)
// this.holdTimer = undefined;
// }
- this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(e), HOLD_DURATION);
+ this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(te), HOLD_DURATION);
+ // e.stopPropagation();
// console.log(this.holdTimer);
break;
case 2:
- this.handle2PointersDown(e);
- break;
- case 5:
- this.handleHandDown(e);
+ this.handle2PointersDown(te);
+ // e.stopPropagation();
break;
+ // case 5:
+ // this.handleHandDown(te);
+ // break;
}
}
}
@@ -69,28 +87,30 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
* Handle touch move event
*/
@action
- protected onTouch = (e: TouchEvent): void => {
- const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
+ protected onTouch = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
+ const te = me.touchEvent;
+ const myTouches = InteractionUtils.GetMyTargetTouches(te, this.prevPoints, true);
// if we're not actually moving a lot, don't consider it as dragging yet
if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return;
this._touchDrag = true;
if (this.holdTimer) {
- console.log("CLEAR")
+ console.log("CLEAR");
clearTimeout(this.holdTimer);
// this.holdTimer = undefined;
}
+ // console.log(myTouches.length);
switch (myTouches.length) {
case 1:
- this.handle1PointerMove(e);
+ this.handle1PointerMove(te);
break;
case 2:
- this.handle2PointersMove(e);
+ this.handle2PointersMove(te);
break;
}
- for (let i = 0; i < e.targetTouches.length; i++) {
- const pt = e.targetTouches.item(i);
+ for (let i = 0; i < te.touches.length; i++) {
+ const pt = te.touches.item(i);
if (pt) {
if (this.prevPoints.has(pt.identifier)) {
this.prevPoints.set(pt.identifier, pt);
@@ -100,11 +120,12 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
@action
- protected onTouchEnd = (e: TouchEvent): void => {
+ protected onTouchEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
// console.log(InteractionUtils.GetMyTargetTouches(e, this.prevPoints).length + " up");
// remove all the touches associated with the event
- for (let i = 0; i < e.changedTouches.length; i++) {
- const pt = e.changedTouches.item(i);
+ const te = me.touchEvent;
+ for (let i = 0; i < te.changedTouches.length; i++) {
+ const pt = te.changedTouches.item(i);
if (pt) {
if (this.prevPoints.has(pt.identifier)) {
this.prevPoints.delete(pt.identifier);
@@ -116,7 +137,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
console.log("clear");
}
this._touchDrag = false;
- e.stopPropagation();
+ te.stopPropagation();
// if (e.targetTouches.length === 0) {
@@ -126,11 +147,12 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
if (this.prevPoints.size === 0) {
this.cleanUpInteractions();
}
+ e.stopPropagation();
}
cleanUpInteractions = (): void => {
- document.removeEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.removeEndListeners();
}
handle1PointerMove = (e: TouchEvent): any => {
@@ -144,23 +166,43 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
handle1PointerDown = (e: React.TouchEvent): any => {
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
}
handle2PointersDown = (e: React.TouchEvent): any => {
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
}
handle1PointerHoldStart = (e: React.TouchEvent): any => {
e.stopPropagation();
e.preventDefault();
- document.removeEventListener("touchmove", this.onTouch);
+ this.removeMoveListeners();
+ }
+
+ addMoveListeners = () => {
+ const handler = (e: Event) => this.onTouch(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
+ document.addEventListener("dashOnTouchMove", handler);
+ this.moveDisposer = () => document.removeEventListener("dashOnTouchMove", handler);
+ }
+
+ removeMoveListeners = () => {
+ this.moveDisposer && this.moveDisposer();
+ }
+
+ addEndListeners = () => {
+ const handler = (e: Event) => this.onTouchEnd(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
+ document.addEventListener("dashOnTouchEnd", handler);
+ this.endDisposer = () => document.removeEventListener("dashOnTouchEnd", handler);
+ }
+
+ removeEndListeners = () => {
+ this.endDisposer && this.endDisposer();
}
handleHandDown = (e: React.TouchEvent) => {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 992820fc7..b8423af20 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -153,7 +153,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
createRef = (ele: HTMLDivElement | null) => {
this._masonryGridRef = ele;
- this.createDropAndGestureTarget(ele!); //so the whole grid is the drop target?
+ this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
}
overlays = (doc: Doc) => {
@@ -309,7 +309,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
docList={docList}
parent={this}
type={type}
- createDropTarget={this.createDropAndGestureTarget}
+ createDropTarget={this.createDashEventsTarget}
screenToLocalTransform={this.props.ScreenToLocalTransform}
/>;
}
@@ -342,7 +342,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
docList={docList}
parent={this}
type={type}
- createDropTarget={this.createDropAndGestureTarget}
+ createDropTarget={this.createDashEventsTarget}
screenToLocalTransform={this.props.ScreenToLocalTransform}
setDocHeight={this.setDocHeight}
/>;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 73dc7edc6..4133956fc 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -24,6 +24,7 @@ import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
import { ImageUtils } from "../../util/Import & Export/ImageUtils";
import { Networking } from "../../Network";
import { GestureUtils } from "../../../pen-gestures/GestureUtils";
+import { InteractionUtils } from "../../util/InteractionUtils";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc) => boolean;
@@ -49,17 +50,20 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
class CollectionSubView extends DocComponent<SubCollectionViewProps, T>(schemaCtor) {
private dropDisposer?: DragManager.DragDropDisposer;
private gestureDisposer?: GestureUtils.GestureEventDisposer;
+ protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
private _childLayoutDisposer?: IReactionDisposer;
- protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this.dropDisposer && this.dropDisposer();
this.gestureDisposer && this.gestureDisposer();
+ this.multiTouchDisposer && this.multiTouchDisposer();
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
+ this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
}
}
protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
- this.createDropAndGestureTarget(ele);
+ this.createDashEventsTarget(ele);
}
componentDidMount() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 84945c6e6..3894e9d63 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -326,29 +326,30 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
handle1PointerDown = (e: React.TouchEvent) => {
- const pt = e.targetTouches.item(0);
- if (pt) {
- this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false;
- if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) {
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
- // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) {
- // e.stopPropagation();
- // e.preventDefault();
- // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY);
- // this._points.push({ X: point[0], Y: point[1] });
- // }
- if (InkingControl.Instance.selectedTool === InkTool.None) {
- this._lastX = pt.pageX;
- this._lastY = pt.pageY;
- e.stopPropagation();
- e.preventDefault();
- }
- else {
- e.stopPropagation();
- e.preventDefault();
+ if (!e.nativeEvent.cancelBubble) {
+ // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
+ const pt = e.changedTouches.item(0);
+ if (pt) {
+ this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false;
+ if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) {
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
+ // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) {
+ // e.stopPropagation();
+ // e.preventDefault();
+ // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY);
+ // this._points.push({ X: point[0], Y: point[1] });
+ // }
+ if (InkingControl.Instance.selectedTool === InkTool.None) {
+ this._lastX = pt.pageX;
+ this._lastY = pt.pageY;
+ e.preventDefault();
+ }
+ else {
+ e.preventDefault();
+ }
}
}
}
@@ -395,8 +396,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- document.removeEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.removeEndListeners();
}
@action
@@ -482,7 +483,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
this.pan(pt);
}
}
- e.stopPropagation();
+ // e.stopPropagation();
e.preventDefault();
}
}
@@ -527,7 +528,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
}
- e.stopPropagation();
+ // e.stopPropagation();
e.preventDefault();
}
}
@@ -535,18 +536,21 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
handle2PointersDown = (e: React.TouchEvent) => {
if (!e.nativeEvent.cancelBubble && this.props.active(true)) {
- const pt1: React.Touch | null = e.targetTouches.item(0);
- const pt2: React.Touch | null = e.targetTouches.item(1);
- if (!pt1 || !pt2) return;
+ // const pt1: React.Touch | null = e.targetTouches.item(0);
+ // const pt2: React.Touch | null = e.targetTouches.item(1);
+ // // if (!pt1 || !pt2) return;
+ const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
+ const pt1 = myTouches[0];
+ const pt2 = myTouches[1];
const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
this._lastX = centerX;
this._lastY = centerY;
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
e.stopPropagation();
}
}
@@ -554,8 +558,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
cleanUpInteractions = () => {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- document.removeEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.removeEndListeners();
}
@action
@@ -989,9 +993,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
if (!this.extensionDoc) return (null);
// let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale;
- return <div className={"collectionfreeformview-container"} ref={this.createDropAndGestureTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
+ return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}>
{!this.Document.LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2e0ae09ba..b33bebe7d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -103,6 +103,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
private _titleRef = React.createRef<EditableView>();
+ protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+
public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
public get ContentDiv() { return this._mainCont.current; }
@computed get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
@@ -177,6 +179,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
componentDidMount() {
this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)));
+ this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)));
!this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this);
}
@@ -184,7 +187,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
componentDidUpdate() {
this._dropDisposer && this._dropDisposer();
+ this._gestureEventDisposer && this._gestureEventDisposer();
+ this.multiTouchDisposer && this.multiTouchDisposer();
this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
+ this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)));
+ this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)));
}
@action
@@ -319,25 +326,24 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation();
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
- if ((e.nativeEvent as any).formattedHandled) e.stopPropagation();
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
+ e.stopPropagation();
}
}
handle1PointerMove = (e: TouchEvent) => {
if ((e as any).formattedHandled) { e.stopPropagation; return; }
if (e.cancelBubble && this.active) {
- document.removeEventListener("touchmove", this.onTouch);
+ this.removeMoveListeners();
}
else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) {
const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0];
if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) {
if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) {
- document.removeEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
+ this.cleanUpInteractions();
this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
}
}
@@ -352,10 +358,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.stopPropagation();
e.preventDefault();
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
}
}
@@ -970,7 +976,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
width: animwidth,
height: animheight,
opacity: this.Document.opacity
- }} onTouchStart={this.onTouchStart}>
+ }}>
{this.innards}
</div>;
}
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index 062604458..4b5ad6684 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -31,7 +31,7 @@ export namespace GestureUtils {
element.addEventListener("dashOnGesture", handler);
return () => {
element.removeEventListener("dashOnGesture", handler);
- }
+ };
}
export enum Gestures {