aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/InteractionUtils.ts153
-rw-r--r--src/client/views/Touchable.tsx34
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx52
-rw-r--r--src/client/views/nodes/DocumentView.tsx10
4 files changed, 150 insertions, 99 deletions
diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts
index e58635a6f..d5799c21f 100644
--- a/src/client/util/InteractionUtils.ts
+++ b/src/client/util/InteractionUtils.ts
@@ -1,9 +1,23 @@
export namespace InteractionUtils {
- export const MOUSE = "mouse";
- export const TOUCH = "touch";
+ export const MOUSETYPE = "mouse";
+ export const TOUCHTYPE = "touch";
+ export const PENTYPE = "pen";
+ export const ERASERTYPE = "eraser";
+
+ const POINTER_PEN_BUTTON = -1;
+ const REACT_POINTER_PEN_BUTTON = 0;
+ const ERASER_BUTTON = 5;
export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean {
- return e.pointerType === type;
+ switch (type) {
+ // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
+ case PENTYPE:
+ return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? POINTER_PEN_BUTTON : REACT_POINTER_PEN_BUTTON);
+ case ERASERTYPE:
+ return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
+ default:
+ return e.pointerType === type;
+ }
}
export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number {
@@ -42,70 +56,75 @@ export namespace InteractionUtils {
return 0;
}
- /**
- * Returns the type of Touch Interaction from a list of points.
- * Also returns any data that is associated with a Touch Interaction
- * @param pts - List of points
- */
- // export function InterpretPointers(pts: React.Touch[]): { type: Opt<TouchInteraction>, data?: any } {
- // const leniency = 200;
- // switch (pts.length) {
- // case 1:
- // return { type: OneFinger };
- // case 2:
- // return { type: TwoSeperateFingers };
- // case 3:
- // let pt1 = pts[0];
- // let pt2 = pts[1];
- // let pt3 = pts[2];
- // if (pt1 && pt2 && pt3) {
- // let dist12 = TwoPointEuclidist(pt1, pt2);
- // let dist23 = TwoPointEuclidist(pt2, pt3);
- // let dist13 = TwoPointEuclidist(pt1, pt3);
- // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`);
- // let dist12close = dist12 < leniency;
- // let dist23close = dist23 < leniency;
- // let dist13close = dist13 < leniency;
- // let xor2313 = dist23close ? !dist13close : dist13close;
- // let xor = dist12close ? !xor2313 : xor2313;
- // // three input xor because javascript doesn't have logical xor's
- // if (xor) {
- // let points: number[] = [];
- // let min = Math.min(dist12, dist23, dist13);
- // switch (min) {
- // case dist12:
- // points = [0, 1, 2];
- // break;
- // case dist23:
- // points = [1, 2, 0];
- // break;
- // case dist13:
- // points = [0, 2, 1];
- // break;
- // }
- // return { type: TwoToOneFingers, data: points };
- // }
- // else {
- // return { type: ThreeSeperateFingers, data: null };
- // }
- // }
- // default:
- // return { type: undefined };
- // }
- // }
+ // These might not be very useful anymore, but I'll leave them here for now -syip2
+ {
- export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: TouchList, leniency: number): boolean {
- for (let i = 0; i < newTouches.length; i++) {
- let touch = newTouches.item(i);
- if (touch) {
- let oldTouch = oldTouches.get(touch.identifier);
- if (oldTouch) {
- if (TwoPointEuclidist(touch, oldTouch) >= leniency) {
- return true;
- }
- }
- }
- }
- return false;
+
+ /**
+ * Returns the type of Touch Interaction from a list of points.
+ * Also returns any data that is associated with a Touch Interaction
+ * @param pts - List of points
+ */
+ // export function InterpretPointers(pts: React.Touch[]): { type: Opt<TouchInteraction>, data?: any } {
+ // const leniency = 200;
+ // switch (pts.length) {
+ // case 1:
+ // return { type: OneFinger };
+ // case 2:
+ // return { type: TwoSeperateFingers };
+ // case 3:
+ // let pt1 = pts[0];
+ // let pt2 = pts[1];
+ // let pt3 = pts[2];
+ // if (pt1 && pt2 && pt3) {
+ // let dist12 = TwoPointEuclidist(pt1, pt2);
+ // let dist23 = TwoPointEuclidist(pt2, pt3);
+ // let dist13 = TwoPointEuclidist(pt1, pt3);
+ // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`);
+ // let dist12close = dist12 < leniency;
+ // let dist23close = dist23 < leniency;
+ // let dist13close = dist13 < leniency;
+ // let xor2313 = dist23close ? !dist13close : dist13close;
+ // let xor = dist12close ? !xor2313 : xor2313;
+ // // three input xor because javascript doesn't have logical xor's
+ // if (xor) {
+ // let points: number[] = [];
+ // let min = Math.min(dist12, dist23, dist13);
+ // switch (min) {
+ // case dist12:
+ // points = [0, 1, 2];
+ // break;
+ // case dist23:
+ // points = [1, 2, 0];
+ // break;
+ // case dist13:
+ // points = [0, 2, 1];
+ // break;
+ // }
+ // return { type: TwoToOneFingers, data: points };
+ // }
+ // else {
+ // return { type: ThreeSeperateFingers, data: null };
+ // }
+ // }
+ // default:
+ // return { type: undefined };
+ // }
+ // }
+
+ // export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: TouchList, leniency: number): boolean {
+ // for (let i = 0; i < newTouches.length; i++) {
+ // let touch = newTouches.item(i);
+ // if (touch) {
+ // let oldTouch = oldTouches.get(touch.identifier);
+ // if (oldTouch) {
+ // if (TwoPointEuclidist(touch, oldTouch) >= leniency) {
+ // return true;
+ // }
+ // }
+ // }
+ // }
+ // return false;
+ // }
}
} \ No newline at end of file
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 326e8e578..dbadc27ea 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -17,22 +17,28 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
@action
protected onTouchStart = (e: React.TouchEvent): void => {
for (let i = 0; i < e.targetTouches.length; i++) {
- let pt = e.targetTouches.item(i);
- this.prevPoints.set(pt.identifier, pt);
+ let pt: any = e.targetTouches.item(i);
+ // pen is also a touch, but with a radius of 0.5 (at least with the surface pens). i doubt anyone's fingers are 2 pixels wide,
+ // and this seems to be the only way of differentiating pen and touch on touch events
+ if (pt.radiusX > 2 && pt.radiusY > 2) {
+ this.prevPoints.set(pt.identifier, pt);
+ }
}
- switch (e.targetTouches.length) {
- case 1:
- this.handle1PointerDown(e);
- break;
- case 2:
- this.handle2PointersDown(e);
- }
+ if (this.prevPoints.size) {
+ switch (e.targetTouches.length) {
+ case 1:
+ this.handle1PointerDown(e);
+ break;
+ case 2:
+ this.handle2PointersDown(e);
+ }
- document.removeEventListener("touchmove", this.onTouch);
- document.addEventListener("touchmove", this.onTouch);
- document.removeEventListener("touchend", this.onTouchEnd);
- document.addEventListener("touchend", this.onTouchEnd);
+ document.removeEventListener("touchmove", this.onTouch);
+ document.addEventListener("touchmove", this.onTouch);
+ document.removeEventListener("touchend", this.onTouchEnd);
+ document.addEventListener("touchend", this.onTouchEnd);
+ }
}
/**
@@ -45,7 +51,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
this._touchDrag = true;
switch (e.targetTouches.length) {
case 1:
- this.handle1PointerMove(e)
+ this.handle1PointerMove(e);
break;
case 2:
this.handle2PointersMove(e);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3dd655b96..03f860b32 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -26,7 +26,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingControl } from "../../InkingControl";
-import { CreatePolyline } from "../../InkingStroke";
+import { CreatePolyline, InkingStroke } from "../../InkingStroke";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentViewProps } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
@@ -282,20 +282,43 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointerup", this.onPointerUp);
- if (InkingControl.Instance.selectedTool === InkTool.None) {
+ // if physically using a pen or we're in pen or highlighter mode
+ if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
+ e.stopPropagation();
+ e.preventDefault();
+ let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ this._points.push({ x: point[0], y: point[1] });
+ }
+ // if not using a pen and in no ink mode
+ else if (InkingControl.Instance.selectedTool === InkTool.None) {
this._lastX = e.pageX;
this._lastY = e.pageY;
}
+ // eraser or scrubber plus anything else mode
else {
e.stopPropagation();
e.preventDefault();
-
- if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- let point = this.getTransform().transformPoint(e.pageX, e.pageY);
- this._points.push({ x: point[0], y: point[1] });
- }
}
}
+ // if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) {
+ // document.removeEventListener("pointermove", this.onPointerMove);
+ // document.removeEventListener("pointerup", this.onPointerUp);
+ // document.addEventListener("pointermove", this.onPointerMove);
+ // document.addEventListener("pointerup", this.onPointerUp);
+ // if (InkingControl.Instance.selectedTool === InkTool.None) {
+ // this._lastX = e.pageX;
+ // this._lastY = e.pageY;
+ // }
+ // else {
+ // e.stopPropagation();
+ // e.preventDefault();
+
+ // if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
+ // let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ // this._points.push({ x: point[0], y: point[1] });
+ // }
+ // }
+ // }
}
@action
@@ -308,7 +331,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerUp = (e: PointerEvent): void => {
- if (InteractionUtils.IsType(e, InteractionUtils.TOUCH) && this._points.length <= 1) return;
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && this._points.length <= 1) return;
if (this._points.length > 1) {
let B = this.svgBounds;
@@ -364,14 +387,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
onPointerMove = (e: PointerEvent): void => {
- if (InteractionUtils.IsType(e, InteractionUtils.TOUCH)) {
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
if (this.props.active(true)) {
e.stopPropagation();
}
return;
}
if (!e.cancelBubble) {
- if (InkingControl.Instance.selectedTool === InkTool.None) {
+ const selectedTool = InkingControl.Instance.selectedTool;
+ if (selectedTool === InkTool.Highlighter || selectedTool === InkTool.Pen || InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ let point = this.getTransform().transformPoint(e.clientX, e.clientY);
+ this._points.push({ x: point[0], y: point[1] });
+ }
+ else if (selectedTool === InkTool.None) {
if (this._hitCluster && this.tryDragCluster(e)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
@@ -381,10 +409,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
this.pan(e);
}
- else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- let point = this.getTransform().transformPoint(e.clientX, e.clientY);
- this._points.push({ x: point[0], y: point[1] });
- }
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3a600e0fd..687106a9e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -193,9 +193,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onPointerDown = (e: React.PointerEvent): void => {
- if ((e.nativeEvent.cancelBubble && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCH)))
+ if ((e.nativeEvent.cancelBubble && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)))
// return if we're inking, and not selecting a button document
- || (InkingControl.Instance.selectedTool !== InkTool.None && !this.Document.onClick)) return;
+ || (InkingControl.Instance.selectedTool !== InkTool.None && !this.Document.onClick)
+ // return if using pen or eraser
+ || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) return;
this._downX = e.clientX;
this._downY = e.clientY;
this._hitTemplateDrag = false;
@@ -206,7 +208,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._hitTemplateDrag = true;
}
}
- if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCH)) && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
+ if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
@@ -221,7 +223,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) {
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
- if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCH))) {
+ if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);