Sfoglia il codice sorgente

Add support for long press to context menu on iOS (#1769)

* Initial support for touch context menu

* Only deal with touch if it's available

* Fix touch checking

* Remove touch checking

* Added comments

* Combine onTouch with onPointer for mobile context menu support
Mehedi Hassan 5 anni fa
parent
commit
cc52ea4ac2
2 ha cambiato i file con 48 aggiunte e 5 eliminazioni
  1. 47 5
      src/components/App.tsx
  2. 1 0
      src/time_constants.ts

+ 47 - 5
src/components/App.tsx

@@ -125,6 +125,7 @@ import {
   INITAL_SCENE_UPDATE_TIMEOUT,
   TAP_TWICE_TIMEOUT,
   SYNC_FULL_SCENE_INTERVAL_MS,
+  TOUCH_CTX_MENU_TIMEOUT,
 } from "../time_constants";
 
 import LayerUI from "./LayerUI";
@@ -172,6 +173,8 @@ let isHoldingSpace: boolean = false;
 let isPanning: boolean = false;
 let isDraggingScrollBar: boolean = false;
 let currentScrollBars: ScrollBars = { horizontal: null, vertical: null };
+let touchTimeout = 0;
+let touchMoving = false;
 
 let lastPointerUp: ((event: any) => void) | null = null;
 const gesture: Gesture = {
@@ -256,6 +259,7 @@ class App extends React.Component<any, AppState> {
             onPointerMove={this.handleCanvasPointerMove}
             onPointerUp={this.removePointer}
             onPointerCancel={this.removePointer}
+            onTouchMove={this.handleTouchMove}
             onDrop={this.handleCanvasOnDrop}
           >
             {t("labels.drawingCanvas")}
@@ -409,6 +413,8 @@ class App extends React.Component<any, AppState> {
     this.unmounted = true;
     this.removeSceneCallback!();
     this.removeEventListeners();
+
+    clearTimeout(touchTimeout);
   }
 
   private onResize = withBatchedUpdates(() => {
@@ -818,6 +824,12 @@ class App extends React.Component<any, AppState> {
   };
 
   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);
   };
 
@@ -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 = (
     event: React.PointerEvent<HTMLCanvasElement>,
   ) => {
     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) {
       // 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
@@ -2847,9 +2880,18 @@ class App extends React.Component<any, AppState> {
     event: React.PointerEvent<HTMLCanvasElement>,
   ) => {
     event.preventDefault();
+    this.openContextMenu(event);
+  };
 
+  private openContextMenu = ({
+    clientX,
+    clientY,
+  }: {
+    clientX: number;
+    clientY: number;
+  }) => {
     const { x, y } = viewportCoordsToSceneCoords(
-      event,
+      { clientX, clientY },
       this.state,
       this.canvas,
       window.devicePixelRatio,
@@ -2888,8 +2930,8 @@ class App extends React.Component<any, AppState> {
             action: this.toggleGridMode,
           },
         ],
-        top: event.clientY,
-        left: event.clientX,
+        top: clientY,
+        left: clientX,
       });
       return;
     }
@@ -2920,8 +2962,8 @@ class App extends React.Component<any, AppState> {
           (action) => !CANVAS_ONLY_ACTIONS.includes(action.name),
         ),
       ],
-      top: event.clientY,
-      left: event.clientX,
+      top: clientY,
+      left: clientX,
     });
   };
 

+ 1 - 0
src/time_constants.ts

@@ -2,3 +2,4 @@
 export const TAP_TWICE_TIMEOUT = 300;
 export const INITAL_SCENE_UPDATE_TIMEOUT = 5000;
 export const SYNC_FULL_SCENE_INTERVAL_MS = 20000;
+export const TOUCH_CTX_MENU_TIMEOUT = 500;