mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): ip adapter layers are selectable
This is largely an internal change, and it should have been this way from the start - less tip-toeing around layer types. The user-facing change is when you click an IP Adapter layer, it is highlighted. That's it.
This commit is contained in:
parent
b180666497
commit
2f9a064d48
@ -19,7 +19,6 @@ export const CALayer = memo(({ layerId }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isSelected = useAppSelector((s) => selectCALayerOrThrow(s.controlLayers.present, layerId).isSelected);
|
||||
const onClick = useCallback(() => {
|
||||
// Must be capture so that the layer is selected before deleting/resetting/etc
|
||||
dispatch(layerSelected(layerId));
|
||||
}, [dispatch, layerId]);
|
||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||
|
@ -1,19 +1,26 @@
|
||||
import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { IPALayerIPAdapterWrapper } from 'features/controlLayers/components/IPALayer/IPALayerIPAdapterWrapper';
|
||||
import { LayerDeleteButton } from 'features/controlLayers/components/LayerCommon/LayerDeleteButton';
|
||||
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
||||
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
||||
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
||||
import { memo } from 'react';
|
||||
import { layerSelected, selectIPALayerOrThrow } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
type Props = {
|
||||
layerId: string;
|
||||
};
|
||||
|
||||
export const IPALayer = memo(({ layerId }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isSelected = useAppSelector((s) => selectIPALayerOrThrow(s.controlLayers.present, layerId).isSelected);
|
||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(layerSelected(layerId));
|
||||
}, [dispatch, layerId]);
|
||||
return (
|
||||
<LayerWrapper borderColor="base.800">
|
||||
<LayerWrapper onClick={onClick} borderColor={isSelected ? 'base.400' : 'base.800'}>
|
||||
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
||||
<LayerVisibilityToggle layerId={layerId} />
|
||||
<LayerTitle type="ip_adapter_layer" />
|
||||
|
@ -124,11 +124,11 @@ const getVectorMaskPreviewColor = (state: ControlLayersState): RgbColor => {
|
||||
const lastColor = rgLayers[rgLayers.length - 1]?.previewColor;
|
||||
return LayerColors.next(lastColor);
|
||||
};
|
||||
const deselectAllLayers = (state: ControlLayersState) => {
|
||||
for (const layer of state.layers.filter(isRenderableLayer)) {
|
||||
layer.isSelected = false;
|
||||
const exclusivelySelectLayer = (state: ControlLayersState, layerId: string) => {
|
||||
for (const layer of state.layers) {
|
||||
layer.isSelected = layer.id === layerId;
|
||||
}
|
||||
state.selectedLayerId = null;
|
||||
state.selectedLayerId = layerId;
|
||||
};
|
||||
|
||||
export const controlLayersSlice = createSlice({
|
||||
@ -137,12 +137,7 @@ export const controlLayersSlice = createSlice({
|
||||
reducers: {
|
||||
//#region Any Layer Type
|
||||
layerSelected: (state, action: PayloadAction<string>) => {
|
||||
deselectAllLayers(state);
|
||||
const layer = state.layers.find((l) => l.id === action.payload);
|
||||
if (isRenderableLayer(layer)) {
|
||||
layer.isSelected = true;
|
||||
state.selectedLayerId = layer.id;
|
||||
}
|
||||
exclusivelySelectLayer(state, action.payload);
|
||||
},
|
||||
layerVisibilityToggled: (state, action: PayloadAction<string>) => {
|
||||
const layer = state.layers.find((l) => l.id === action.payload);
|
||||
@ -232,7 +227,6 @@ export const controlLayersSlice = createSlice({
|
||||
action: PayloadAction<{ layerId: string; controlAdapter: ControlNetConfigV2 | T2IAdapterConfigV2 }>
|
||||
) => {
|
||||
const { layerId, controlAdapter } = action.payload;
|
||||
deselectAllLayers(state);
|
||||
const layer: ControlAdapterLayer = {
|
||||
id: getCALayerId(layerId),
|
||||
type: 'control_adapter_layer',
|
||||
@ -247,16 +241,15 @@ export const controlLayersSlice = createSlice({
|
||||
controlAdapter,
|
||||
};
|
||||
state.layers.push(layer);
|
||||
state.selectedLayerId = layer.id;
|
||||
exclusivelySelectLayer(state, layer.id);
|
||||
},
|
||||
prepare: (controlAdapter: ControlNetConfigV2 | T2IAdapterConfigV2) => ({
|
||||
payload: { layerId: uuidv4(), controlAdapter },
|
||||
}),
|
||||
},
|
||||
caLayerRecalled: (state, action: PayloadAction<ControlAdapterLayer>) => {
|
||||
deselectAllLayers(state);
|
||||
state.layers.push({ ...action.payload, isSelected: true });
|
||||
state.selectedLayerId = action.payload.id;
|
||||
exclusivelySelectLayer(state, action.payload.id);
|
||||
},
|
||||
caLayerImageChanged: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => {
|
||||
const { layerId, imageDTO } = action.payload;
|
||||
@ -359,9 +352,11 @@ export const controlLayersSlice = createSlice({
|
||||
id: getIPALayerId(layerId),
|
||||
type: 'ip_adapter_layer',
|
||||
isEnabled: true,
|
||||
isSelected: true,
|
||||
ipAdapter,
|
||||
};
|
||||
state.layers.push(layer);
|
||||
exclusivelySelectLayer(state, layer.id);
|
||||
},
|
||||
prepare: (ipAdapter: IPAdapterConfigV2) => ({ payload: { layerId: uuidv4(), ipAdapter } }),
|
||||
},
|
||||
@ -431,7 +426,6 @@ export const controlLayersSlice = createSlice({
|
||||
rgLayerAdded: {
|
||||
reducer: (state, action: PayloadAction<{ layerId: string }>) => {
|
||||
const { layerId } = action.payload;
|
||||
deselectAllLayers(state);
|
||||
const layer: RegionalGuidanceLayer = {
|
||||
id: getRGLayerId(layerId),
|
||||
type: 'regional_guidance_layer',
|
||||
@ -450,14 +444,13 @@ export const controlLayersSlice = createSlice({
|
||||
uploadedMaskImage: null,
|
||||
};
|
||||
state.layers.push(layer);
|
||||
state.selectedLayerId = layer.id;
|
||||
exclusivelySelectLayer(state, layer.id);
|
||||
},
|
||||
prepare: () => ({ payload: { layerId: uuidv4() } }),
|
||||
},
|
||||
rgLayerRecalled: (state, action: PayloadAction<RegionalGuidanceLayer>) => {
|
||||
deselectAllLayers(state);
|
||||
state.layers.push({ ...action.payload, isSelected: true });
|
||||
state.selectedLayerId = action.payload.id;
|
||||
exclusivelySelectLayer(state, action.payload.id);
|
||||
},
|
||||
rgLayerPositivePromptChanged: (state, action: PayloadAction<{ layerId: string; prompt: string | null }>) => {
|
||||
const { layerId, prompt } = action.payload;
|
||||
@ -622,7 +615,6 @@ export const controlLayersSlice = createSlice({
|
||||
//#region Initial Image Layer
|
||||
iiLayerAdded: {
|
||||
reducer: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => {
|
||||
deselectAllLayers(state);
|
||||
const { layerId, imageDTO } = action.payload;
|
||||
// Highlander! There can be only one!
|
||||
state.layers = state.layers.filter((l) => (isInitialImageLayer(l) ? false : true));
|
||||
@ -640,15 +632,14 @@ export const controlLayersSlice = createSlice({
|
||||
denoisingStrength: 0.75,
|
||||
};
|
||||
state.layers.push(layer);
|
||||
state.selectedLayerId = layer.id;
|
||||
exclusivelySelectLayer(state, layer.id);
|
||||
},
|
||||
prepare: (imageDTO: ImageDTO | null) => ({ payload: { layerId: INITIAL_IMAGE_LAYER_ID, imageDTO } }),
|
||||
},
|
||||
iiLayerRecalled: (state, action: PayloadAction<InitialImageLayer>) => {
|
||||
deselectAllLayers(state);
|
||||
state.layers = state.layers.filter((l) => (isInitialImageLayer(l) ? false : true));
|
||||
state.layers.push({ ...action.payload, isSelected: true });
|
||||
state.selectedLayerId = action.payload.id;
|
||||
exclusivelySelectLayer(state, action.payload.id);
|
||||
},
|
||||
iiLayerImageChanged: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => {
|
||||
const { layerId, imageDTO } = action.payload;
|
||||
|
@ -48,7 +48,8 @@ export type VectorMaskRect = z.infer<typeof zVectorMaskRect>;
|
||||
|
||||
const zLayerBase = z.object({
|
||||
id: z.string(),
|
||||
isEnabled: z.boolean(),
|
||||
isEnabled: z.boolean().default(true),
|
||||
isSelected: z.boolean().default(true),
|
||||
});
|
||||
|
||||
const zRect = z.object({
|
||||
@ -62,7 +63,6 @@ const zRenderableLayerBase = zLayerBase.extend({
|
||||
y: z.number(),
|
||||
bbox: zRect.nullable(),
|
||||
bboxNeedsUpdate: z.boolean(),
|
||||
isSelected: z.boolean(),
|
||||
});
|
||||
|
||||
const zControlAdapterLayer = zRenderableLayerBase.extend({
|
||||
|
@ -702,6 +702,7 @@ const renderLayers = (
|
||||
if (isInitialImageLayer(reduxLayer)) {
|
||||
renderInitialImageLayer(stage, reduxLayer);
|
||||
}
|
||||
// IP Adapter layers are not rendered
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -692,8 +692,9 @@ const parseIPAdapterToIPAdapterLayer: MetadataParseFunc<IPAdapterLayer> = async
|
||||
|
||||
const layer: IPAdapterLayer = {
|
||||
id: getIPALayerId(uuidv4()),
|
||||
isEnabled: true,
|
||||
type: 'ip_adapter_layer',
|
||||
isEnabled: true,
|
||||
isSelected: true,
|
||||
ipAdapter: {
|
||||
id: uuidv4(),
|
||||
type: 'ip_adapter',
|
||||
|
Loading…
Reference in New Issue
Block a user