From 5c0676bcc25ad76047ba74470fb6cdd11d710e55 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:06:49 +1000 Subject: [PATCH] feat(ui): support image objects on raster layers Just the UI and internal state, not rendering yet. --- .../listeners/imageDropped.ts | 19 +++++++++++++++ .../components/LayerCommon/LayerWrapper.tsx | 1 + .../components/RasterLayer/RasterLayer.tsx | 14 ++++++++++- .../controlLayers/store/controlLayersSlice.ts | 24 ++++++++++++++++++- .../src/features/controlLayers/store/types.ts | 4 +++- .../web/src/features/dnd/types/index.ts | 7 ++++++ .../web/src/features/dnd/util/isValidDrop.ts | 2 ++ 7 files changed, 68 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts index a65c31b7cd..b5431508cf 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts @@ -10,6 +10,7 @@ import { import { caLayerImageChanged, iiLayerImageChanged, + imageAdded, ipaLayerImageChanged, rgLayerIPAdapterImageChanged, } from 'features/controlLayers/store/controlLayersSlice'; @@ -161,6 +162,24 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) => return; } + /** + * Image dropped on Raster layer + */ + if ( + overData.actionType === 'ADD_RASTER_LAYER_IMAGE' && + activeData.payloadType === 'IMAGE_DTO' && + activeData.payload.imageDTO + ) { + const { layerId } = overData.context; + dispatch( + imageAdded({ + layerId, + imageDTO: activeData.payload.imageDTO, + }) + ); + return; + } + /** * Image dropped on Canvas */ diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx b/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx index 9757cf3972..804ae40070 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx @@ -11,6 +11,7 @@ type Props = PropsWithChildren<{ export const LayerWrapper = memo(({ onClick, borderColor, children }: Props) => { return ( { }, [dispatch, layerId]); const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true }); + const droppableData = useMemo(() => { + const _droppableData: RasterLayerImageDropData = { + id: layerId, + actionType: 'ADD_RASTER_LAYER_IMAGE', + context: { layerId }, + }; + return _droppableData; + }, [layerId]); + return ( @@ -39,6 +50,7 @@ export const RasterLayer = memo(({ layerId }: Props) => { PLACEHOLDER )} + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts index a1709cac6d..4c2c98fe40 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts @@ -8,6 +8,7 @@ import { getBrushLineId, getCALayerId, getEraserLineId, + getImageObjectId, getIPALayerId, getRasterLayerId, getRectId, @@ -48,6 +49,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { AddBrushLineArg, AddEraserLineArg, + AddImageObjectArg, AddPointToLineArg, AddRectShapeArg, ControlAdapterLayer, @@ -715,7 +717,7 @@ export const controlLayersSlice = createSlice({ return; } const layer = selectLayerOrThrow(state, layerId, isRGOrRasterlayer); - const id = getRectId(layer.id, rectUuid); + const id = getRectShapeId(layer.id, rectUuid); layer.objects.push({ type: 'rect_shape', id, @@ -732,6 +734,25 @@ export const controlLayersSlice = createSlice({ }, prepare: (payload: AddRectShapeArg) => ({ payload: { ...payload, rectUuid: uuidv4() } }), }, + imageAdded: { + reducer: (state, action: PayloadAction) => { + const { layerId, imageUuid, imageDTO } = action.payload; + const layer = selectLayerOrThrow(state, layerId, isRasterLayer); + const id = getImageObjectId(layer.id, imageUuid); + const { width, height, image_name: name } = imageDTO; + layer.objects.push({ + type: 'image', + id, + x: 0, + y: 0, + width, + height, + image: { width, height, name }, + }); + layer.bboxNeedsUpdate = true; + }, + prepare: (payload: AddImageObjectArg) => ({ payload: { ...payload, imageUuid: uuidv4() } }), + }, //#endregion //#region Globals @@ -897,6 +918,7 @@ export const { eraserLineAdded, linePointsAdded, rectAdded, + imageAdded, rgLayerMaskImageUploaded, rgLayerAutoNegativeChanged, rgLayerIPAdapterAdded, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index 32ed9a674b..8fea17406c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -20,6 +20,7 @@ import { zParameterStrength, } from 'features/parameters/types/parameterSchemas'; import type { IRect } from 'konva/lib/types'; +import type { ImageDTO } from 'services/api/types'; import { z } from 'zod'; const zTool = z.enum(['brush', 'eraser', 'move', 'rect']); @@ -273,7 +274,8 @@ export type ControlLayersState = { export type AddEraserLineArg = { layerId: string; points: [number, number, number, number] }; export type AddBrushLineArg = AddEraserLineArg & { color: RgbaColor }; export type AddPointToLineArg = { layerId: string; point: [number, number] }; -export type AddRectShapeArg = { layerId: string; rect: IRect; color: RgbaColor }; //#region Type guards +export type AddRectShapeArg = { layerId: string; rect: IRect; color: RgbaColor }; +export type AddImageObjectArg = { layerId: string; imageDTO: ImageDTO }; //#region Type guards export const isLine = (obj: AnyLayerObject): obj is BrushLine | EraserLine => { diff --git a/invokeai/frontend/web/src/features/dnd/types/index.ts b/invokeai/frontend/web/src/features/dnd/types/index.ts index 93bde117a1..1d99f4a415 100644 --- a/invokeai/frontend/web/src/features/dnd/types/index.ts +++ b/invokeai/frontend/web/src/features/dnd/types/index.ts @@ -58,6 +58,13 @@ export type IILayerImageDropData = BaseDropData & { }; }; +export type RasterLayerImageDropData = BaseDropData & { + actionType: 'ADD_RASTER_LAYER_IMAGE'; + context: { + layerId: string; + }; +}; + export type CanvasInitialImageDropData = BaseDropData & { actionType: 'SET_CANVAS_INITIAL_IMAGE'; }; diff --git a/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts b/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts index 3f8fe5ab73..4f0a31d387 100644 --- a/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts +++ b/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts @@ -33,6 +33,8 @@ export const isValidDrop = (overData?: TypesafeDroppableData | null, activeData? return payloadType === 'IMAGE_DTO'; case 'SELECT_FOR_COMPARE': return payloadType === 'IMAGE_DTO'; + case 'ADD_RASTER_LAYER_IMAGE': + return payloadType === 'IMAGE_DTO'; case 'ADD_TO_BOARD': { // If the board is the same, don't allow the drop