Просмотр исходного кода

add laser pointer to view mode

zsviczian 1 год назад
Родитель
Сommit
ca22a52102
6 измененных файлов с 80 добавлено и 13 удалено
  1. 44 0
      src/actions/actionCanvas.tsx
  2. 2 1
      src/actions/types.ts
  3. 8 0
      src/appState.ts
  4. 24 12
      src/components/App.tsx
  5. 1 0
      src/keys.ts
  6. 1 0
      src/locales/en.json

+ 44 - 0
src/actions/actionCanvas.tsx

@@ -18,6 +18,7 @@ import {
   getDefaultAppState,
   isEraserActive,
   isHandToolActive,
+  isLaserPointerActive,
 } from "../appState";
 import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors";
 import { Bounds } from "../element/bounds";
@@ -439,3 +440,46 @@ export const actionToggleHandTool = register({
   },
   keyTest: (event) => event.key === KEYS.H,
 });
+
+export const actionToggleLaserPointer = register({
+  name: "toggleLaserPointerTool",
+  viewMode: true,
+  trackEvent: { category: "menu" },
+  perform(elements, appState, _, app) {
+    let activeTool: AppState["activeTool"];
+
+    if (isLaserPointerActive(appState)) {
+      activeTool = updateActiveTool(appState, {
+        ...(appState.activeTool.lastActiveTool || {
+          type: appState.viewModeEnabled ? "hand" : "selection",
+        }),
+        lastActiveToolBeforeEraser: null,
+      });
+      setCursor(
+        app.interactiveCanvas,
+        appState.viewModeEnabled ? CURSOR_TYPE.GRAB : CURSOR_TYPE.POINTER,
+      );
+    } else {
+      activeTool = updateActiveTool(appState, {
+        type: "laser",
+        lastActiveToolBeforeEraser: appState.activeTool,
+      });
+      setCursor(app.interactiveCanvas, CURSOR_TYPE.CROSSHAIR);
+    }
+
+    return {
+      appState: {
+        ...appState,
+        selectedElementIds: {},
+        selectedGroupIds: {},
+        activeEmbeddable: null,
+        activeTool,
+      },
+      commitToHistory: true,
+    };
+  },
+  checked: (appState) => appState.activeTool.type === "laser",
+  contextItemLabel: "labels.laser",
+  keyTest: (event) =>
+    event.code === CODES.K && !event[KEYS.CTRL_OR_CMD] && !event.altKey,
+});

+ 2 - 1
src/actions/types.ts

@@ -124,7 +124,8 @@ export type ActionName =
   | "setFrameAsActiveTool"
   | "setEmbeddableAsActiveTool"
   | "createContainerFromText"
-  | "wrapTextInContainer";
+  | "wrapTextInContainer"
+  | "toggleLaserPointerTool";
 
 export type PanelComponentProps = {
   elements: readonly ExcalidrawElement[];

+ 8 - 0
src/appState.ts

@@ -266,3 +266,11 @@ export const isHandToolActive = ({
 }) => {
   return activeTool.type === "hand";
 };
+
+export const isLaserPointerActive = ({
+  activeTool,
+}: {
+  activeTool: AppState["activeTool"];
+}) => {
+  return activeTool.type === "laser";
+};

+ 24 - 12
src/components/App.tsx

@@ -46,6 +46,7 @@ import {
   getDefaultAppState,
   isEraserActive,
   isHandToolActive,
+  isLaserPointerActive,
 } from "../appState";
 import { parseClipboard } from "../clipboard";
 import {
@@ -343,7 +344,11 @@ import {
   actionRemoveAllElementsFromFrame,
   actionSelectAllElementsInFrame,
 } from "../actions/actionFrame";
-import { actionToggleHandTool, zoomToFit } from "../actions/actionCanvas";
+import {
+  actionToggleHandTool,
+  zoomToFit,
+  actionToggleLaserPointer,
+} from "../actions/actionCanvas";
 import { jotaiStore } from "../jotai";
 import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
 import {
@@ -2910,7 +2915,22 @@ class App extends React.Component<AppProps, AppState> {
         return;
       }
 
+      if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
+        if (isLaserPointerActive(this.state)) {
+          this.setActiveTool({
+            type: this.state.viewModeEnabled ? "hand" : "selection",
+          });
+        } else {
+          this.setActiveTool({ type: "laser" });
+        }
+        return;
+      }
+
       if (this.state.viewModeEnabled) {
+        //revert to hand in case a key is pressed (K is handled above)
+        if (event.key !== KEYS.K) {
+          this.setActiveTool({ type: "hand" });
+        }
         return;
       }
 
@@ -3060,15 +3080,6 @@ class App extends React.Component<AppProps, AppState> {
         }
       }
 
-      if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
-        if (this.state.activeTool.type === "laser") {
-          this.setActiveTool({ type: "selection" });
-        } else {
-          this.setActiveTool({ type: "laser" });
-        }
-        return;
-      }
-
       if (
         event[KEYS.CTRL_OR_CMD] &&
         (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
@@ -4609,7 +4620,7 @@ class App extends React.Component<AppProps, AppState> {
 
     lastPointerUp = onPointerUp;
 
-    if (!this.state.viewModeEnabled || this.state.activeTool.type === "laser") {
+    if (this.state.activeTool.type === "laser") {
       window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
       window.addEventListener(EVENT.POINTER_UP, onPointerUp);
       window.addEventListener(EVENT.KEYDOWN, onKeyDown);
@@ -4739,7 +4750,7 @@ class App extends React.Component<AppProps, AppState> {
         (event.button === POINTER_BUTTON.WHEEL ||
           (event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
           isHandToolActive(this.state) ||
-          this.state.viewModeEnabled)
+          (this.state.viewModeEnabled && !isLaserPointerActive(this.state)))
       ) ||
       isTextElement(this.state.editingElement)
     ) {
@@ -8143,6 +8154,7 @@ class App extends React.Component<AppProps, AppState> {
           actionToggleZenMode,
           actionToggleViewMode,
           actionToggleStats,
+          actionToggleLaserPointer,
         ];
       }
 

+ 1 - 0
src/keys.ts

@@ -22,6 +22,7 @@ export const CODES = {
   Z: "KeyZ",
   R: "KeyR",
   S: "KeyS",
+  K: "KeyK",
 } as const;
 
 export const KEYS = {

+ 1 - 0
src/locales/en.json

@@ -1,5 +1,6 @@
 {
   "labels": {
+    "laser": "Toggle laser pointer",
     "paste": "Paste",
     "pasteAsPlaintext": "Paste as plaintext",
     "pasteCharts": "Paste charts",