Skip to content

Commit

Permalink
feat(ui): update hotkey list
Browse files Browse the repository at this point in the history
- Rework hotkey data to include the keys for each hotkey action.
- Add wrapper for `useHotkeys` that accepts a hotkey category and id. Automatically selects the key from the hotkey data.
- Add handling for macOS (cmd vs ctrl, option vs alt).
- Redo all hotkey descriptions, deleting nonexistant ones.
- Some `esc` hotkeys that just close whatever you are currently in are omitted due to their relative simplicity and intuitiveness.
  • Loading branch information
psychedelicious authored and hipsterusername committed Sep 18, 2024
1 parent fdcd26f commit 9e6b60a
Show file tree
Hide file tree
Showing 31 changed files with 1,066 additions and 831 deletions.
476 changes: 259 additions & 217 deletions invokeai/frontend/web/public/locales/en.json

Large diffs are not rendered by default.

118 changes: 64 additions & 54 deletions invokeai/frontend/web/src/common/hooks/useGlobalHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,112 +3,122 @@ import { addScope, removeScope, setScopes } from 'common/hooks/interactionScopes
import { useClearQueue } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
import { useInvoke } from 'features/queue/hooks/useInvoke';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { useHotkeys } from 'react-hotkeys-hook';

export const useGlobalHotkeys = () => {
const dispatch = useAppDispatch();
const isModelManagerEnabled = useFeatureStatus('modelManager');
const queue = useInvoke();

useHotkeys(
['ctrl+enter', 'meta+enter'],
queue.queueBack,
{
useRegisteredHotkeys({
id: 'invoke',
category: 'app',
callback: queue.queueBack,
options: {
enabled: !queue.isDisabled && !queue.isLoading,
preventDefault: true,
enableOnFormTags: ['input', 'textarea', 'select'],
},
[queue]
);
dependencies: [queue],
});

useHotkeys(
['ctrl+shift+enter', 'meta+shift+enter'],
queue.queueFront,
{
useRegisteredHotkeys({
id: 'invokeFront',
category: 'app',
callback: queue.queueFront,
options: {
enabled: !queue.isDisabled && !queue.isLoading,
preventDefault: true,
enableOnFormTags: ['input', 'textarea', 'select'],
},
[queue]
);
dependencies: [queue],
});

const {
cancelQueueItem,
isDisabled: isDisabledCancelQueueItem,
isLoading: isLoadingCancelQueueItem,
} = useCancelCurrentQueueItem();

useHotkeys(
['shift+x'],
cancelQueueItem,
{
useRegisteredHotkeys({
id: 'cancelQueueItem',
category: 'app',
callback: cancelQueueItem,
options: {
enabled: !isDisabledCancelQueueItem && !isLoadingCancelQueueItem,
preventDefault: true,
},
[cancelQueueItem, isDisabledCancelQueueItem, isLoadingCancelQueueItem]
);
dependencies: [cancelQueueItem, isDisabledCancelQueueItem, isLoadingCancelQueueItem],
});

const { clearQueue, isDisabled: isDisabledClearQueue, isLoading: isLoadingClearQueue } = useClearQueue();

useHotkeys(
['ctrl+shift+x', 'meta+shift+x'],
clearQueue,
{
useRegisteredHotkeys({
id: 'clearQueue',
category: 'app',
callback: clearQueue,
options: {
enabled: !isDisabledClearQueue && !isLoadingClearQueue,
preventDefault: true,
},
[clearQueue, isDisabledClearQueue, isLoadingClearQueue]
);
dependencies: [clearQueue, isDisabledClearQueue, isLoadingClearQueue],
});

useHotkeys(
'1',
() => {
useRegisteredHotkeys({
id: 'selectCanvasTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('canvas'));
addScope('canvas');
removeScope('workflows');
},
[dispatch]
);
dependencies: [dispatch],
});

useHotkeys(
'2',
() => {
useRegisteredHotkeys({
id: 'selectUpscalingTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('upscaling'));
removeScope('canvas');
removeScope('workflows');
},
[dispatch]
);
dependencies: [dispatch],
});

useHotkeys(
'3',
() => {
useRegisteredHotkeys({
id: 'selectWorkflowsTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('workflows'));
removeScope('canvas');
addScope('workflows');
},
[dispatch]
);
dependencies: [dispatch],
});

useHotkeys(
'4',
() => {
if (isModelManagerEnabled) {
dispatch(setActiveTab('models'));
setScopes([]);
}
useRegisteredHotkeys({
id: 'selectModelsTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('models'));
setScopes([]);
},
options: {
enabled: isModelManagerEnabled,
},
[dispatch, isModelManagerEnabled]
);
dependencies: [dispatch, isModelManagerEnabled],
});

useHotkeys(
isModelManagerEnabled ? '5' : '4',
() => {
useRegisteredHotkeys({
id: 'selectQueueTab',
category: 'app',
callback: () => {
dispatch(setActiveTab('queue'));
setScopes([]);
},
[dispatch, isModelManagerEnabled]
);
dependencies: [dispatch, isModelManagerEnabled],
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
import { selectEntityCountActive } from 'features/controlLayers/store/selectors';
import GalleryPanelContent from 'features/gallery/components/GalleryPanelContent';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo, useCallback, useMemo, useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';

export const CanvasRightPanel = memo(() => {
Expand All @@ -28,7 +28,12 @@ export const CanvasRightPanel = memo(() => {
}
imageViewer.toggle();
}, [imageViewer]);
useHotkeys('z', imageViewer.toggle);
useRegisteredHotkeys({
id: 'toggleViewer',
category: 'viewer',
callback: imageViewer.toggle,
dependencies: [imageViewer],
});

return (
<Tabs index={tabIndex} onChange={$canvasRightPanelTabIndex.set} w="full" h="full" display="flex" flexDir="column">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiBoundingBoxBold } from 'react-icons/pi';

Expand All @@ -10,7 +10,13 @@ export const ToolBboxButton = memo(() => {
const selectBbox = useSelectTool('bbox');
const isSelected = useToolIsSelected('bbox');

useHotkeys('c', selectBbox, { enabled: !isSelected }, [selectBbox, isSelected]);
useRegisteredHotkeys({
id: 'selectBboxTool',
category: 'canvas',
callback: selectBbox,
options: { enabled: !isSelected },
dependencies: [selectBbox, isSelected],
});

return (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiPaintBrushBold } from 'react-icons/pi';

Expand All @@ -10,7 +10,13 @@ export const ToolBrushButton = memo(() => {
const isSelected = useToolIsSelected('brush');
const selectBrush = useSelectTool('brush');

useHotkeys('b', selectBrush, { enabled: !isSelected }, [isSelected, selectBrush]);
useRegisteredHotkeys({
id: 'selectBrushTool',
category: 'canvas',
callback: selectBrush,
options: { enabled: !isSelected },
dependencies: [isSelected, selectBrush],
});

return (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { selectCanvasSettingsSlice, settingsBrushWidthChanged } from 'features/controlLayers/store/canvasSettingsSlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { clamp } from 'lodash-es';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiCaretDownBold } from 'react-icons/pi';

Expand Down Expand Up @@ -127,8 +127,20 @@ export const ToolBrushWidth = memo(() => {
setLocalValue(width);
}, [width]);

useHotkeys('[', decrement, { enabled: isSelected }, [decrement, isSelected]);
useHotkeys(']', increment, { enabled: isSelected }, [increment, isSelected]);
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: decrement,
options: { enabled: isSelected },
dependencies: [decrement, isSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
options: { enabled: isSelected },
dependencies: [increment, isSelected],
});

return (
<Popover>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiEyedropperBold } from 'react-icons/pi';

Expand All @@ -10,7 +10,13 @@ export const ToolColorPickerButton = memo(() => {
const isSelected = useToolIsSelected('colorPicker');
const selectColorPicker = useSelectTool('colorPicker');

useHotkeys('i', selectColorPicker, { enabled: !isSelected }, [selectColorPicker, isSelected]);
useRegisteredHotkeys({
id: 'selectColorPickerTool',
category: 'canvas',
callback: selectColorPicker,
options: { enabled: !isSelected },
dependencies: [selectColorPicker, isSelected],
});

return (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiEraserBold } from 'react-icons/pi';

Expand All @@ -10,7 +10,13 @@ export const ToolEraserButton = memo(() => {
const isSelected = useToolIsSelected('eraser');
const selectEraser = useSelectTool('eraser');

useHotkeys('e', selectEraser, { enabled: !isSelected }, [isSelected, selectEraser]);
useRegisteredHotkeys({
id: 'selectEraserTool',
category: 'canvas',
callback: selectEraser,
options: { enabled: !isSelected },
dependencies: [isSelected, selectEraser],
});

return (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import {
selectCanvasSettingsSlice,
settingsEraserWidthChanged,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { clamp } from 'lodash-es';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiCaretDownBold } from 'react-icons/pi';

Expand Down Expand Up @@ -130,8 +130,20 @@ export const ToolEraserWidth = memo(() => {
setLocalValue(width);
}, [width]);

useHotkeys('[', decrement, { enabled: isSelected }, [decrement, isSelected]);
useHotkeys(']', increment, { enabled: isSelected }, [increment, isSelected]);
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: decrement,
options: { enabled: isSelected },
dependencies: [decrement, isSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
options: { enabled: isSelected },
dependencies: [increment, isSelected],
});

return (
<Popover>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiCursorBold } from 'react-icons/pi';

Expand All @@ -10,7 +10,13 @@ export const ToolMoveButton = memo(() => {
const isSelected = useToolIsSelected('move');
const selectMove = useSelectTool('move');

useHotkeys('v', selectMove, { enabled: !isSelected }, [isSelected, selectMove]);
useRegisteredHotkeys({
id: 'selectMoveTool',
category: 'canvas',
callback: selectMove,
options: { enabled: !isSelected },
dependencies: [isSelected, selectMove],
});

return (
<IconButton
Expand Down
Loading

0 comments on commit 9e6b60a

Please sign in to comment.