feat(ui): consolidate isolated preview settings

`isolatedFilteringPreview` and `isolatedTransformingPreview` are merged into `isolatedLayerPreview`. This is also used for segment anything.
This commit is contained in:
psychedelicious
2024-10-23 12:15:00 +10:00
parent bc85bd4bd4
commit 7f2c83b9e6
10 changed files with 71 additions and 101 deletions

View File

@@ -0,0 +1,28 @@
import { FormControl, FormLabel, Switch, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
selectIsolatedLayerPreview,
settingsIsolatedLayerPreviewToggled,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const CanvasOperationIsolatedLayerPreviewSwitch = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isolatedLayerPreview = useAppSelector(selectIsolatedLayerPreview);
const onChangeIsolatedPreview = useCallback(() => {
dispatch(settingsIsolatedLayerPreviewToggled());
}, [dispatch]);
return (
<Tooltip label={t('controlLayers.settings.isolatedLayerPreviewDesc')}>
<FormControl w="min-content">
<FormLabel m={0}>{t('controlLayers.settings.isolatedLayerPreview')}</FormLabel>
<Switch size="sm" isChecked={isolatedLayerPreview} onChange={onChangeIsolatedPreview} />
</FormControl>
</Tooltip>
);
});
CanvasOperationIsolatedLayerPreviewSwitch.displayName = 'CanvasOperationIsolatedLayerPreviewSwitch';

View File

