From 82f0cb2c8c0542e2674e7b81b6b4aabc8ba5609e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:14:11 +1000 Subject: [PATCH] feat(ui): rework settings menu --- invokeai/frontend/web/public/locales/en.json | 4 + .../ControlLayersSettingsPopover.tsx | 84 ------------------- .../components/ControlLayersToolbar.tsx | 59 ++++--------- .../components/ResetCanvasButton.tsx | 15 ---- .../CanvasSettingsClearCachesButton.tsx | 19 +++++ .../CanvasSettingsClipToBboxCheckbox.tsx | 24 ++++++ .../CanvasSettingsDynamicGridSwitch.tsx} | 4 +- .../CanvasSettingsInvertScrollCheckbox.tsx | 24 ++++++ .../Settings/CanvasSettingsPopover.tsx | 61 ++++++++++++++ .../CanvasSettingsRecalculateRectsButton.tsx | 28 +++++++ .../Settings/CanvasSettingsResetButton.tsx | 20 +++++ 11 files changed, 199 insertions(+), 143 deletions(-) delete mode 100644 invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx delete mode 100644 invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx rename invokeai/frontend/web/src/features/controlLayers/components/{CanvasSettingsDynamicGridToggle.tsx => Settings/CanvasSettingsDynamicGridSwitch.tsx} (85%) create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 55d1256de2..83195488c5 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1645,7 +1645,11 @@ "storeNotInitialized": "Store is not initialized" }, "controlLayers": { + "resetCanvas": "Reset Canvas", "resetAll": "Reset All", + "clearCaches": "Clear Caches", + "recalculateRects": "Recalculate Rects", + "clipToBbox": "Clip Strokes to Bbox", "addLayer": "Add Layer", "moveToFront": "Move to Front", "moveToBack": "Move to Back", diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx deleted file mode 100644 index 58942034bc..0000000000 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { - Button, - Checkbox, - Flex, - FormControl, - FormLabel, - IconButton, - Popover, - PopoverBody, - PopoverContent, - PopoverTrigger, -} from '@invoke-ai/ui-library'; -import { useStore } from '@nanostores/react'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { CanvasSettingsDynamicGridToggle } from 'features/controlLayers/components/CanvasSettingsDynamicGridToggle'; -import { $canvasManager } from 'features/controlLayers/konva/CanvasManager'; -import { clipToBboxChanged, invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice'; -import type { ChangeEvent } from 'react'; -import { memo, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { RiSettings4Fill } from 'react-icons/ri'; - -const ControlLayersSettingsPopover = () => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const canvasManager = useStore($canvasManager); - const clipToBbox = useAppSelector((s) => s.canvasV2.settings.clipToBbox); - const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll); - const onChangeInvertScroll = useCallback( - (e: ChangeEvent) => dispatch(invertScrollChanged(e.target.checked)), - [dispatch] - ); - const onChangeClipToBbox = useCallback( - (e: ChangeEvent) => dispatch(clipToBboxChanged(e.target.checked)), - [dispatch] - ); - const clearCaches = useCallback(() => { - canvasManager?.cache.clearAll(); - }, [canvasManager]); - const calculateBboxes = useCallback(() => { - if (!canvasManager) { - return; - } - const adapters = [ - ...canvasManager.rasterLayerAdapters.values(), - ...canvasManager.controlLayerAdapters.values(), - ...canvasManager.regionalGuidanceAdapters.values(), - ...canvasManager.inpaintMaskAdapters.values(), - ]; - for (const adapter of adapters) { - adapter.transformer.requestRectCalculation(); - } - }, [canvasManager]); - return ( - - - } /> - - - - - - {t('unifiedCanvas.invertBrushSizeScrollDirection')} - - - - {t('unifiedCanvas.clipToBbox')} - - - - - - - - - - ); -}; - -export default memo(ControlLayersSettingsPopover); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx index 69ec88c5cf..ec968f19f8 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx @@ -1,63 +1,38 @@ /* eslint-disable i18next/no-literal-string */ -import { Flex, Switch } from '@invoke-ai/ui-library'; -import { useStore } from '@nanostores/react'; +import { Flex, Spacer } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import { CanvasResetViewButton } from 'features/controlLayers/components/CanvasResetViewButton'; import { CanvasScale } from 'features/controlLayers/components/CanvasScale'; -import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover'; -import { ResetCanvasButton } from 'features/controlLayers/components/ResetCanvasButton'; +import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover'; import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth'; import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser'; import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth'; import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker'; import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup'; -import { $canvasManager } from 'features/controlLayers/konva/CanvasManager'; +import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton'; import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu'; -import type { ChangeEvent } from 'react'; -import { memo, useCallback } from 'react'; +import { memo } from 'react'; export const ControlLayersToolbar = memo(() => { const tool = useAppSelector((s) => s.canvasV2.tool.selected); - const canvasManager = useStore($canvasManager); - const onChangeDebugging = useCallback( - (e: ChangeEvent) => { - if (!canvasManager) { - return; - } - if (e.target.checked) { - canvasManager.enableDebugging(); - } else { - canvasManager.disableDebugging(); - } - }, - [canvasManager] - ); return ( - - - - - - - - + + + + {tool === 'brush' && } {tool === 'eraser' && } + + + + + + + + - - - debug - - - - - - - - - - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx deleted file mode 100644 index f0880b6e02..0000000000 --- a/invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { IconButton } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { canvasReset } from 'features/controlLayers/store/canvasV2Slice'; -import { memo, useCallback } from 'react'; -import { PiTrashBold } from 'react-icons/pi'; - -export const ResetCanvasButton = memo(() => { - const dispatch = useAppDispatch(); - const onClick = useCallback(() => { - dispatch(canvasReset()); - }, [dispatch]); - return } aria-label="Reset canvas" colorScheme="error" />; -}); - -ResetCanvasButton.displayName = 'ResetCanvasButton'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx new file mode 100644 index 0000000000..a3fd6c7317 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx @@ -0,0 +1,19 @@ +import { Button } from '@invoke-ai/ui-library'; +import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const CanvasSettingsClearCachesButton = memo(() => { + const { t } = useTranslation(); + const canvasManager = useCanvasManager(); + const clearCaches = useCallback(() => { + canvasManager.cache.clearAll(); + }, [canvasManager]); + return ( + + ); +}); + +CanvasSettingsClearCachesButton.displayName = 'CanvasSettingsClearCachesButton'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx new file mode 100644 index 0000000000..205c36070c --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx @@ -0,0 +1,24 @@ +import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { clipToBboxChanged } from 'features/controlLayers/store/canvasV2Slice'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const CanvasSettingsClipToBboxCheckbox = memo(() => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const clipToBbox = useAppSelector((s) => s.canvasV2.settings.clipToBbox); + const onChange = useCallback( + (e: ChangeEvent) => dispatch(clipToBboxChanged(e.target.checked)), + [dispatch] + ); + return ( + + {t('controlLayers.clipToBbox')} + + + ); +}); + +CanvasSettingsClipToBboxCheckbox.displayName = 'CanvasSettingsClipToBboxCheckbox'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasSettingsDynamicGridToggle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx similarity index 85% rename from invokeai/frontend/web/src/features/controlLayers/components/CanvasSettingsDynamicGridToggle.tsx rename to invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx index 916abc5574..71ffe1e729 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasSettingsDynamicGridToggle.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx @@ -4,7 +4,7 @@ import { settingsDynamicGridToggled } from 'features/controlLayers/store/canvasV import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export const CanvasSettingsDynamicGridToggle = memo(() => { +export const CanvasSettingsDynamicGridSwitch = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const dynamicGrid = useAppSelector((s) => s.canvasV2.settings.dynamicGrid); @@ -22,4 +22,4 @@ export const CanvasSettingsDynamicGridToggle = memo(() => { ); }); -CanvasSettingsDynamicGridToggle.displayName = 'CanvasSettingsDynamicGridToggle'; +CanvasSettingsDynamicGridSwitch.displayName = 'CanvasSettingsDynamicGridSwitch'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx new file mode 100644 index 0000000000..1c9a4f174c --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx @@ -0,0 +1,24 @@ +import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const CanvasSettingsInvertScrollCheckbox = memo(() => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll); + const onChange = useCallback( + (e: ChangeEvent) => dispatch(invertScrollChanged(e.target.checked)), + [dispatch] + ); + return ( + + {t('unifiedCanvas.invertBrushSizeScrollDirection')} + + + ); +}); + +CanvasSettingsInvertScrollCheckbox.displayName = 'CanvasSettingsInvertScrollCheckbox'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx new file mode 100644 index 0000000000..c0c91ba936 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx @@ -0,0 +1,61 @@ +import { + Divider, + Flex, + IconButton, + Popover, + PopoverArrow, + PopoverBody, + PopoverContent, + PopoverTrigger, + useShiftModifier, +} from '@invoke-ai/ui-library'; +import { CanvasSettingsClearCachesButton } from 'features/controlLayers/components/Settings/CanvasSettingsClearCachesButton'; +import { CanvasSettingsClipToBboxCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox'; +import { CanvasSettingsDynamicGridSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch'; +import { CanvasSettingsInvertScrollCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox'; +import { CanvasSettingsRecalculateRectsButton } from 'features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton'; +import { CanvasSettingsResetButton } from 'features/controlLayers/components/Settings/CanvasSettingsResetButton'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { RiSettings4Fill } from 'react-icons/ri'; + +export const CanvasSettingsPopover = memo(() => { + const { t } = useTranslation(); + return ( + + + } /> + + + + + + + + + + + + + + + ); +}); + +CanvasSettingsPopover.displayName = 'CanvasSettingsPopover'; + +const DebugSettings = () => { + const shift = useShiftModifier(); + + if (!shift) { + return null; + } + + return ( + <> + + + + + ); +}; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx new file mode 100644 index 0000000000..7b3be93bdd --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx @@ -0,0 +1,28 @@ +import { Button } from '@invoke-ai/ui-library'; +import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const CanvasSettingsRecalculateRectsButton = memo(() => { + const { t } = useTranslation(); + const canvasManager = useCanvasManager(); + const onClick = useCallback(() => { + const adapters = [ + ...canvasManager.rasterLayerAdapters.values(), + ...canvasManager.controlLayerAdapters.values(), + ...canvasManager.regionalGuidanceAdapters.values(), + ...canvasManager.inpaintMaskAdapters.values(), + ]; + for (const adapter of adapters) { + adapter.transformer.requestRectCalculation(); + } + }, [canvasManager]); + + return ( + + ); +}); + +CanvasSettingsRecalculateRectsButton.displayName = 'CanvasSettingsRecalculateRectsButton'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx new file mode 100644 index 0000000000..a40a3f3aae --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx @@ -0,0 +1,20 @@ +import { Button } from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { canvasReset } from 'features/controlLayers/store/canvasV2Slice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const CanvasSettingsResetButton = memo(() => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const onClick = useCallback(() => { + dispatch(canvasReset()); + }, [dispatch]); + return ( + + ); +}); + +CanvasSettingsResetButton.displayName = 'CanvasSettingsResetButton';