From ad68aa722604a61269e7086538c97072d3cc2722 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Thu, 25 Mar 2021 16:31:06 +1100 Subject: [PATCH] Refactor shortcuts into a central file and fix issue with caps lock --- src/components/map/MapFog.js | 14 ++- src/components/map/MapGridEditor.js | 13 ++- src/components/map/MapInteraction.js | 20 ++-- .../map/controls/DrawingToolSettings.js | 34 +++--- .../map/controls/FogToolSettings.js | 34 +++--- src/hooks/useStageInteraction.js | 18 ++- src/modals/SelectMapModal.js | 16 +-- src/modals/SelectTokensModal.js | 16 +-- src/shortcuts.js | 110 ++++++++++++++++++ 9 files changed, 190 insertions(+), 85 deletions(-) create mode 100644 src/shortcuts.js diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js index f90ff86..208d9ac 100644 --- a/src/components/map/MapFog.js +++ b/src/components/map/MapFog.js @@ -42,6 +42,8 @@ import SubtractShapeAction from "../../actions/SubtractShapeAction"; import useSetting from "../../hooks/useSetting"; +import shortcuts from "../../shortcuts"; + function MapFog({ map, shapes, @@ -402,16 +404,20 @@ function MapFog({ }, [toolSettings, drawingShape, onShapesCut, onShapesAdd, shapes]); // Add keyboard shortcuts - function handleKeyDown({ key }) { - if (key === "Enter" && toolSettings.type === "polygon" && drawingShape) { + function handleKeyDown(event) { + if ( + shortcuts.fogFinishPolygon(event) && + toolSettings.type === "polygon" && + drawingShape + ) { finishDrawingPolygon(); } - if (key === "Escape" && drawingShape) { + if (shortcuts.fogCancelPolygon(event) && drawingShape) { setDrawingShape(null); } // Remove last point from polygon shape if delete pressed if ( - (key === "Backspace" || key === "Delete") && + shortcuts.delete(event) && drawingShape && toolSettings.type === "polygon" ) { diff --git a/src/components/map/MapGridEditor.js b/src/components/map/MapGridEditor.js index 9957b73..8962654 100644 --- a/src/components/map/MapGridEditor.js +++ b/src/components/map/MapGridEditor.js @@ -11,6 +11,8 @@ import { useKeyboard } from "../../contexts/KeyboardContext"; import Vector2 from "../../helpers/Vector2"; +import shortcuts from "../../shortcuts"; + function MapGridEditor({ map, onGridChange }) { const stageScale = useDebouncedStageScale(); const mapWidth = useMapWidth(); @@ -166,20 +168,19 @@ function MapGridEditor({ map, onGridChange }) { } function handleKeyDown(event) { - const { key, shiftKey } = event; - const nudgeAmount = shiftKey ? 2 : 0.5; - if (key === "ArrowUp") { + const nudgeAmount = event.shiftKey ? 2 : 0.5; + if (shortcuts.gridNudgeUp(event)) { // Stop arrow up/down scrolling if overflowing event.preventDefault(); nudgeGrid({ x: 0, y: -1 }, nudgeAmount); } - if (key === "ArrowLeft") { + if (shortcuts.gridNudgeLeft(event)) { nudgeGrid({ x: -1, y: 0 }, nudgeAmount); } - if (key === "ArrowRight") { + if (shortcuts.gridNudgeRight(event)) { nudgeGrid({ x: 1, y: 0 }, nudgeAmount); } - if (key === "ArrowDown") { + if (shortcuts.gridNudgeDown(event)) { event.preventDefault(); nudgeGrid({ x: 0, y: 1 }, nudgeAmount); } diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js index c4addce..e1bb369 100644 --- a/src/components/map/MapInteraction.js +++ b/src/components/map/MapInteraction.js @@ -17,6 +17,8 @@ import { useMapStage } from "../../contexts/MapStageContext"; import { GridProvider } from "../../contexts/GridContext"; import { useKeyboard } from "../../contexts/KeyboardContext"; +import shortcuts from "../../shortcuts"; + function MapInteraction({ map, mapState, @@ -111,12 +113,12 @@ function MapInteraction({ function handleKeyDown(event) { // Change to move tool when pressing space - if (event.key === " " && selectedToolId === "move") { + if (shortcuts.move(event) && selectedToolId === "move") { // Stop active state on move icon from being selected event.preventDefault(); } if ( - event.key === " " && + shortcuts.move(event) && selectedToolId !== "move" && !disabledControls.includes("move") ) { @@ -126,28 +128,28 @@ function MapInteraction({ } // Basic keyboard shortcuts - if (event.key === "w" && !disabledControls.includes("move")) { + if (shortcuts.moveTool(event) && !disabledControls.includes("move")) { onSelectedToolChange("move"); } - if (event.key === "d" && !disabledControls.includes("drawing")) { + if (shortcuts.drawingTool(event) && !disabledControls.includes("drawing")) { onSelectedToolChange("drawing"); } - if (event.key === "f" && !disabledControls.includes("fog")) { + if (shortcuts.fogTool(event) && !disabledControls.includes("fog")) { onSelectedToolChange("fog"); } - if (event.key === "m" && !disabledControls.includes("measure")) { + if (shortcuts.measureTool(event) && !disabledControls.includes("measure")) { onSelectedToolChange("measure"); } - if (event.key === "q" && !disabledControls.includes("pointer")) { + if (shortcuts.pointerTool(event) && !disabledControls.includes("pointer")) { onSelectedToolChange("pointer"); } - if (event.key === "n" && !disabledControls.includes("note")) { + if (shortcuts.noteTool(event) && !disabledControls.includes("note")) { onSelectedToolChange("note"); } } function handleKeyUp(event) { - if (event.key === " " && selectedToolId === "move") { + if (shortcuts.move(event) && selectedToolId === "move") { onSelectedToolChange(previousSelectedToolRef.current); } } diff --git a/src/components/map/controls/DrawingToolSettings.js b/src/components/map/controls/DrawingToolSettings.js index 429766b..2537e3f 100644 --- a/src/components/map/controls/DrawingToolSettings.js +++ b/src/components/map/controls/DrawingToolSettings.js @@ -24,6 +24,8 @@ import Divider from "../../Divider"; import { useKeyboard } from "../../../contexts/KeyboardContext"; +import shortcuts from "../../../shortcuts"; + function DrawingToolSettings({ settings, onSettingChange, @@ -31,36 +33,26 @@ function DrawingToolSettings({ disabledActions, }) { // Keyboard shotcuts - function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) { - if (key === "b") { + function handleKeyDown(event) { + if (shortcuts.drawBrush(event)) { onSettingChange({ type: "brush" }); - } else if (key === "p") { + } else if (shortcuts.drawPaint(event)) { onSettingChange({ type: "paint" }); - } else if (key === "l") { + } else if (shortcuts.drawLine(event)) { onSettingChange({ type: "line" }); - } else if (key === "r") { + } else if (shortcuts.drawRect(event)) { onSettingChange({ type: "rectangle" }); - } else if (key === "c") { + } else if (shortcuts.drawCircle(event)) { onSettingChange({ type: "circle" }); - } else if (key === "t") { + } else if (shortcuts.drawTriangle(event)) { onSettingChange({ type: "triangle" }); - } else if (key === "e") { + } else if (shortcuts.drawErase(event)) { onSettingChange({ type: "erase" }); - } else if (key === "o") { + } else if (shortcuts.drawBlend(event)) { onSettingChange({ useBlending: !settings.useBlending }); - } else if ( - (key === "z" || key === "Z") && - (ctrlKey || metaKey) && - shiftKey && - !disabledActions.includes("redo") - ) { + } else if (shortcuts.redo(event) && !disabledActions.includes("redo")) { onToolAction("mapRedo"); - } else if ( - key === "z" && - (ctrlKey || metaKey) && - !shiftKey && - !disabledActions.includes("undo") - ) { + } else if (shortcuts.undo(event) && !disabledActions.includes("undo")) { onToolAction("mapUndo"); } } diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js index 7342a1b..5eb14d7 100644 --- a/src/components/map/controls/FogToolSettings.js +++ b/src/components/map/controls/FogToolSettings.js @@ -22,6 +22,8 @@ import Divider from "../../Divider"; import { useKeyboard } from "../../../contexts/KeyboardContext"; +import shortcuts from "../../../shortcuts"; + function BrushToolSettings({ settings, onSettingChange, @@ -29,36 +31,26 @@ function BrushToolSettings({ disabledActions, }) { // Keyboard shortcuts - function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) { - if (key === "p") { + function handleKeyDown(event) { + if (shortcuts.fogPolygon(event)) { onSettingChange({ type: "polygon" }); - } else if (key === "b") { + } else if (shortcuts.fogBrush(event)) { onSettingChange({ type: "brush" }); - } else if (key === "t") { + } else if (shortcuts.fogToggle(event)) { onSettingChange({ type: "toggle" }); - } else if (key === "e") { + } else if (shortcuts.fogErase(event)) { onSettingChange({ type: "remove" }); - } else if (key === "l") { + } else if (shortcuts.fogLayer(event)) { onSettingChange({ multilayer: !settings.multilayer }); - } else if (key === "f") { + } else if (shortcuts.fogPreview(event)) { onSettingChange({ preview: !settings.preview }); - } else if (key === "c") { + } else if (shortcuts.fogCut(event)) { onSettingChange({ useFogCut: !settings.useFogCut }); - } else if (key === "r") { + } else if (shortcuts.fogRectangle(event)) { onSettingChange({ type: "rectangle" }); - } else if ( - (key === "z" || key === "Z") && - (ctrlKey || metaKey) && - shiftKey && - !disabledActions.includes("redo") - ) { + } else if (shortcuts.redo(event) && !disabledActions.includes("redo")) { onToolAction("fogRedo"); - } else if ( - key === "z" && - (ctrlKey || metaKey) && - !shiftKey && - !disabledActions.includes("undo") - ) { + } else if (shortcuts.undo(event) && !disabledActions.includes("undo")) { onToolAction("fogUndo"); } } diff --git a/src/hooks/useStageInteraction.js b/src/hooks/useStageInteraction.js index 123116e..a35d69c 100644 --- a/src/hooks/useStageInteraction.js +++ b/src/hooks/useStageInteraction.js @@ -4,6 +4,8 @@ import normalizeWheel from "normalize-wheel"; import { useKeyboard, useBlur } from "../contexts/KeyboardContext"; +import shortcuts from "../shortcuts"; + const wheelZoomSpeed = -1; const touchZoomSpeed = 0.005; const minZoom = 0.1; @@ -184,18 +186,14 @@ function useStageInteraction( } ); - function handleKeyDown({ key, ctrlKey, metaKey }) { + function handleKeyDown(event) { // TODO: Find better way to detect whether keyboard event should fire. // This one fires on all open stages if (preventInteraction) { return; } - if ( - (key === "=" || key === "+" || key === "-" || key === "_") && - !ctrlKey && - !metaKey - ) { - const pixelY = key === "=" || key === "+" ? -100 : 100; + if (shortcuts.stageZoomIn(event) || shortcuts.stageZoomOut(event)) { + const pixelY = shortcuts.stageZoomIn(event) ? -100 : 100; const newScale = Math.min( Math.max( stageScale + @@ -219,13 +217,13 @@ function useStageInteraction( onStageScaleChange(newScale); } - if (key === "Shift") { + if (shortcuts.stagePrecisionZoom(event)) { setZoomSpeed(0.25); } } - function handleKeyUp({ key }) { - if (key === "Shift") { + function handleKeyUp(event) { + if (shortcuts.stagePrecisionZoom(event)) { setZoomSpeed(1); } } diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index 47a9220..80dc8d3 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -28,6 +28,8 @@ import { useMapData } from "../contexts/MapDataContext"; import { useAuth } from "../contexts/AuthContext"; import { useKeyboard, useBlur } from "../contexts/KeyboardContext"; +import shortcuts from "../shortcuts"; + const defaultMapProps = { showGrid: false, snapToGrid: true, @@ -345,17 +347,17 @@ function SelectMapModal({ /** * Shortcuts */ - function handleKeyDown({ key }) { + function handleKeyDown(event) { if (!isOpen) { return; } - if (key === "Shift") { + if (shortcuts.selectRange(event)) { setSelectMode("range"); } - if (key === "Control" || key === "Meta") { + if (shortcuts.selectMultiple(event)) { setSelectMode("multiple"); } - if (key === "Backspace" || key === "Delete") { + if (shortcuts.delete(event)) { // Selected maps and none are default if ( selectedMapIds.length > 0 && @@ -370,14 +372,14 @@ function SelectMapModal({ } } - function handleKeyUp({ key }) { + function handleKeyUp(event) { if (!isOpen) { return; } - if (key === "Shift" && selectMode === "range") { + if (shortcuts.selectRange(event) && selectMode === "range") { setSelectMode("single"); } - if ((key === "Control" || key === "Meta") && selectMode === "multiple") { + if (shortcuts.selectMultiple(event) && selectMode === "multiple") { setSelectMode("single"); } } diff --git a/src/modals/SelectTokensModal.js b/src/modals/SelectTokensModal.js index b506843..e4bcd54 100644 --- a/src/modals/SelectTokensModal.js +++ b/src/modals/SelectTokensModal.js @@ -22,6 +22,8 @@ import { useTokenData } from "../contexts/TokenDataContext"; import { useAuth } from "../contexts/AuthContext"; import { useKeyboard, useBlur } from "../contexts/KeyboardContext"; +import shortcuts from "../shortcuts"; + function SelectTokensModal({ isOpen, onRequestClose }) { const { userId } = useAuth(); const { @@ -186,17 +188,17 @@ function SelectTokensModal({ isOpen, onRequestClose }) { /** * Shortcuts */ - function handleKeyDown({ key }) { + function handleKeyDown(event) { if (!isOpen) { return; } - if (key === "Shift") { + if (shortcuts.selectRange(event)) { setSelectMode("range"); } - if (key === "Control" || key === "Meta") { + if (shortcuts.selectMultiple(event)) { setSelectMode("multiple"); } - if (key === "Backspace" || key === "Delete") { + if (shortcuts.delete(event)) { // Selected tokens and none are default if ( selectedTokenIds.length > 0 && @@ -210,14 +212,14 @@ function SelectTokensModal({ isOpen, onRequestClose }) { } } - function handleKeyUp({ key }) { + function handleKeyUp(event) { if (!isOpen) { return; } - if (key === "Shift" && selectMode === "range") { + if (shortcuts.selectRange(event) && selectMode === "range") { setSelectMode("single"); } - if ((key === "Control" || key === "Meta") && selectMode === "multiple") { + if (shortcuts.selectMultiple(event) && selectMode === "multiple") { setSelectMode("single"); } } diff --git a/src/shortcuts.js b/src/shortcuts.js new file mode 100644 index 0000000..35eea4a --- /dev/null +++ b/src/shortcuts.js @@ -0,0 +1,110 @@ +/** + * @param {KeyboardEvent} event + * @returns {boolean} + */ +function hasModifier(event) { + return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; +} + +/** + * Key press without any modifiers and ignoring capitals + * @param {KeyboardEvent} event + * @param {string} key + * @returns {boolean} + */ +function singleKey(event, key) { + return ( + !hasModifier(event) && + (event.key === key || event.key === key.toUpperCase()) + ); +} + +/** + * @param {Keyboard} event + */ +function undo(event) { + const { key, ctrlKey, metaKey, shiftKey } = event; + return (key === "z" || key === "Z") && (ctrlKey || metaKey) && !shiftKey; +} + +/** + * @param {Keyboard} event + */ +function redo(event) { + const { key, ctrlKey, metaKey, shiftKey } = event; + return (key === "z" || key === "Z") && (ctrlKey || metaKey) && shiftKey; +} + +/** + * @param {Keyboard} event + */ +function zoomIn(event) { + const { key, ctrlKey, metaKey } = event; + return (key === "=" || key === "+") && !ctrlKey && !metaKey; +} + +/** + * @param {Keyboard} event + */ +function zoomOut(event) { + const { key, ctrlKey, metaKey } = event; + return (key === "-" || key === "_") && !ctrlKey && !metaKey; +} + +/** + * @callback shortcut + * @param {KeyboardEvent} event + * @returns {boolean} + */ + +/** + * @type {Object.} + */ +const shortcuts = { + // Tools + move: (event) => singleKey(event, " "), + moveTool: (event) => singleKey(event, "w"), + drawingTool: (event) => singleKey(event, "d"), + fogTool: (event) => singleKey(event, "f"), + measureTool: (event) => singleKey(event, "m"), + pointerTool: (event) => singleKey(event, "q"), + noteTool: (event) => singleKey(event, "n"), + // Map editor + gridNudgeUp: ({ key }) => key === "ArrowUp", + gridNudgeLeft: ({ key }) => key === "ArrowLeft", + gridNudgeRight: ({ key }) => key === "ArrowRight", + gridNudgeDown: ({ key }) => key === "ArrowDown", + // Drawing tool + drawBrush: (event) => singleKey(event, "b"), + drawPaint: (event) => singleKey(event, "p"), + drawLine: (event) => singleKey(event, "l"), + drawRect: (event) => singleKey(event, "r"), + drawCircle: (event) => singleKey(event, "c"), + drawTriangle: (event) => singleKey(event, "t"), + drawErase: (event) => singleKey(event, "e"), + drawBlend: (event) => singleKey(event, "o"), + // Fog tool + fogPolygon: (event) => singleKey(event, "p"), + fogRectangle: (event) => singleKey(event, "r"), + fogBrush: (event) => singleKey(event, "b"), + fogToggle: (event) => singleKey(event, "t"), + fogErase: (event) => singleKey(event, "e"), + fogLayer: (event) => singleKey(event, "l"), + fogPreview: (event) => singleKey(event, "f"), + fogCut: (event) => singleKey(event, "c"), + fogFinishPolygon: ({ key }) => key === "Enter", + fogCancelPolygon: ({ key }) => key === "Escape", + // Stage interaction + stageZoomIn: zoomIn, + stageZoomOut: zoomOut, + stagePrecisionZoom: ({ key }) => key === "Shift", + // Select + selectRange: ({ key }) => key === "Shift", + selectMultiple: ({ key }) => key === "Control" || key === "Meta", + // Common + undo, + redo, + delete: ({ key }) => key === "Backspace" || key === "Delete", +}; + +export default shortcuts;