diff --git a/src/appState.ts b/src/appState.ts index 3515463c..81607964 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -132,8 +132,8 @@ const APP_STATE_STORAGE_CONF = (< editingLinearElement: { browser: false, export: false, server: false }, elementLocked: { browser: true, export: false, server: false }, elementType: { browser: true, export: false, server: false }, - penMode: { browser: false, export: false, server: false }, - penDetected: { browser: false, export: false, server: false }, + penMode: { browser: true, export: false, server: false }, + penDetected: { browser: true, export: false, server: false }, errorMessage: { browser: false, export: false, server: false }, exportBackground: { browser: true, export: false, server: false }, exportEmbedScene: { browser: true, export: false, server: false }, diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index 21cbbcf8..6482483c 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -15,7 +15,12 @@ import { } from "../scene"; import { SHAPES } from "../shapes"; import { AppState, Zoom } from "../types"; -import { capitalizeString, isTransparent, setCursorForShape } from "../utils"; +import { + capitalizeString, + isTransparent, + setCursorForShape, + withBatchedUpdates, +} from "../utils"; import Stack from "./Stack"; import { ToolButton } from "./ToolButton"; import { hasStrokeColor } from "../scene/comparisons"; @@ -192,43 +197,66 @@ export const ShapesSwitcher = ({ setAppState: React.Component["setState"]; onImageAction: (data: { pointerType: PointerType | null }) => void; appState: AppState; -}) => ( - <> - {SHAPES.map(({ value, icon, key }, index) => { - const label = t(`toolBar.${value}`); - const letter = key && (typeof key === "string" ? key : key[0]); - const shortcut = letter - ? `${capitalizeString(letter)} ${t("helpDialog.or")} ${index + 1}` - : `${index + 1}`; - return ( - { - setAppState({ - elementType: value, - multiElement: null, - selectedElementIds: {}, - }); - setCursorForShape(canvas, { ...appState, elementType: value }); - if (value === "image") { - onImageAction({ pointerType }); - } - }} - /> - ); - })} - -); +}) => { + const onChange = withBatchedUpdates( + ({ + elementType, + pointerType, + }: { + elementType: typeof SHAPES[number]["value"]; + pointerType: PointerType | null; + }) => { + if (!appState.penDetected && pointerType === "pen") { + setAppState({ + penDetected: true, + penMode: true, + }); + } + setAppState({ + elementType, + multiElement: null, + selectedElementIds: {}, + }); + setCursorForShape(canvas, { ...appState, elementType }); + if (elementType === "image") { + onImageAction({ pointerType }); + } + }, + ); + + return ( + <> + {SHAPES.map(({ value, icon, key }, index) => { + const label = t(`toolBar.${value}`); + const letter = key && (typeof key === "string" ? key : key[0]); + const shortcut = letter + ? `${capitalizeString(letter)} ${t("helpDialog.or")} ${index + 1}` + : `${index + 1}`; + return ( + { + onChange({ elementType: value, pointerType }); + }} + onChange={({ pointerType }) => { + onChange({ elementType: value, pointerType }); + }} + /> + ); + })} + + ); +}; export const ZoomActions = ({ renderAction, diff --git a/src/components/ToolButton.tsx b/src/components/ToolButton.tsx index 6361954f..328c135c 100644 --- a/src/components/ToolButton.tsx +++ b/src/components/ToolButton.tsx @@ -48,6 +48,7 @@ type ToolButtonProps = type: "radio"; checked: boolean; onChange?(data: { pointerType: PointerType | null }): void; + onPointerDown?(data: { pointerType: PointerType }): void; }); export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => { @@ -149,6 +150,7 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => { title={props.title} onPointerDown={(event) => { lastPointerTypeRef.current = event.pointerType || null; + props.onPointerDown?.({ pointerType: event.pointerType || null }); }} onPointerUp={() => { requestAnimationFrame(() => {