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"
|
||||
},
|
||||
"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",
|
||||
|
@ -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 */
|
||||
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<HTMLInputElement>) => {
|
||||
if (!canvasManager) {
|
||||
return;
|
||||
}
|
||||
if (e.target.checked) {
|
||||
canvasManager.enableDebugging();
|
||||
} else {
|
||||
canvasManager.disableDebugging();
|
||||
}
|
||||
},
|
||||
[canvasManager]
|
||||
);
|
||||
return (
|
||||
<Flex w="full" gap={2}>
|
||||
<Flex flex={1} justifyContent="center">
|
||||
<Flex gap={2} marginInlineEnd="auto" alignItems="center">
|
||||
<ToggleProgressButton />
|
||||
<ToolChooser />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex flex={1} gap={2} justifyContent="center" alignItems="center">
|
||||
<CanvasManagerProviderGate>
|
||||
<Flex w="full" gap={2} alignItems="center">
|
||||
<ToggleProgressButton />
|
||||
<ToolChooser />
|
||||
{tool === 'brush' && <ToolBrushWidth />}
|
||||
{tool === 'eraser' && <ToolEraserWidth />}
|
||||
<Spacer />
|
||||
<CanvasScale />
|
||||
<CanvasResetViewButton />
|
||||
<Spacer />
|
||||
<ToolFillColorPicker />
|
||||
<UndoRedoButtonGroup />
|
||||
<CanvasSettingsPopover />
|
||||
<ViewerToggleMenu />
|
||||
</Flex>
|
||||
<CanvasScale />
|
||||
<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>
|
||||
</CanvasManagerProviderGate>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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 { 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';
|
@ -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…
Reference in New Issue
Block a user