From 73ea5cb42afe25bfa39e549f0364d99c751fb93c Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:09:19 +1000 Subject: [PATCH] feat(ui): global canvas hotkey interaction restrictions --- .../components/CanvasMainPanelContent.tsx | 4 ++- .../components/Toolbar/CanvasToolbar.tsx | 29 +++++++++---------- .../hooks/useCanvasDeleteLayerHotkey.ts | 5 +++- .../hooks/useCanvasResetLayerHotkey.ts | 17 ++++++++++- .../hooks/useCanvasUndoRedoHotkeys.tsx | 12 ++++++-- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx index 4b2c548885..8efea6fb10 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx @@ -48,7 +48,9 @@ export const CanvasMainPanelContent = memo(() => { alignItems="center" justifyContent="center" > - + + + renderMenu={renderMenu}> {(ref) => ( { useNextPrevEntityHotkeys(); return ( - - - - - - - - - - - - - - - + + + + + + + + + + + + + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts index c7b639a128..c2ebc687cd 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts @@ -1,5 +1,6 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; +import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { entityDeleted } from 'features/controlLayers/store/canvasSlice'; import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors'; @@ -11,6 +12,7 @@ export function useCanvasDeleteLayerHotkey() { const dispatch = useAppDispatch(); const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier); const isStaging = useAppSelector(selectIsStaging); + const isBusy = useCanvasIsBusy(); const deleteSelectedLayer = useCallback(() => { if (selectedEntityIdentifier === null) { @@ -24,8 +26,9 @@ export function useCanvasDeleteLayerHotkey() { [selectedEntityIdentifier, isStaging] ); - useHotkeys(['delete', 'backspace'], deleteSelectedLayer, { enabled: isDeleteEnabled }, [ + useHotkeys(['delete', 'backspace'], deleteSelectedLayer, { enabled: isDeleteEnabled && !isBusy }, [ isDeleteEnabled, + isBusy, deleteSelectedLayer, ]); } diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts index a0e7577173..ac1e36f91e 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts @@ -1,9 +1,14 @@ +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; +import { useEntityAdapterSafe } from 'features/controlLayers/contexts/EntityAdapterContext'; +import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { entityReset } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { isMaskEntityIdentifier } from 'features/controlLayers/store/types'; +import type { ReadableAtom } from 'nanostores'; +import { atom } from 'nanostores'; import { useCallback, useMemo } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -12,10 +17,15 @@ const selectSelectedEntityIdentifier = createMemoizedSelector( (canvasState) => canvasState.selectedEntityIdentifier ); +const $fallbackFalse: ReadableAtom = atom(false); + export function useCanvasResetLayerHotkey() { useAssertSingleton(useCanvasResetLayerHotkey.name); const dispatch = useAppDispatch(); const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier); + const isBusy = useCanvasIsBusy(); + const adapter = useEntityAdapterSafe(selectedEntityIdentifier); + const isInteractable = useStore(adapter?.$isInteractable ?? $fallbackFalse); const resetSelectedLayer = useCallback(() => { if (selectedEntityIdentifier === null) { @@ -29,5 +39,10 @@ export function useCanvasResetLayerHotkey() { [selectedEntityIdentifier] ); - useHotkeys('shift+c', resetSelectedLayer, { enabled: isResetEnabled }, [isResetEnabled, resetSelectedLayer]); + useHotkeys('shift+c', resetSelectedLayer, { enabled: isResetEnabled && !isBusy && isInteractable }, [ + isResetEnabled, + isBusy, + isInteractable, + resetSelectedLayer, + ]); } diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx index ac146d1929..93a001ff75 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx @@ -1,5 +1,6 @@ import { useAppSelector } from 'app/store/storeHooks'; import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; +import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { canvasRedo, canvasUndo } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasMayRedo, selectCanvasMayUndo } from 'features/controlLayers/store/selectors'; import { useCallback } from 'react'; @@ -9,12 +10,17 @@ import { useDispatch } from 'react-redux'; export const useCanvasUndoRedoHotkeys = () => { useAssertSingleton('useCanvasUndoRedo'); const dispatch = useDispatch(); + const isBusy = useCanvasIsBusy(); const mayUndo = useAppSelector(selectCanvasMayUndo); const handleUndo = useCallback(() => { dispatch(canvasUndo()); }, [dispatch]); - useHotkeys(['meta+z', 'ctrl+z'], handleUndo, { enabled: mayUndo, preventDefault: true }, [mayUndo, handleUndo]); + useHotkeys(['meta+z', 'ctrl+z'], handleUndo, { enabled: mayUndo && !isBusy, preventDefault: true }, [ + mayUndo, + isBusy, + handleUndo, + ]); const mayRedo = useAppSelector(selectCanvasMayRedo); const handleRedo = useCallback(() => { @@ -23,7 +29,7 @@ export const useCanvasUndoRedoHotkeys = () => { useHotkeys( ['meta+shift+z', 'ctrl+shift+z', 'meta+y', 'ctrl+y'], handleRedo, - { enabled: mayRedo, preventDefault: true }, - [mayRedo, handleRedo] + { enabled: mayRedo && !isBusy, preventDefault: true }, + [mayRedo, handleRedo, isBusy] ); };