mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): fix layer arranging
This commit is contained in:
parent
d884c15d0c
commit
e5ec529f0f
invokeai/frontend/web/src/features/regionalPrompts
@ -8,39 +8,16 @@ import { ControlAdapterLayerListItem } from 'features/regionalPrompts/components
|
||||
import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton';
|
||||
import { IPAdapterLayerListItem } from 'features/regionalPrompts/components/IPAdapterLayerListItem';
|
||||
import { MaskedGuidanceLayerListItem } from 'features/regionalPrompts/components/MaskedGuidanceLayerListItem';
|
||||
import {
|
||||
isControlAdapterLayer,
|
||||
isIPAdapterLayer,
|
||||
isMaskedGuidanceLayer,
|
||||
selectRegionalPromptsSlice,
|
||||
} from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||
import type { Layer } from 'features/regionalPrompts/store/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectMaskedGuidanceLayerIds = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
|
||||
regionalPrompts.present.layers
|
||||
.filter(isMaskedGuidanceLayer)
|
||||
.map((l) => l.id)
|
||||
.reverse()
|
||||
);
|
||||
|
||||
const selectControlNetLayerIds = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
|
||||
regionalPrompts.present.layers
|
||||
.filter(isControlAdapterLayer)
|
||||
.map((l) => l.id)
|
||||
.reverse()
|
||||
);
|
||||
|
||||
const selectIPAdapterLayerIds = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
|
||||
regionalPrompts.present.layers
|
||||
.filter(isIPAdapterLayer)
|
||||
.map((l) => l.id)
|
||||
.reverse()
|
||||
const selectLayerIdTypePairs = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
|
||||
regionalPrompts.present.layers.map((l) => ({ id: l.id, type: l.type })).reverse()
|
||||
);
|
||||
|
||||
export const RegionalPromptsPanelContent = memo(() => {
|
||||
const maskedGuidanceLayerIds = useAppSelector(selectMaskedGuidanceLayerIds);
|
||||
const controlNetLayerIds = useAppSelector(selectControlNetLayerIds);
|
||||
const ipAdapterLayerIds = useAppSelector(selectIPAdapterLayerIds);
|
||||
const layerIdTypePairs = useAppSelector(selectLayerIdTypePairs);
|
||||
return (
|
||||
<Flex flexDir="column" gap={4} w="full" h="full">
|
||||
<Flex justifyContent="space-around">
|
||||
@ -49,14 +26,8 @@ export const RegionalPromptsPanelContent = memo(() => {
|
||||
</Flex>
|
||||
<ScrollableContent>
|
||||
<Flex flexDir="column" gap={4}>
|
||||
{maskedGuidanceLayerIds.map((id) => (
|
||||
<MaskedGuidanceLayerListItem key={id} layerId={id} />
|
||||
))}
|
||||
{controlNetLayerIds.map((id) => (
|
||||
<ControlAdapterLayerListItem key={id} layerId={id} />
|
||||
))}
|
||||
{ipAdapterLayerIds.map((id) => (
|
||||
<IPAdapterLayerListItem key={id} layerId={id} />
|
||||
{layerIdTypePairs.map(({ id, type }) => (
|
||||
<LayerWrapper key={id} id={id} type={type} />
|
||||
))}
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
@ -65,3 +36,22 @@ export const RegionalPromptsPanelContent = memo(() => {
|
||||
});
|
||||
|
||||
RegionalPromptsPanelContent.displayName = 'RegionalPromptsPanelContent';
|
||||
|
||||
type LayerWrapperProps = {
|
||||
id: string;
|
||||
type: Layer['type'];
|
||||
};
|
||||
|
||||
const LayerWrapper = memo(({ id, type }: LayerWrapperProps) => {
|
||||
if (type === 'masked_guidance_layer') {
|
||||
return <MaskedGuidanceLayerListItem key={id} layerId={id} />;
|
||||
}
|
||||
if (type === 'control_adapter_layer') {
|
||||
return <ControlAdapterLayerListItem key={id} layerId={id} />;
|
||||
}
|
||||
if (type === 'ip_adapter_layer') {
|
||||
return <IPAdapterLayerListItem key={id} layerId={id} />;
|
||||
}
|
||||
});
|
||||
|
||||
LayerWrapper.displayName = 'LayerWrapper';
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
isMaskedGuidanceLayer,
|
||||
selectRegionalPromptsSlice,
|
||||
} from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||
import { isMaskedGuidanceLayer, selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||
import { useMemo } from 'react';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
@ -50,3 +47,17 @@ export const useLayerIsVisible = (layerId: string) => {
|
||||
const isVisible = useAppSelector(selectLayer);
|
||||
return isVisible;
|
||||
};
|
||||
|
||||
export const useLayerType = (layerId: string) => {
|
||||
const selectLayer = useMemo(
|
||||
() =>
|
||||
createSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
|
||||
const layer = regionalPrompts.present.layers.find((l) => l.id === layerId);
|
||||
assert(layer, `Layer ${layerId} not found`);
|
||||
return layer.type;
|
||||
}),
|
||||
[layerId]
|
||||
);
|
||||
const type = useAppSelector(selectLayer);
|
||||
return type;
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||
import type { ParameterAutoNegative } from 'features/parameters/types/parameterSchemas';
|
||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { isEqual, partition } from 'lodash-es';
|
||||
import { atom } from 'nanostores';
|
||||
import type { RgbColor } from 'react-colorful';
|
||||
import type { UndoableOptions } from 'redux-undo';
|
||||
@ -183,21 +183,29 @@ export const regionalPromptsSlice = createSlice({
|
||||
},
|
||||
layerMovedForward: (state, action: PayloadAction<string>) => {
|
||||
const cb = (l: Layer) => l.id === action.payload;
|
||||
moveForward(state.layers, cb);
|
||||
const [renderableLayers, ipAdapterLayers] = partition(state.layers, isRenderableLayer);
|
||||
moveForward(renderableLayers, cb);
|
||||
state.layers = [...ipAdapterLayers, ...renderableLayers];
|
||||
},
|
||||
layerMovedToFront: (state, action: PayloadAction<string>) => {
|
||||
const cb = (l: Layer) => l.id === action.payload;
|
||||
const [renderableLayers, ipAdapterLayers] = partition(state.layers, isRenderableLayer);
|
||||
// Because the layers are in reverse order, moving to the front is equivalent to moving to the back
|
||||
moveToBack(state.layers, cb);
|
||||
moveToBack(renderableLayers, cb);
|
||||
state.layers = [...ipAdapterLayers, ...renderableLayers];
|
||||
},
|
||||
layerMovedBackward: (state, action: PayloadAction<string>) => {
|
||||
const cb = (l: Layer) => l.id === action.payload;
|
||||
moveBackward(state.layers, cb);
|
||||
const [renderableLayers, ipAdapterLayers] = partition(state.layers, isRenderableLayer);
|
||||
moveBackward(renderableLayers, cb);
|
||||
state.layers = [...ipAdapterLayers, ...renderableLayers];
|
||||
},
|
||||
layerMovedToBack: (state, action: PayloadAction<string>) => {
|
||||
const cb = (l: Layer) => l.id === action.payload;
|
||||
const [renderableLayers, ipAdapterLayers] = partition(state.layers, isRenderableLayer);
|
||||
// Because the layers are in reverse order, moving to the back is equivalent to moving to the front
|
||||
moveToFront(state.layers, cb);
|
||||
moveToFront(renderableLayers, cb);
|
||||
state.layers = [...ipAdapterLayers, ...renderableLayers];
|
||||
},
|
||||
selectedLayerReset: (state) => {
|
||||
const layer = state.layers.find((l) => l.id === state.selectedLayerId);
|
||||
|
Loading…
x
Reference in New Issue
Block a user