mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-16 13:04:57 -05:00
feat(ui): revise filter implementation
This commit is contained in:
@@ -6,20 +6,23 @@ import { InpaintMaskList } from 'features/controlLayers/components/InpaintMask/I
|
||||
import { IPAdapterList } from 'features/controlLayers/components/IPAdapter/IPAdapterList';
|
||||
import { RasterLayerEntityList } from 'features/controlLayers/components/RasterLayer/RasterLayerEntityList';
|
||||
import { RegionalGuidanceEntityList } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceEntityList';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const CanvasEntityList = memo(() => {
|
||||
return (
|
||||
<ScrollableContent>
|
||||
<Flex flexDir="column" gap={4} pt={2} data-testid="control-layers-layer-list">
|
||||
<CanvasEntityOpacity />
|
||||
<InpaintMaskList />
|
||||
<RegionalGuidanceEntityList />
|
||||
<IPAdapterList />
|
||||
<ControlLayerEntityList />
|
||||
<RasterLayerEntityList />
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
<CanvasManagerProviderGate>
|
||||
<ScrollableContent>
|
||||
<Flex flexDir="column" gap={4} pt={2} data-testid="control-layers-layer-list">
|
||||
<CanvasEntityOpacity />
|
||||
<InpaintMaskList />
|
||||
<RegionalGuidanceEntityList />
|
||||
<IPAdapterList />
|
||||
<ControlLayerEntityList />
|
||||
<RasterLayerEntityList />
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
</CanvasManagerProviderGate>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Combobox, FormControl, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { $filterConfig, $filteringEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { IMAGE_FILTERS, isFilterType } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -17,6 +17,7 @@ type Props = {
|
||||
export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onChangeModel }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const canvasManager = useCanvasManager();
|
||||
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
|
||||
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);
|
||||
@@ -35,19 +36,21 @@ export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onCha
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the filter, preferring the model's default
|
||||
if (isFilterType(modelConfig.default_settings?.preprocessor)) {
|
||||
$filterConfig.set(IMAGE_FILTERS[modelConfig.default_settings.preprocessor].buildDefaults(modelConfig.base));
|
||||
} else {
|
||||
$filterConfig.set(IMAGE_FILTERS.canny_image_processor.buildDefaults(modelConfig.base));
|
||||
}
|
||||
|
||||
// Open the filter popup by setting this entity as the filtering entity
|
||||
if (!$filteringEntity.get()) {
|
||||
$filteringEntity.set(entityIdentifier);
|
||||
if (!canvasManager.filter.$adapter.get()) {
|
||||
// Update the filter, preferring the model's default
|
||||
if (isFilterType(modelConfig.default_settings?.preprocessor)) {
|
||||
canvasManager.filter.$config.set(
|
||||
IMAGE_FILTERS[modelConfig.default_settings.preprocessor].buildDefaults(modelConfig.base)
|
||||
);
|
||||
} else {
|
||||
canvasManager.filter.$config.set(IMAGE_FILTERS.canny_image_processor.buildDefaults(modelConfig.base));
|
||||
}
|
||||
canvasManager.filter.initialize(entityIdentifier);
|
||||
canvasManager.filter.previewFilter();
|
||||
}
|
||||
},
|
||||
[entityIdentifier, modelKey, onChangeModel]
|
||||
[canvasManager.filter, entityIdentifier, modelKey, onChangeModel]
|
||||
);
|
||||
|
||||
const getIsDisabled = useCallback(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ControlLayersToolbar } from 'features/controlLayers/components/ControlL
|
||||
import { Filter } from 'features/controlLayers/components/Filters/Filter';
|
||||
import { StageComponent } from 'features/controlLayers/components/StageComponent';
|
||||
import { StagingAreaToolbar } from 'features/controlLayers/components/StagingArea/StagingAreaToolbar';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { memo, useRef } from 'react';
|
||||
|
||||
export const CanvasEditor = memo(() => {
|
||||
@@ -33,7 +34,9 @@ export const CanvasEditor = memo(() => {
|
||||
<StagingAreaToolbar />
|
||||
</Flex>
|
||||
<Flex position="absolute" bottom={16}>
|
||||
<Filter />
|
||||
<CanvasManagerProviderGate>
|
||||
<Filter />
|
||||
</CanvasManagerProviderGate>
|
||||
</Flex>
|
||||
<CanvasDropArea />
|
||||
</Flex>
|
||||
|
||||
@@ -2,8 +2,7 @@ import { Button, ButtonGroup, Flex, Heading } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { FilterSettings } from 'features/controlLayers/components/Filters/FilterSettings';
|
||||
import { FilterTypeSelect } from 'features/controlLayers/components/Filters/FilterTypeSelect';
|
||||
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { $filterConfig, $filteringEntity, $isProcessingFilter } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { type FilterConfig, IMAGE_FILTERS } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -11,64 +10,38 @@ import { PiCheckBold, PiShootingStarBold, PiXBold } from 'react-icons/pi';
|
||||
|
||||
export const Filter = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const filteringEntity = useStore($filteringEntity);
|
||||
const filterConfig = useStore($filterConfig);
|
||||
const isProcessingFilter = useStore($isProcessingFilter);
|
||||
const canvasManager = useCanvasManager();
|
||||
const adapter = useStore(canvasManager.filter.$adapter);
|
||||
const config = useStore(canvasManager.filter.$config);
|
||||
const isProcessing = useStore(canvasManager.filter.$isProcessing);
|
||||
|
||||
const previewFilter = useCallback(() => {
|
||||
if (!filteringEntity) {
|
||||
return;
|
||||
}
|
||||
const canvasManager = $canvasManager.get();
|
||||
if (!canvasManager) {
|
||||
return;
|
||||
}
|
||||
const entity = canvasManager.stateApi.getEntity(filteringEntity);
|
||||
if (!entity || (entity.type !== 'raster_layer' && entity.type !== 'control_layer')) {
|
||||
return;
|
||||
}
|
||||
entity.adapter.filter.previewFilter();
|
||||
}, [filteringEntity]);
|
||||
canvasManager.filter.previewFilter();
|
||||
}, [canvasManager.filter]);
|
||||
|
||||
const applyFilter = useCallback(() => {
|
||||
if (!filteringEntity) {
|
||||
return;
|
||||
}
|
||||
const canvasManager = $canvasManager.get();
|
||||
if (!canvasManager) {
|
||||
return;
|
||||
}
|
||||
const entity = canvasManager.stateApi.getEntity(filteringEntity);
|
||||
if (!entity || (entity.type !== 'raster_layer' && entity.type !== 'control_layer')) {
|
||||
return;
|
||||
}
|
||||
entity.adapter.filter.applyFilter();
|
||||
}, [filteringEntity]);
|
||||
canvasManager.filter.applyFilter();
|
||||
}, [canvasManager.filter]);
|
||||
|
||||
const cancelFilter = useCallback(() => {
|
||||
if (!filteringEntity) {
|
||||
return;
|
||||
}
|
||||
const canvasManager = $canvasManager.get();
|
||||
if (!canvasManager) {
|
||||
return;
|
||||
}
|
||||
const entity = canvasManager.stateApi.getEntity(filteringEntity);
|
||||
if (!entity || (entity.type !== 'raster_layer' && entity.type !== 'control_layer')) {
|
||||
return;
|
||||
}
|
||||
entity.adapter.filter.cancelFilter();
|
||||
}, [filteringEntity]);
|
||||
canvasManager.filter.cancelFilter();
|
||||
}, [canvasManager.filter]);
|
||||
|
||||
const onChangeFilterConfig = useCallback((filterConfig: FilterConfig) => {
|
||||
$filterConfig.set(filterConfig);
|
||||
}, []);
|
||||
const onChangeFilterConfig = useCallback(
|
||||
(filterConfig: FilterConfig) => {
|
||||
canvasManager.filter.$config.set(filterConfig);
|
||||
},
|
||||
[canvasManager.filter.$config]
|
||||
);
|
||||
|
||||
const onChangeFilterType = useCallback((filterType: FilterConfig['type']) => {
|
||||
$filterConfig.set(IMAGE_FILTERS[filterType].buildDefaults());
|
||||
}, []);
|
||||
const onChangeFilterType = useCallback(
|
||||
(filterType: FilterConfig['type']) => {
|
||||
canvasManager.filter.$config.set(IMAGE_FILTERS[filterType].buildDefaults());
|
||||
},
|
||||
[canvasManager.filter.$config]
|
||||
);
|
||||
|
||||
if (!filteringEntity || !filterConfig) {
|
||||
if (!adapter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -88,13 +61,13 @@ export const Filter = memo(() => {
|
||||
<Heading size="md" color="base.300" userSelect="none">
|
||||
{t('controlLayers.filter.filter')}
|
||||
</Heading>
|
||||
<FilterTypeSelect filterType={filterConfig.type} onChange={onChangeFilterType} />
|
||||
<FilterSettings filterConfig={filterConfig} onChange={onChangeFilterConfig} />
|
||||
<FilterTypeSelect filterType={config.type} onChange={onChangeFilterType} />
|
||||
<FilterSettings filterConfig={config} onChange={onChangeFilterConfig} />
|
||||
<ButtonGroup isAttached={false} size="sm" alignSelf="self-end">
|
||||
<Button
|
||||
leftIcon={<PiShootingStarBold />}
|
||||
onClick={previewFilter}
|
||||
isLoading={isProcessingFilter}
|
||||
isLoading={isProcessing}
|
||||
loadingText={t('controlLayers.filter.preview')}
|
||||
>
|
||||
{t('controlLayers.filter.preview')}
|
||||
@@ -102,7 +75,7 @@ export const Filter = memo(() => {
|
||||
<Button
|
||||
leftIcon={<PiCheckBold />}
|
||||
onClick={applyFilter}
|
||||
isLoading={isProcessingFilter}
|
||||
isLoading={isProcessing}
|
||||
loadingText={t('controlLayers.filter.apply')}
|
||||
>
|
||||
{t('controlLayers.filter.apply')}
|
||||
@@ -110,7 +83,7 @@ export const Filter = memo(() => {
|
||||
<Button
|
||||
leftIcon={<PiXBold />}
|
||||
onClick={cancelFilter}
|
||||
isLoading={isProcessingFilter}
|
||||
isLoading={isProcessing}
|
||||
loadingText={t('controlLayers.filter.cancel')}
|
||||
>
|
||||
{t('controlLayers.filter.cancel')}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { $filteringEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiShootingStarBold } from 'react-icons/pi';
|
||||
|
||||
export const CanvasEntityMenuItemsFilter = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const canvasManager = useCanvasManager();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const filter = useCallback(() => {
|
||||
$filteringEntity.set(entityIdentifier);
|
||||
}, [entityIdentifier]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
canvasManager.filter.initialize(entityIdentifier);
|
||||
}, [entityIdentifier, canvasManager.filter]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={filter} icon={<PiShootingStarBold />}>
|
||||
<MenuItem onClick={onClick} icon={<PiShootingStarBold />}>
|
||||
{t('controlLayers.filter.filter')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user