Browse Source

confirm arrow on doubleclick (#949)

* confirm arrow on double click

* change hint

* fix cursor not updating on click
David Luzar 5 years ago
parent
commit
b7da524538

+ 9 - 6
src/actions/actionFinalize.tsx

@@ -18,12 +18,15 @@ export const actionFinalize = register({
     if (appState.multiElement) {
       // pen and mouse have hover
       if (appState.lastPointerDownWith !== "touch") {
-        mutateElement(appState.multiElement, {
-          points: appState.multiElement.points.slice(
-            0,
-            appState.multiElement.points.length - 1,
-          ),
-        });
+        const { points, lastCommittedPoint } = appState.multiElement;
+        if (
+          !lastCommittedPoint ||
+          points[points.length - 1] !== lastCommittedPoint
+        ) {
+          mutateElement(appState.multiElement, {
+            points: appState.multiElement.points.slice(0, -1),
+          });
+        }
       }
       if (isInvisiblySmallElement(appState.multiElement)) {
         newElements = newElements.slice(0, -1);

+ 10 - 0
src/actions/manager.tsx

@@ -60,6 +60,16 @@ export class ActionManager implements ActionsManagerInterface {
     return true;
   }
 
+  executeAction(action: Action) {
+    const commitToHistory =
+      action.commitToHistory &&
+      action.commitToHistory(this.getAppState(), this.getElements());
+    this.updater(
+      action.perform(this.getElements(), this.getAppState(), null),
+      commitToHistory,
+    );
+  }
+
   getContextMenuItems(actionFilter: ActionFilterFn = action => action) {
     return Object.values(this.actions)
       .filter(actionFilter)

+ 69 - 8
src/components/App.tsx

@@ -92,6 +92,7 @@ import {
   POINTER_BUTTON,
   DRAGGING_THRESHOLD,
   TEXT_TO_CENTER_SNAP_THRESHOLD,
+  ARROW_CONFIRM_THRESHOLD,
 } from "../constants";
 import { LayerUI } from "./LayerUI";
 import { ScrollBars } from "../scene/types";
@@ -102,6 +103,7 @@ import { unstable_batchedUpdates } from "react-dom";
 import { SceneStateCallbackRemover } from "../scene/globalScene";
 import { isLinearElement } from "../element/typeChecks";
 import { rescalePoints } from "../points";
+import { actionFinalize } from "../actions";
 
 function withBatchedUpdates<
   TFunction extends ((event: any) => void) | (() => void)
@@ -1122,13 +1124,52 @@ export class App extends React.Component<any, AppState> {
     );
     if (this.state.multiElement) {
       const { multiElement } = this.state;
-      const originX = multiElement.x;
-      const originY = multiElement.y;
-      const points = multiElement.points;
+      const { x: rx, y: ry } = multiElement;
 
-      mutateElement(multiElement, {
-        points: [...points.slice(0, -1), [x - originX, y - originY]],
-      });
+      const { points, lastCommittedPoint } = multiElement;
+      const lastPoint = points[points.length - 1];
+
+      setCursorForShape(this.state.elementType);
+
+      if (lastPoint === lastCommittedPoint) {
+        // if we haven't yet created a temp point and we're beyond commit-zone
+        //  threshold, add a point
+        if (
+          distance2d(x - rx, y - ry, lastPoint[0], lastPoint[1]) >=
+          ARROW_CONFIRM_THRESHOLD
+        ) {
+          mutateElement(multiElement, {
+            points: [...points, [x - rx, y - ry]],
+          });
+        } else {
+          document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
+          // in this branch, we're inside the commit zone, and no uncommitted
+          //  point exists. Thus do nothing (don't add/remove points).
+        }
+      } else {
+        // cursor moved inside commit zone, and there's uncommitted point,
+        //  thus remove it
+        if (
+          points.length > 2 &&
+          lastCommittedPoint &&
+          distance2d(
+            x - rx,
+            y - ry,
+            lastCommittedPoint[0],
+            lastCommittedPoint[1],
+          ) < ARROW_CONFIRM_THRESHOLD
+        ) {
+          document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
+          mutateElement(multiElement, {
+            points: points.slice(0, -1),
+          });
+        } else {
+          // update last uncommitted point
+          mutateElement(multiElement, {
+            points: [...points.slice(0, -1), [x - rx, y - ry]],
+          });
+        }
+      }
       return;
     }
 
@@ -1505,16 +1546,36 @@ export class App extends React.Component<any, AppState> {
     ) {
       if (this.state.multiElement) {
         const { multiElement } = this.state;
-        const { x: rx, y: ry } = multiElement;
+
+        const { x: rx, y: ry, lastCommittedPoint } = multiElement;
+
+        // clicking inside commit zone → finalize arrow
+        if (
+          multiElement.points.length > 1 &&
+          lastCommittedPoint &&
+          distance2d(
+            x - rx,
+            y - ry,
+            lastCommittedPoint[0],
+            lastCommittedPoint[1],
+          ) < ARROW_CONFIRM_THRESHOLD
+        ) {
+          this.actionManager.executeAction(actionFinalize);
+          return;
+        }
         this.setState(prevState => ({
           selectedElementIds: {
             ...prevState.selectedElementIds,
             [multiElement.id]: true,
           },
         }));
+        // clicking outside commit zone → update reference for last committed
+        //  point
         mutateElement(multiElement, {
-          points: [...multiElement.points, [x - rx, y - ry]],
+          lastCommittedPoint:
+            multiElement.points[multiElement.points.length - 1],
         });
+        document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
       } else {
         const element = newLinearElement({
           type: this.state.elementType,

+ 2 - 0
src/constants.ts

@@ -1,4 +1,5 @@
 export const DRAGGING_THRESHOLD = 10; // 10px
+export const ARROW_CONFIRM_THRESHOLD = 10; // 10px
 export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
 export const ELEMENT_TRANSLATE_AMOUNT = 1;
 export const TEXT_TO_CENTER_SNAP_THRESHOLD = 30;
@@ -6,6 +7,7 @@ export const CURSOR_TYPE = {
   TEXT: "text",
   CROSSHAIR: "crosshair",
   GRABBING: "grabbing",
+  POINTER: "pointer",
 };
 export const POINTER_BUTTON = {
   MAIN: 0,

+ 3 - 1
src/element/newElement.ts

@@ -91,12 +91,14 @@ export function newTextElement(
 
 export function newLinearElement(
   opts: {
-    type: "arrow" | "line";
+    type: ExcalidrawLinearElement["type"];
+    lastCommittedPoint?: ExcalidrawLinearElement["lastCommittedPoint"];
   } & ElementConstructorOpts,
 ): ExcalidrawLinearElement {
   return {
     ..._newElementBase<ExcalidrawLinearElement>(opts.type, opts),
     points: [],
+    lastCommittedPoint: opts.lastCommittedPoint || null,
   };
 }
 

+ 1 - 0
src/element/types.ts

@@ -44,6 +44,7 @@ export type ExcalidrawLinearElement = _ExcalidrawElementBase &
   Readonly<{
     type: "arrow" | "line";
     points: Point[];
+    lastCommittedPoint?: Point | null;
   }>;
 
 export type PointerType = "mouse" | "pen" | "touch";

+ 1 - 1
src/locales/en.json

@@ -95,7 +95,7 @@
   },
   "hints": {
     "linearElement": "Click to start multiple points, drag for single line",
-    "linearElementMulti": "Press Escape or Enter to finish",
+    "linearElementMulti": "Click on last point or press Escape or Enter to finish",
     "resize": "You can constraint proportions by holding SHIFT while resizing"
   },
   "errorSplash": {