From ca22a521022023dddd2c35c1f3ca4527fe80735d Mon Sep 17 00:00:00 2001 From: zsviczian Date: Fri, 6 Oct 2023 15:04:34 +0000 Subject: [PATCH] add laser pointer to view mode --- src/actions/actionCanvas.tsx | 44 ++++++++++++++++++++++++++++++++++++ src/actions/types.ts | 3 ++- src/appState.ts | 8 +++++++ src/components/App.tsx | 36 +++++++++++++++++++---------- src/keys.ts | 1 + src/locales/en.json | 1 + 6 files changed, 80 insertions(+), 13 deletions(-) diff --git a/src/actions/actionCanvas.tsx b/src/actions/actionCanvas.tsx index 6531203ee..9dcb4ec60 100644 --- a/src/actions/actionCanvas.tsx +++ b/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, +}); diff --git a/src/actions/types.ts b/src/actions/types.ts index c74e19552..57918429c 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -124,7 +124,8 @@ export type ActionName = | "setFrameAsActiveTool" | "setEmbeddableAsActiveTool" | "createContainerFromText" - | "wrapTextInContainer"; + | "wrapTextInContainer" + | "toggleLaserPointerTool"; export type PanelComponentProps = { elements: readonly ExcalidrawElement[]; diff --git a/src/appState.ts b/src/appState.ts index 0089f57e9..d9f3a95fe 100644 --- a/src/appState.ts +++ b/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"; +}; diff --git a/src/components/App.tsx b/src/components/App.tsx index 737d2bed7..d3ea746b6 100644 --- a/src/components/App.tsx +++ b/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 { 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 { } } - 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 { 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 { (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 { actionToggleZenMode, actionToggleViewMode, actionToggleStats, + actionToggleLaserPointer, ]; } diff --git a/src/keys.ts b/src/keys.ts index f7bf54db5..d4cf0c6f4 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -22,6 +22,7 @@ export const CODES = { Z: "KeyZ", R: "KeyR", S: "KeyS", + K: "KeyK", } as const; export const KEYS = { diff --git a/src/locales/en.json b/src/locales/en.json index f2e6b601a..5a070503c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,5 +1,6 @@ { "labels": { + "laser": "Toggle laser pointer", "paste": "Paste", "pasteAsPlaintext": "Paste as plaintext", "pasteCharts": "Paste charts",