@@ -2,6 +2,7 @@ import { Button, ButtonGroup, Flex, FormControl, FormLabel, Heading, Spacer, Swi
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
import { CanvasOperationIsolatedLayerPreviewSwitch } from 'features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch';
import { FilterSettings } from 'features/controlLayers/components/Filters/FilterSettings';
import { FilterTypeSelect } from 'features/controlLayers/components/Filters/FilterTypeSelect';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
@@ -9,9 +10,7 @@ import type { CanvasEntityAdapterControlLayer } from 'features/controlLayers/kon
import type { CanvasEntityAdapterRasterLayer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRasterLayer';
import {
selectAutoProcessFilter,
selectIsolatedFilteringPreview,
settingsAutoProcessFilterToggled,
settingsIsolatedFilteringPreviewToggled,
} from 'features/controlLayers/store/canvasSettingsSlice';
import type { FilterConfig } from 'features/controlLayers/store/filters';
import { IMAGE_FILTERS } from 'features/controlLayers/store/filters';
@@ -26,16 +25,11 @@ const FilterContent = memo(
const dispatch = useAppDispatch();
const ref = useRef<HTMLDivElement>(null);
useFocusRegion('canvas', ref, { focusOnMount: true });
const config = useStore(adapter.filterer.$filterConfig);
const isCanvasFocused = useIsRegionFocused('canvas');
const isProcessing = useStore(adapter.filterer.$isProcessing);
const hasProcessed = useStore(adapter.filterer.$hasProcessed);
const autoProcessFilter = useAppSelector(selectAutoProcessFilter);
const isolatedFilteringPreview = useAppSelector(selectIsolatedFilteringPreview);
const onChangeIsolatedPreview = useCallback(() => {
dispatch(settingsIsolatedFilteringPreviewToggled());
}, [dispatch]);
const onChangeFilterConfig = useCallback(
(filterConfig: FilterConfig) => {
@@ -98,10 +92,7 @@ const FilterContent = memo(
<FormLabel m={0}>{t('controlLayers.filter.autoProcess')}</FormLabel>
<Switch size="sm" isChecked={autoProcessFilter} onChange={onChangeAutoProcessFilter} />
</FormControl>
<FormControl w="min-content">
<FormLabel m={0}>{t('controlLayers.settings.isolatedPreview')}</FormLabel>
<Switch size="sm" isChecked={isolatedFilteringPreview} onChange={onChangeIsolatedPreview} />
</FormControl>
<CanvasOperationIsolatedLayerPreviewSwitch />
</Flex>
<FilterTypeSelect filterType={config.type} onChange={onChangeFilterType} />
<FilterSettings filterConfig={config} onChange={onChangeFilterConfig} />

View File

@@ -1,6 +1,7 @@
import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
import { CanvasOperationIsolatedLayerPreviewSwitch } from 'features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch';
import { SegmentAnythingPointType } from 'features/controlLayers/components/SegmentAnything/SegmentAnythingPointType';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import type { CanvasEntityAdapterControlLayer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterControlLayer';
@@ -52,6 +53,8 @@ const SegmentAnythingContent = memo(
<Heading size="md" color="base.300" userSelect="none">
{t('controlLayers.segment.segment')}
</Heading>
<Spacer />
<CanvasOperationIsolatedLayerPreviewSwitch />
</Flex>
<SegmentAnythingPointType adapter={adapter} />

View File

@@ -1,28 +1,28 @@
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
selectIsolatedFilteringPreview,
settingsIsolatedFilteringPreviewToggled,
selectIsolatedLayerPreview,
settingsIsolatedLayerPreviewToggled,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const CanvasSettingsIsolatedFilteringPreviewSwitch = memo(() => {
export const CanvasSettingsIsolatedLayerPreviewSwitch = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isolatedFilteringPreview = useAppSelector(selectIsolatedFilteringPreview);
const isolatedLayerPreview = useAppSelector(selectIsolatedLayerPreview);
const onChange = useCallback(() => {
dispatch(settingsIsolatedFilteringPreviewToggled());
dispatch(settingsIsolatedLayerPreviewToggled());
}, [dispatch]);
return (
<FormControl>
<FormLabel m={0} flexGrow={1}>
{t('controlLayers.settings.isolatedFilteringPreview')}
{t('controlLayers.settings.isolatedLayerPreview')}
</FormLabel>
<Switch size="sm" isChecked={isolatedFilteringPreview} onChange={onChange} />
<Switch size="sm" isChecked={isolatedLayerPreview} onChange={onChange} />
</FormControl>
);
});
CanvasSettingsIsolatedFilteringPreviewSwitch.displayName = 'CanvasSettingsIsolatedFilteringPreviewSwitch';
CanvasSettingsIsolatedLayerPreviewSwitch.displayName = 'CanvasSettingsIsolatedLayerPreviewSwitch';

View File

@@ -1,28 +0,0 @@
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
selectIsolatedTransformingPreview,
settingsIsolatedTransformingPreviewToggled,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const CanvasSettingsIsolatedTransformingPreviewSwitch = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isolatedTransformingPreview = useAppSelector(selectIsolatedTransformingPreview);
const onChange = useCallback(() => {
dispatch(settingsIsolatedTransformingPreviewToggled());
}, [dispatch]);
return (
<FormControl>
<FormLabel m={0} flexGrow={1}>
{t('controlLayers.settings.isolatedTransformingPreview')}
</FormLabel>
<Switch size="sm" isChecked={isolatedTransformingPreview} onChange={onChange} />
</FormControl>
);
});
CanvasSettingsIsolatedTransformingPreviewSwitch.displayName = 'CanvasSettingsIsolatedTransformingPreviewSwitch';

View File

@@ -16,9 +16,8 @@ import { CanvasSettingsClipToBboxCheckbox } from 'features/controlLayers/compone
import { CanvasSettingsDynamicGridSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch';
import { CanvasSettingsSnapToGridCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsGridSize';
import { CanvasSettingsInvertScrollCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox';
import { CanvasSettingsIsolatedFilteringPreviewSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsIsolatedFilteringPreviewSwitch';
import { CanvasSettingsIsolatedLayerPreviewSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsIsolatedLayerPreviewSwitch';
import { CanvasSettingsIsolatedStagingPreviewSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsIsolatedStagingPreviewSwitch';
import { CanvasSettingsIsolatedTransformingPreviewSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsIsolatedTransformingPreviewSwitch';
import { CanvasSettingsLogDebugInfoButton } from 'features/controlLayers/components/Settings/CanvasSettingsLogDebugInfo';
import { CanvasSettingsOutputOnlyMaskedRegionsCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsOutputOnlyMaskedRegionsCheckbox';
import { CanvasSettingsPreserveMaskCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsPreserveMaskCheckbox';
@@ -54,8 +53,7 @@ export const CanvasSettingsPopover = memo(() => {
<CanvasSettingsPressureSensitivityCheckbox />
<CanvasSettingsShowProgressOnCanvas />
<CanvasSettingsIsolatedStagingPreviewSwitch />
<CanvasSettingsIsolatedFilteringPreviewSwitch />
<CanvasSettingsIsolatedTransformingPreviewSwitch />
<CanvasSettingsIsolatedLayerPreviewSwitch />
<CanvasSettingsDynamicGridSwitch />
<CanvasSettingsBboxOverlaySwitch />
<CanvasSettingsShowHUDSwitch />

View File

@@ -1,30 +1,21 @@
import { Button, ButtonGroup, Flex, FormControl, FormLabel, Heading, Spacer, Switch } from '@invoke-ai/ui-library';
import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
import { CanvasOperationIsolatedLayerPreviewSwitch } from 'features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch';
import { TransformFitToBboxButtons } from 'features/controlLayers/components/Transform/TransformFitToBboxButtons';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types';
import {
selectIsolatedTransformingPreview,
settingsIsolatedTransformingPreviewToggled,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo, useCallback, useRef } from 'react';
import { memo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowsCounterClockwiseBold, PiCheckBold, PiXBold } from 'react-icons/pi';
const TransformContent = memo(({ adapter }: { adapter: CanvasEntityAdapter }) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const ref = useRef<HTMLDivElement>(null);
useFocusRegion('canvas', ref, { focusOnMount: true });
const isCanvasFocused = useIsRegionFocused('canvas');
const isProcessing = useStore(adapter.transformer.$isProcessing);
const isolatedTransformingPreview = useAppSelector(selectIsolatedTransformingPreview);
const onChangeIsolatedPreview = useCallback(() => {
dispatch(settingsIsolatedTransformingPreviewToggled());
}, [dispatch]);
const silentTransform = useStore(adapter.transformer.$silentTransform);
useRegisteredHotkeys({
@@ -66,10 +57,7 @@ const TransformContent = memo(({ adapter }: { adapter: CanvasEntityAdapter }) =>
{t('controlLayers.transform.transform')}
</Heading>
<Spacer />
<FormControl w="min-content">
<FormLabel m={0}>{t('controlLayers.settings.isolatedPreview')}</FormLabel>
<Switch size="sm" isChecked={isolatedTransformingPreview} onChange={onChangeIsolatedPreview} />
</FormControl>
<CanvasOperationIsolatedLayerPreviewSwitch />
</Flex>
<TransformFitToBboxButtons adapter={adapter} />

View File

@@ -12,10 +12,7 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { CanvasSegmentAnythingModule } from 'features/controlLayers/konva/CanvasSegmentAnythingModule';
import { getKonvaNodeDebugAttrs, getRectIntersection } from 'features/controlLayers/konva/util';
import {
selectIsolatedFilteringPreview,
selectIsolatedTransformingPreview,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { selectIsolatedLayerPreview } from 'features/controlLayers/store/canvasSettingsSlice';
import {
buildSelectIsHidden,
buildSelectIsSelected,
@@ -274,13 +271,11 @@ export abstract class CanvasEntityAdapterBase<
*/
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(this.selectIsHidden, this.syncVisibility));
this.subscriptions.add(
this.manager.stateApi.createStoreSubscription(selectIsolatedFilteringPreview, this.syncVisibility)
this.manager.stateApi.createStoreSubscription(selectIsolatedLayerPreview, this.syncVisibility)
);
this.subscriptions.add(this.manager.stateApi.$filteringAdapter.listen(this.syncVisibility));
this.subscriptions.add(
this.manager.stateApi.createStoreSubscription(selectIsolatedTransformingPreview, this.syncVisibility)
);
this.subscriptions.add(this.manager.stateApi.$transformingAdapter.listen(this.syncVisibility));
this.subscriptions.add(this.manager.stateApi.$segmentingAdapter.listen(this.syncVisibility));
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(this.selectIsSelected, this.syncVisibility));
/**
@@ -445,8 +440,10 @@ export abstract class CanvasEntityAdapterBase<
return;
}
const isolatedLayerPreview = this.manager.stateApi.runSelector(selectIsolatedLayerPreview);
// Handle isolated preview modes - if another entity is filtering or transforming, we may need to hide this entity.
if (this.manager.stateApi.runSelector(selectIsolatedFilteringPreview)) {
if (isolatedLayerPreview) {
const filteringEntityIdentifier = this.manager.stateApi.$filteringAdapter.get()?.entityIdentifier;
if (filteringEntityIdentifier && filteringEntityIdentifier.id !== this.id) {
this.setVisibility(false);
@@ -454,7 +451,7 @@ export abstract class CanvasEntityAdapterBase<
}
}
if (this.manager.stateApi.runSelector(selectIsolatedTransformingPreview)) {
if (isolatedLayerPreview) {
const transformingEntity = this.manager.stateApi.$transformingAdapter.get();
if (
transformingEntity &&
@@ -467,6 +464,14 @@ export abstract class CanvasEntityAdapterBase<
}
}
if (isolatedLayerPreview) {
const segmentingEntity = this.manager.stateApi.$segmentingAdapter.get();
if (segmentingEntity && segmentingEntity.entityIdentifier.id !== this.id) {
this.setVisibility(false);
return;
}
}
// If the entity is not selected and offscreen, we can hide it
if (!this.$isOnScreen.get() && !this.manager.stateApi.getIsSelected(this.entityIdentifier.id)) {
this.setVisibility(false);

View File

@@ -72,13 +72,9 @@ type CanvasSettingsState = {
*/
isolatedStagingPreview: boolean;
/**
* Whether to show only the selected layer while filtering.
* Whether to show only the selected layer while filtering, transforming, or doing other operations.
*/
isolatedFilteringPreview: boolean;
/**
* Whether to show only the selected layer while transforming.
*/
isolatedTransformingPreview: boolean;
isolatedLayerPreview: boolean;
/**
* Whether to use pressure sensitivity for the brush and eraser tool when a pen device is used.
*/
@@ -101,8 +97,7 @@ const initialState: CanvasSettingsState = {
bboxOverlay: false,
preserveMask: false,
isolatedStagingPreview: true,
isolatedFilteringPreview: true,
isolatedTransformingPreview: true,
isolatedLayerPreview: true,
pressureSensitivity: true,
};
@@ -155,11 +150,8 @@ export const canvasSettingsSlice = createSlice({
settingsIsolatedStagingPreviewToggled: (state) => {
state.isolatedStagingPreview = !state.isolatedStagingPreview;
},
settingsIsolatedFilteringPreviewToggled: (state) => {
state.isolatedFilteringPreview = !state.isolatedFilteringPreview;
},
settingsIsolatedTransformingPreviewToggled: (state) => {
state.isolatedTransformingPreview = !state.isolatedTransformingPreview;
settingsIsolatedLayerPreviewToggled: (state) => {
state.isolatedLayerPreview = !state.isolatedLayerPreview;
},
settingsPressureSensitivityToggled: (state) => {
state.pressureSensitivity = !state.pressureSensitivity;
@@ -191,8 +183,7 @@ export const {
settingsBboxOverlayToggled,
settingsPreserveMaskToggled,
settingsIsolatedStagingPreviewToggled,
settingsIsolatedFilteringPreviewToggled,
settingsIsolatedTransformingPreviewToggled,
settingsIsolatedLayerPreviewToggled,
settingsPressureSensitivityToggled,
} = canvasSettingsSlice.actions;
@@ -226,10 +217,5 @@ export const selectShowProgressOnCanvas = createCanvasSettingsSelector(
(canvasSettings) => canvasSettings.showProgressOnCanvas
);
export const selectIsolatedStagingPreview = createCanvasSettingsSelector((settings) => settings.isolatedStagingPreview);
export const selectIsolatedFilteringPreview = createCanvasSettingsSelector(
(settings) => settings.isolatedFilteringPreview
);
export const selectIsolatedTransformingPreview = createCanvasSettingsSelector(
(settings) => settings.isolatedTransformingPreview
);
export const selectIsolatedLayerPreview = createCanvasSettingsSelector((settings) => settings.isolatedLayerPreview);
export const selectPressureSensitivity = createCanvasSettingsSelector((settings) => settings.pressureSensitivity);