mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-03 01:55:07 -05:00
feat(ui): global canvas hotkey interaction restrictions
This commit is contained in:
@@ -48,7 +48,9 @@ export const CanvasMainPanelContent = memo(() => {
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<CanvasToolbar />
|
||||
<CanvasManagerProviderGate>
|
||||
<CanvasToolbar />
|
||||
</CanvasManagerProviderGate>
|
||||
<ContextMenu<HTMLDivElement> renderMenu={renderMenu}>
|
||||
{(ref) => (
|
||||
<Flex
|
||||
|
||||
@@ -8,7 +8,6 @@ import { CanvasToolbarFitBboxToLayersButton } from 'features/controlLayers/compo
|
||||
import { CanvasToolbarResetViewButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton';
|
||||
import { CanvasToolbarSaveToGalleryButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton';
|
||||
import { CanvasToolbarScale } from 'features/controlLayers/components/Toolbar/CanvasToolbarScale';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useCanvasDeleteLayerHotkey } from 'features/controlLayers/hooks/useCanvasDeleteLayerHotkey';
|
||||
import { useCanvasEntityQuickSwitchHotkey } from 'features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey';
|
||||
import { useCanvasResetLayerHotkey } from 'features/controlLayers/hooks/useCanvasResetLayerHotkey';
|
||||
@@ -24,21 +23,19 @@ export const CanvasToolbar = memo(() => {
|
||||
useNextPrevEntityHotkeys();
|
||||
|
||||
return (
|
||||
<CanvasManagerProviderGate>
|
||||
<Flex w="full" gap={2} alignItems="center">
|
||||
<ToolChooser />
|
||||
<Spacer />
|
||||
<ToolSettings />
|
||||
<Spacer />
|
||||
<CanvasToolbarScale />
|
||||
<CanvasToolbarResetViewButton />
|
||||
<Spacer />
|
||||
<ToolColorPicker />
|
||||
<CanvasToolbarFitBboxToLayersButton />
|
||||
<CanvasToolbarSaveToGalleryButton />
|
||||
<CanvasSettingsPopover />
|
||||
</Flex>
|
||||
</CanvasManagerProviderGate>
|
||||
<Flex w="full" gap={2} alignItems="center">
|
||||
<ToolChooser />
|
||||
<Spacer />
|
||||
<ToolSettings />
|
||||
<Spacer />
|
||||
<CanvasToolbarScale />
|
||||
<CanvasToolbarResetViewButton />
|
||||
<Spacer />
|
||||
<ToolColorPicker />
|
||||
<CanvasToolbarFitBboxToLayersButton />
|
||||
<CanvasToolbarSaveToGalleryButton />
|
||||
<CanvasSettingsPopover />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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<boolean> = 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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user