mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): rework settings menu
This commit is contained in:
parent
7685e36886
commit
62445680ad
@ -1650,7 +1650,11 @@
|
|||||||
"storeNotInitialized": "Store is not initialized"
|
"storeNotInitialized": "Store is not initialized"
|
||||||
},
|
},
|
||||||
"controlLayers": {
|
"controlLayers": {
|
||||||
|
"resetCanvas": "Reset Canvas",
|
||||||
"resetAll": "Reset All",
|
"resetAll": "Reset All",
|
||||||
|
"clearCaches": "Clear Caches",
|
||||||
|
"recalculateRects": "Recalculate Rects",
|
||||||
|
"clipToBbox": "Clip Strokes to Bbox",
|
||||||
"addLayer": "Add Layer",
|
"addLayer": "Add Layer",
|
||||||
"moveToFront": "Move to Front",
|
"moveToFront": "Move to Front",
|
||||||
"moveToBack": "Move to Back",
|
"moveToBack": "Move to Back",
|
||||||
|
@ -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<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)),
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
const onChangeClipToBbox = useCallback(
|
|
||||||
(e: ChangeEvent<HTMLInputElement>) => 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 (
|
|
||||||
<Popover isLazy>
|
|
||||||
<PopoverTrigger>
|
|
||||||
<IconButton aria-label={t('common.settingsLabel')} icon={<RiSettings4Fill />} />
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent>
|
|
||||||
<PopoverBody>
|
|
||||||
<Flex direction="column" gap={2}>
|
|
||||||
<FormControl w="full">
|
|
||||||
<FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
|
|
||||||
<Checkbox isChecked={invertScroll} onChange={onChangeInvertScroll} />
|
|
||||||
</FormControl>
|
|
||||||
<FormControl w="full">
|
|
||||||
<FormLabel flexGrow={1}>{t('unifiedCanvas.clipToBbox')}</FormLabel>
|
|
||||||
<Checkbox isChecked={clipToBbox} onChange={onChangeClipToBbox} />
|
|
||||||
</FormControl>
|
|
||||||
<CanvasSettingsDynamicGridToggle />
|
|
||||||
<Button onClick={clearCaches} size="sm">
|
|
||||||
Clear Caches
|
|
||||||
</Button>
|
|
||||||
<Button onClick={calculateBboxes} size="sm">
|
|
||||||
Calculate Bboxes
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</PopoverBody>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ControlLayersSettingsPopover);
|
|
@ -1,63 +1,38 @@
|
|||||||
/* eslint-disable i18next/no-literal-string */
|
/* eslint-disable i18next/no-literal-string */
|
||||||
import { Flex, Switch } from '@invoke-ai/ui-library';
|
import { Flex, Spacer } from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { CanvasResetViewButton } from 'features/controlLayers/components/CanvasResetViewButton';
|
import { CanvasResetViewButton } from 'features/controlLayers/components/CanvasResetViewButton';
|
||||||
import { CanvasScale } from 'features/controlLayers/components/CanvasScale';
|
import { CanvasScale } from 'features/controlLayers/components/CanvasScale';
|
||||||
import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover';
|
import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover';
|
||||||
import { ResetCanvasButton } from 'features/controlLayers/components/ResetCanvasButton';
|
|
||||||
import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth';
|
import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth';
|
||||||
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
|
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
|
||||||
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
|
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
|
||||||
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
||||||
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
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 { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
||||||
import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu';
|
import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu';
|
||||||
import type { ChangeEvent } from 'react';
|
import { memo } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
|
|
||||||
export const ControlLayersToolbar = memo(() => {
|
export const ControlLayersToolbar = memo(() => {
|
||||||
const tool = useAppSelector((s) => s.canvasV2.tool.selected);
|
const tool = useAppSelector((s) => s.canvasV2.tool.selected);
|
||||||
const canvasManager = useStore($canvasManager);
|
|
||||||
const onChangeDebugging = useCallback(
|
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
if (!canvasManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.target.checked) {
|
|
||||||
canvasManager.enableDebugging();
|
|
||||||
} else {
|
|
||||||
canvasManager.disableDebugging();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[canvasManager]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<Flex w="full" gap={2}>
|
<CanvasManagerProviderGate>
|
||||||
<Flex flex={1} justifyContent="center">
|
<Flex w="full" gap={2} alignItems="center">
|
||||||
<Flex gap={2} marginInlineEnd="auto" alignItems="center">
|
<ToggleProgressButton />
|
||||||
<ToggleProgressButton />
|
<ToolChooser />
|
||||||
<ToolChooser />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex flex={1} gap={2} justifyContent="center" alignItems="center">
|
|
||||||
{tool === 'brush' && <ToolBrushWidth />}
|
{tool === 'brush' && <ToolBrushWidth />}
|
||||||
{tool === 'eraser' && <ToolEraserWidth />}
|
{tool === 'eraser' && <ToolEraserWidth />}
|
||||||
|
<Spacer />
|
||||||
|
<CanvasScale />
|
||||||
|
<CanvasResetViewButton />
|
||||||
|
<Spacer />
|
||||||
|
<ToolFillColorPicker />
|
||||||
|
<UndoRedoButtonGroup />
|
||||||
|
<CanvasSettingsPopover />
|
||||||
|
<ViewerToggleMenu />
|
||||||
</Flex>
|
</Flex>
|
||||||
<CanvasScale />
|
</CanvasManagerProviderGate>
|
||||||
<CanvasResetViewButton />
|
|
||||||
<Switch onChange={onChangeDebugging}>debug</Switch>
|
|
||||||
<Flex flex={1} justifyContent="center">
|
|
||||||
<Flex gap={2} marginInlineStart="auto" alignItems="center">
|
|
||||||
<ToolFillColorPicker />
|
|
||||||
<UndoRedoButtonGroup />
|
|
||||||
<ControlLayersSettingsPopover />
|
|
||||||
<ResetCanvasButton />
|
|
||||||
<ViewerToggleMenu />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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 <IconButton onClick={onClick} icon={<PiTrashBold />} aria-label="Reset canvas" colorScheme="error" />;
|
|
||||||
});
|
|
||||||
|
|
||||||
ResetCanvasButton.displayName = 'ResetCanvasButton';
|
|
@ -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 (
|
||||||
|
<Button onClick={clearCaches} size="sm" colorScheme="warning">
|
||||||
|
{t('controlLayers.clearCaches')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasSettingsClearCachesButton.displayName = 'CanvasSettingsClearCachesButton';
|
@ -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<HTMLInputElement>) => dispatch(clipToBboxChanged(e.target.checked)),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<FormControl w="full">
|
||||||
|
<FormLabel flexGrow={1}>{t('controlLayers.clipToBbox')}</FormLabel>
|
||||||
|
<Checkbox isChecked={clipToBbox} onChange={onChange} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasSettingsClipToBboxCheckbox.displayName = 'CanvasSettingsClipToBboxCheckbox';
|
@ -4,7 +4,7 @@ import { settingsDynamicGridToggled } from 'features/controlLayers/store/canvasV
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const CanvasSettingsDynamicGridToggle = memo(() => {
|
export const CanvasSettingsDynamicGridSwitch = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const dynamicGrid = useAppSelector((s) => s.canvasV2.settings.dynamicGrid);
|
const dynamicGrid = useAppSelector((s) => s.canvasV2.settings.dynamicGrid);
|
||||||
@ -22,4 +22,4 @@ export const CanvasSettingsDynamicGridToggle = memo(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
CanvasSettingsDynamicGridToggle.displayName = 'CanvasSettingsDynamicGridToggle';
|
CanvasSettingsDynamicGridSwitch.displayName = 'CanvasSettingsDynamicGridSwitch';
|
@ -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<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<FormControl w="full">
|
||||||
|
<FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
|
||||||
|
<Checkbox isChecked={invertScroll} onChange={onChange} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasSettingsInvertScrollCheckbox.displayName = 'CanvasSettingsInvertScrollCheckbox';
|
@ -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 (
|
||||||
|
<Popover isLazy>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<IconButton aria-label={t('common.settingsLabel')} icon={<RiSettings4Fill />} />
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<PopoverArrow />
|
||||||
|
<PopoverBody>
|
||||||
|
<Flex direction="column" gap={2}>
|
||||||
|
<CanvasSettingsInvertScrollCheckbox />
|
||||||
|
<CanvasSettingsClipToBboxCheckbox />
|
||||||
|
<CanvasSettingsDynamicGridSwitch />
|
||||||
|
<CanvasSettingsResetButton />
|
||||||
|
<DebugSettings />
|
||||||
|
</Flex>
|
||||||
|
</PopoverBody>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasSettingsPopover.displayName = 'CanvasSettingsPopover';
|
||||||
|
|
||||||
|
const DebugSettings = () => {
|
||||||
|
const shift = useShiftModifier();
|
||||||
|
|
||||||
|
if (!shift) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
<CanvasSettingsClearCachesButton />
|
||||||
|
<CanvasSettingsRecalculateRectsButton />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -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 (
|
||||||
|
<Button onClick={onClick} size="sm" colorScheme="warning">
|
||||||
|
{t('controlLayers.recalculateRects')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasSettingsRecalculateRectsButton.displayName = 'CanvasSettingsRecalculateRectsButton';
|
@ -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 (
|
||||||
|
<Button onClick={onClick} colorScheme="error" size="sm">
|
||||||
|
{t('controlLayers.resetCanvas')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasSettingsResetButton.displayName = 'CanvasSettingsResetButton';
|
Loading…
x
Reference in New Issue
Block a user