aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorStanley Yip <stanley_yip@brown.edu>2020-01-27 19:08:05 -0500
committerStanley Yip <stanley_yip@brown.edu>2020-01-27 19:08:05 -0500
commitb04bb12a5942d97eef369936e30b36db62b51f30 (patch)
tree058211fb20189a58b5b19b6295d64a85803b6318 /src/client
parent691d5500c0722d0af9ef3b3ab9036042970e75e9 (diff)
pen updates
Diffstat (limited to 'src/client')
-rw-r--r--src/client/util/InteractionUtils.tsx5
-rw-r--r--src/client/views/GestureOverlay.tsx52
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx28
4 files changed, 66 insertions, 21 deletions
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 1fe95474c..ad1d1b5de 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -63,7 +63,7 @@ export namespace InteractionUtils {
export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent<React.TouchEvent | TouchEvent>, prevPoints: Map<number, React.Touch>, ignorePen: boolean): React.Touch[] {
const myTouches = new Array<React.Touch>();
for (const pt of mte.touches) {
- if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) {
+ if (!ignorePen || ((pt as any).radiusX > 1 && (pt as any).radiusY > 1)) {
for (const tPt of mte.targetTouches) {
if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
if (pt && prevPoints.has(pt.identifier)) {
@@ -73,6 +73,9 @@ export namespace InteractionUtils {
}
}
}
+ if (mte.touches.length !== myTouches.length) {
+ throw Error("opo")
+ }
return myTouches;
}
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index e8d685139..9dd87554f 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -20,6 +20,12 @@ import { DocumentView } from "./nodes/DocumentView";
import { Transform } from "../util/Transform";
import { DocumentContentsView } from "./nodes/DocumentContentsView";
+/**
+ * This class handles all of the gesture and touch events first. Native touch and pen
+ * events should be ignored by all classes and handled up here, and this class will interpret
+ * these events before dispatching our custom Dash gesture and touch events. Classes that want
+ * to use touch and pen events should handle custom Dash events, as opposed to native events.
+ */
@observer
export default class GestureOverlay extends Touchable {
static Instance: GestureOverlay;
@@ -54,6 +60,15 @@ export default class GestureOverlay extends Touchable {
GestureOverlay.Instance = this;
}
+ /**
+ * @description
+ * Given a touch event, returns three arrays that represent the event's targetTouches,
+ * changedTouches, and touches after filtering out the touch events that are being handled
+ * as hands. This helps us separate hand events and touch events, as they are different
+ * events in the mental model that we are pursuing.
+ * @param e - Touch event to filter
+ * @returns \{ newTargetTouches, newChangedTouches, newTouches }
+ */
getNewTouches(e: React.TouchEvent | TouchEvent) {
const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches);
const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches);
@@ -61,6 +76,7 @@ export default class GestureOverlay extends Touchable {
this._hands.forEach((hand) => {
for (let i = 0; i < e.targetTouches.length; i++) {
const pt = e.targetTouches.item(i);
+ // if there is a finger in this hand that matches the current point, ignore the current point
if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
ntt.splice(ntt.indexOf(pt), 1);
}
@@ -83,7 +99,11 @@ export default class GestureOverlay extends Touchable {
return { ntt, nct, nt };
}
+ /**
+ * @description Handler for the native React touchStart event.
+ */
onReactTouchStart = (te: React.TouchEvent) => {
+ // clean up any ghost points that are remaining but don't actually exist
const actualPts: React.Touch[] = [];
for (let i = 0; i < te.touches.length; i++) {
const pt: any = te.touches.item(i);
@@ -91,9 +111,6 @@ export default class GestureOverlay extends Touchable {
// 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);
}
}
@@ -106,10 +123,11 @@ export default class GestureOverlay extends Touchable {
});
ptsToDelete.forEach(pt => this.prevPoints.delete(pt));
- const nts = this.getNewTouches(te);
- console.log(nts.nt.length);
+ // decide whether we should be handling this as a hand event or a touch event
+ const nts = this.getNewTouches(te);
if (nts.nt.length < 5) {
+ // dispatch a touch event
const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY);
target?.dispatchEvent(
new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>("dashOnTouchStart",
@@ -131,12 +149,17 @@ export default class GestureOverlay extends Touchable {
document.addEventListener("touchend", this.onReactTouchEnd);
}
else {
+ // handle this event as a hand event
this.handleHandDown(te);
document.removeEventListener("touchmove", this.onReactTouchMove);
document.removeEventListener("touchend", this.onReactTouchEnd);
}
}
+ /**
+ * @description Handler for the native React touchMove event. Filters and dispatches
+ * the custom Dash touchMove event.
+ */
onReactTouchMove = (e: TouchEvent) => {
const nts: any = this.getNewTouches(e);
document.dispatchEvent(
@@ -154,7 +177,11 @@ export default class GestureOverlay extends Touchable {
);
}
+ /**
+ * @description Handler for the native React touchEnd event.
+ */
onReactTouchEnd = (e: TouchEvent) => {
+ // filter and dispatch custom touchEnd event
const nts: any = this.getNewTouches(e);
document.dispatchEvent(
new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchEnd",
@@ -169,6 +196,8 @@ export default class GestureOverlay extends Touchable {
}
})
);
+
+ // clean up any points that have ended
for (let i = 0; i < e.changedTouches.length; i++) {
const pt = e.changedTouches.item(i);
if (pt) {
@@ -178,6 +207,7 @@ export default class GestureOverlay extends Touchable {
}
}
+ // clean up events
if (this.prevPoints.size === 0) {
document.removeEventListener("touchmove", this.onReactTouchMove);
document.removeEventListener("touchend", this.onReactTouchEnd);
@@ -185,8 +215,12 @@ export default class GestureOverlay extends Touchable {
e.stopPropagation();
}
+ /**
+ * @description Handler for "handDown" events
+ */
handleHandDown = async (e: React.TouchEvent) => {
const fingers = new Array<React.Touch>();
+ // log all the fingers on the hand
for (let i = 0; i < e.touches.length; i++) {
const pt: any = e.touches.item(i);
if (pt.radiusX > 1 && pt.radiusY > 1) {
@@ -200,6 +234,8 @@ export default class GestureOverlay extends Touchable {
}
}
}
+
+ // figure out left/right hand and thumb/pointer finger
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));
@@ -229,6 +265,7 @@ export default class GestureOverlay extends Touchable {
const minX = Math.min(...others.map(f => f.clientX));
const minY = Math.min(...others.map(f => f.clientY));
+ // pull up the palette
const thumbDoc = await Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc);
if (thumbDoc) {
runInAction(() => {
@@ -246,6 +283,9 @@ export default class GestureOverlay extends Touchable {
document.addEventListener("touchend", this.handleHandUp);
}
+ /**
+ * @description Handler for "handMove" event
+ */
@action
handleHandMove = (e: TouchEvent) => {
const fingers = new Array<React.Touch>();
@@ -370,7 +410,7 @@ export default class GestureOverlay extends Touchable {
else {
const result = GestureUtils.GestureRecognizer.Recognize(new Array(points));
let actionPerformed = false;
- if (result && result.Score > 0.7) {
+ if (result && result.Score > 0.8) {
switch (result.Name) {
case GestureUtils.Gestures.Box:
const target = document.elementFromPoint(this._points[0].X, this._points[0].Y);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 132bf9c8e..a0c75c1b7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -347,7 +347,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
this._lastX = pt.pageX;
this._lastY = pt.pageY;
e.preventDefault();
- e.stopPropagation();
+ // e.stopPropagation();
}
else {
e.preventDefault();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e0913b154..82ee1bd63 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -316,21 +316,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
if (this.Document.onPointerDown) return;
const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0];
- this._downX = touch.clientX;
- this._downY = touch.clientY;
- if (!e.nativeEvent.cancelBubble) {
- this._hitTemplateDrag = false;
- for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
- if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
- this._hitTemplateDrag = true;
+ if (touch) {
+ this._downX = touch.clientX;
+ this._downY = touch.clientY;
+ if (!e.nativeEvent.cancelBubble) {
+ this._hitTemplateDrag = false;
+ for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
+ if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
+ this._hitTemplateDrag = true;
+ }
}
+ if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation();
+ this.removeMoveListeners();
+ this.addMoveListeners();
+ this.removeEndListeners();
+ this.addEndListeners();
+ e.stopPropagation();
}
- if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation();
- this.removeMoveListeners();
- this.addMoveListeners();
- this.removeEndListeners();
- this.addEndListeners();
- e.stopPropagation();
}
}