|
@@ -125,6 +125,7 @@ import {
|
|
INITAL_SCENE_UPDATE_TIMEOUT,
|
|
INITAL_SCENE_UPDATE_TIMEOUT,
|
|
TAP_TWICE_TIMEOUT,
|
|
TAP_TWICE_TIMEOUT,
|
|
SYNC_FULL_SCENE_INTERVAL_MS,
|
|
SYNC_FULL_SCENE_INTERVAL_MS,
|
|
|
|
+ TOUCH_CTX_MENU_TIMEOUT,
|
|
} from "../time_constants";
|
|
} from "../time_constants";
|
|
|
|
|
|
import LayerUI from "./LayerUI";
|
|
import LayerUI from "./LayerUI";
|
|
@@ -172,6 +173,8 @@ let isHoldingSpace: boolean = false;
|
|
let isPanning: boolean = false;
|
|
let isPanning: boolean = false;
|
|
let isDraggingScrollBar: boolean = false;
|
|
let isDraggingScrollBar: boolean = false;
|
|
let currentScrollBars: ScrollBars = { horizontal: null, vertical: null };
|
|
let currentScrollBars: ScrollBars = { horizontal: null, vertical: null };
|
|
|
|
+let touchTimeout = 0;
|
|
|
|
+let touchMoving = false;
|
|
|
|
|
|
let lastPointerUp: ((event: any) => void) | null = null;
|
|
let lastPointerUp: ((event: any) => void) | null = null;
|
|
const gesture: Gesture = {
|
|
const gesture: Gesture = {
|
|
@@ -256,6 +259,7 @@ class App extends React.Component<any, AppState> {
|
|
onPointerMove={this.handleCanvasPointerMove}
|
|
onPointerMove={this.handleCanvasPointerMove}
|
|
onPointerUp={this.removePointer}
|
|
onPointerUp={this.removePointer}
|
|
onPointerCancel={this.removePointer}
|
|
onPointerCancel={this.removePointer}
|
|
|
|
+ onTouchMove={this.handleTouchMove}
|
|
onDrop={this.handleCanvasOnDrop}
|
|
onDrop={this.handleCanvasOnDrop}
|
|
>
|
|
>
|
|
{t("labels.drawingCanvas")}
|
|
{t("labels.drawingCanvas")}
|
|
@@ -409,6 +413,8 @@ class App extends React.Component<any, AppState> {
|
|
this.unmounted = true;
|
|
this.unmounted = true;
|
|
this.removeSceneCallback!();
|
|
this.removeSceneCallback!();
|
|
this.removeEventListeners();
|
|
this.removeEventListeners();
|
|
|
|
+
|
|
|
|
+ clearTimeout(touchTimeout);
|
|
}
|
|
}
|
|
|
|
|
|
private onResize = withBatchedUpdates(() => {
|
|
private onResize = withBatchedUpdates(() => {
|
|
@@ -818,6 +824,12 @@ class App extends React.Component<any, AppState> {
|
|
};
|
|
};
|
|
|
|
|
|
removePointer = (event: React.PointerEvent<HTMLElement>) => {
|
|
removePointer = (event: React.PointerEvent<HTMLElement>) => {
|
|
|
|
+ // remove touch handler for context menu on touch devices
|
|
|
|
+ if (event.pointerType === "touch" && touchTimeout) {
|
|
|
|
+ clearTimeout(touchTimeout);
|
|
|
|
+ touchMoving = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
gesture.pointers.delete(event.pointerId);
|
|
gesture.pointers.delete(event.pointerId);
|
|
};
|
|
};
|
|
|
|
|
|
@@ -1802,11 +1814,32 @@ class App extends React.Component<any, AppState> {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ // set touch moving for mobile context menu
|
|
|
|
+ private handleTouchMove = (event: React.TouchEvent<HTMLCanvasElement>) => {
|
|
|
|
+ touchMoving = true;
|
|
|
|
+ };
|
|
|
|
+
|
|
private handleCanvasPointerDown = (
|
|
private handleCanvasPointerDown = (
|
|
event: React.PointerEvent<HTMLCanvasElement>,
|
|
event: React.PointerEvent<HTMLCanvasElement>,
|
|
) => {
|
|
) => {
|
|
event.persist();
|
|
event.persist();
|
|
|
|
|
|
|
|
+ // deal with opening context menu on touch devices
|
|
|
|
+ if (event.pointerType === "touch") {
|
|
|
|
+ touchMoving = false;
|
|
|
|
+
|
|
|
|
+ // open the context menu with the first touch's clientX and clientY
|
|
|
|
+ // if the touch is not moving
|
|
|
|
+ touchTimeout = window.setTimeout(() => {
|
|
|
|
+ if (!touchMoving) {
|
|
|
|
+ this.openContextMenu({
|
|
|
|
+ clientX: event.clientX,
|
|
|
|
+ clientY: event.clientY,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }, TOUCH_CTX_MENU_TIMEOUT);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (lastPointerUp !== null) {
|
|
if (lastPointerUp !== null) {
|
|
// Unfortunately, sometimes we don't get a pointerup after a pointerdown,
|
|
// Unfortunately, sometimes we don't get a pointerup after a pointerdown,
|
|
// this can happen when a contextual menu or alert is triggered. In order to avoid
|
|
// this can happen when a contextual menu or alert is triggered. In order to avoid
|
|
@@ -2847,9 +2880,18 @@ class App extends React.Component<any, AppState> {
|
|
event: React.PointerEvent<HTMLCanvasElement>,
|
|
event: React.PointerEvent<HTMLCanvasElement>,
|
|
) => {
|
|
) => {
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
|
|
+ this.openContextMenu(event);
|
|
|
|
+ };
|
|
|
|
|
|
|
|
+ private openContextMenu = ({
|
|
|
|
+ clientX,
|
|
|
|
+ clientY,
|
|
|
|
+ }: {
|
|
|
|
+ clientX: number;
|
|
|
|
+ clientY: number;
|
|
|
|
+ }) => {
|
|
const { x, y } = viewportCoordsToSceneCoords(
|
|
const { x, y } = viewportCoordsToSceneCoords(
|
|
- event,
|
|
|
|
|
|
+ { clientX, clientY },
|
|
this.state,
|
|
this.state,
|
|
this.canvas,
|
|
this.canvas,
|
|
window.devicePixelRatio,
|
|
window.devicePixelRatio,
|
|
@@ -2888,8 +2930,8 @@ class App extends React.Component<any, AppState> {
|
|
action: this.toggleGridMode,
|
|
action: this.toggleGridMode,
|
|
},
|
|
},
|
|
],
|
|
],
|
|
- top: event.clientY,
|
|
|
|
- left: event.clientX,
|
|
|
|
|
|
+ top: clientY,
|
|
|
|
+ left: clientX,
|
|
});
|
|
});
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -2920,8 +2962,8 @@ class App extends React.Component<any, AppState> {
|
|
(action) => !CANVAS_ONLY_ACTIONS.includes(action.name),
|
|
(action) => !CANVAS_ONLY_ACTIONS.includes(action.name),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
- top: event.clientY,
|
|
|
|
- left: event.clientX,
|
|
|
|
|
|
+ top: clientY,
|
|
|
|
+ left: clientX,
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|