mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): dnd image into layer
This commit is contained in:
parent
c988c58c63
commit
0b5f4cac57
@ -5,9 +5,10 @@ import { parseify } from 'common/util/serialize';
|
||||
import {
|
||||
caImageChanged,
|
||||
ipaImageChanged,
|
||||
layerImageAdded,
|
||||
layerAddedFromImage,
|
||||
rgIPAdapterImageChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||
import {
|
||||
@ -28,7 +29,7 @@ export const dndDropped = createAction<{
|
||||
export const addImageDroppedListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: dndDropped,
|
||||
effect: async (action, { dispatch, getState }) => {
|
||||
effect: (action, { dispatch, getState }) => {
|
||||
const log = logger('dnd');
|
||||
const { activeData, overData } = action.payload;
|
||||
if (!isValidDrop(overData, activeData)) {
|
||||
@ -101,12 +102,11 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
* Image dropped on Raster layer
|
||||
*/
|
||||
if (
|
||||
overData.actionType === 'ADD_LAYER_IMAGE' &&
|
||||
overData.actionType === 'ADD_LAYER_FROM_IMAGE' &&
|
||||
activeData.payloadType === 'IMAGE_DTO' &&
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const { id } = overData.context;
|
||||
dispatch(layerImageAdded({ id, imageDTO: activeData.payload.imageDTO }));
|
||||
dispatch(layerAddedFromImage({ imageObject: imageDTOToImageObject(activeData.payload.imageDTO) }));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import IAIDroppable from 'common/components/IAIDroppable';
|
||||
import type { AddLayerFromImageDropData } from 'features/dnd/types';
|
||||
import { memo } from 'react';
|
||||
|
||||
const addLayerFromImageDropData: AddLayerFromImageDropData = {
|
||||
id: 'add-layer-from-image-drop-data',
|
||||
actionType: 'ADD_LAYER_FROM_IMAGE',
|
||||
};
|
||||
|
||||
export const CanvasDropArea = memo(() => {
|
||||
return (
|
||||
<Flex position="absolute" top={0} right={0} bottom={0} left={0} gap={2} pointerEvents="none">
|
||||
<IAIDroppable dropLabel="Create Layer" data={addLayerFromImageDropData} />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasDropArea.displayName = 'CanvasDropArea';
|
@ -1,5 +1,6 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { CanvasDropArea } from 'features/controlLayers/components/CanvasDropArea';
|
||||
import { ControlLayersToolbar } from 'features/controlLayers/components/ControlLayersToolbar';
|
||||
import { StageComponent } from 'features/controlLayers/components/StageComponent';
|
||||
import { StagingAreaToolbar } from 'features/controlLayers/components/StagingArea/StagingAreaToolbar';
|
||||
@ -24,6 +25,7 @@ export const ControlLayersEditor = memo(() => {
|
||||
{/* <Flex position="absolute" top={0} right={0} bottom={0} left={0} align="center" justify="center">
|
||||
<CanvasResizer />
|
||||
</Flex> */}
|
||||
<CanvasDropArea />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ import { LayerActionsMenu } from 'features/controlLayers/components/Layer/LayerA
|
||||
import { LayerSettings } from 'features/controlLayers/components/Layer/LayerSettings';
|
||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import type { LayerImageDropData } from 'features/dnd/types';
|
||||
import type { AddLayerFromImageDropData } from 'features/dnd/types';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import { LayerOpacity } from './LayerOpacity';
|
||||
@ -21,7 +21,7 @@ type Props = {
|
||||
export const Layer = memo(({ id }: Props) => {
|
||||
const entityIdentifier = useMemo<CanvasEntityIdentifier>(() => ({ id, type: 'layer' }), [id]);
|
||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: false });
|
||||
const droppableData = useMemo<LayerImageDropData>(
|
||||
const droppableData = useMemo<AddLayerFromImageDropData>(
|
||||
() => ({ id, actionType: 'ADD_LAYER_IMAGE', context: { id } }),
|
||||
[id]
|
||||
);
|
||||
|
@ -97,12 +97,12 @@ type EntityStateAndAdapter =
|
||||
state: CanvasInpaintMaskState;
|
||||
adapter: CanvasMaskAdapter;
|
||||
}
|
||||
| {
|
||||
id: string;
|
||||
type: CanvasControlAdapterState['type'];
|
||||
state: CanvasControlAdapterState;
|
||||
adapter: CanvasControlAdapter;
|
||||
}
|
||||
// | {
|
||||
// id: string;
|
||||
// type: CanvasControlAdapterState['type'];
|
||||
// state: CanvasControlAdapterState;
|
||||
// adapter: CanvasControlAdapter;
|
||||
// }
|
||||
| {
|
||||
id: string;
|
||||
type: CanvasRegionalGuidanceState['type'];
|
||||
|
@ -411,9 +411,9 @@ export const {
|
||||
bboxSizeOptimized,
|
||||
// layers
|
||||
layerAdded,
|
||||
layerAddedFromImage,
|
||||
layerRecalled,
|
||||
layerOpacityChanged,
|
||||
layerImageAdded,
|
||||
layerAllDeleted,
|
||||
layerImageCacheChanged,
|
||||
// IP Adapters
|
||||
|
@ -4,8 +4,8 @@ import { merge } from 'lodash-es';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
import type { CanvasLayerState, CanvasV2State, ImageObjectAddedArg } from './types';
|
||||
import { imageDTOToImageObject, imageDTOToImageWithDims } from './types';
|
||||
import type { CanvasImageState, CanvasLayerState, CanvasV2State } from './types';
|
||||
import { imageDTOToImageWithDims } from './types';
|
||||
|
||||
export const selectLayer = (state: CanvasV2State, id: string) => state.layers.entities.find((layer) => layer.id === id);
|
||||
export const selectLayerOrThrow = (state: CanvasV2State, id: string) => {
|
||||
@ -25,6 +25,7 @@ export const layersReducers = {
|
||||
objects: [],
|
||||
opacity: 1,
|
||||
position: { x: 0, y: 0 },
|
||||
imageCache: null,
|
||||
};
|
||||
merge(layer, action.payload.overrides);
|
||||
state.layers.entities.push(layer);
|
||||
@ -41,6 +42,26 @@ export const layersReducers = {
|
||||
state.selectedEntityIdentifier = { type: 'layer', id: data.id };
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
layerAddedFromImage: {
|
||||
reducer: (state, action: PayloadAction<{ id: string; imageObject: CanvasImageState }>) => {
|
||||
const { id, imageObject } = action.payload;
|
||||
const layer: CanvasLayerState = {
|
||||
id,
|
||||
type: 'layer',
|
||||
isEnabled: true,
|
||||
objects: [imageObject],
|
||||
opacity: 1,
|
||||
position: { x: 0, y: 0 },
|
||||
imageCache: null,
|
||||
};
|
||||
state.layers.entities.push(layer);
|
||||
state.selectedEntityIdentifier = { type: 'layer', id };
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
prepare: (payload: { imageObject: CanvasImageState }) => ({
|
||||
payload: { ...payload, id: getPrefixedId('layer') },
|
||||
}),
|
||||
},
|
||||
layerAllDeleted: (state) => {
|
||||
state.layers.entities = [];
|
||||
state.layers.imageCache = null;
|
||||
@ -54,23 +75,6 @@ export const layersReducers = {
|
||||
layer.opacity = opacity;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
layerImageAdded: (
|
||||
state,
|
||||
action: PayloadAction<ImageObjectAddedArg & { objectId: string; pos?: { x: number; y: number } }>
|
||||
) => {
|
||||
const { id, imageDTO, pos } = action.payload;
|
||||
const layer = selectLayer(state, id);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
const imageObject = imageDTOToImageObject(imageDTO);
|
||||
if (pos) {
|
||||
imageObject.x = pos.x;
|
||||
imageObject.y = pos.y;
|
||||
}
|
||||
layer.objects.push(imageObject);
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
layerImageCacheChanged: (state, action: PayloadAction<{ imageDTO: ImageDTO | null }>) => {
|
||||
const { imageDTO } = action.payload;
|
||||
state.layers.imageCache = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
|
||||
|
@ -44,11 +44,8 @@ export type RGIPAdapterImageDropData = BaseDropData & {
|
||||
};
|
||||
};
|
||||
|
||||
export type LayerImageDropData = BaseDropData & {
|
||||
actionType: 'ADD_LAYER_IMAGE';
|
||||
context: {
|
||||
id: string;
|
||||
};
|
||||
export type AddLayerFromImageDropData = BaseDropData & {
|
||||
actionType: 'ADD_LAYER_FROM_IMAGE';
|
||||
};
|
||||
|
||||
type UpscaleInitialImageDropData = BaseDropData & {
|
||||
@ -94,7 +91,7 @@ export type TypesafeDroppableData =
|
||||
| RGIPAdapterImageDropData
|
||||
| SelectForCompareDropData
|
||||
| UpscaleInitialImageDropData
|
||||
| LayerImageDropData;
|
||||
| AddLayerFromImageDropData;
|
||||
|
||||
type BaseDragData = {
|
||||
id: string;
|
||||
|
@ -21,7 +21,7 @@ export const isValidDrop = (overData?: TypesafeDroppableData | null, activeData?
|
||||
return payloadType === 'IMAGE_DTO';
|
||||
case 'SET_RG_IP_ADAPTER_IMAGE':
|
||||
return payloadType === 'IMAGE_DTO';
|
||||
case 'ADD_LAYER_IMAGE':
|
||||
case 'ADD_LAYER_FROM_IMAGE':
|
||||
return payloadType === 'IMAGE_DTO';
|
||||
case 'SET_UPSCALE_INITIAL_IMAGE':
|
||||
return payloadType === 'IMAGE_DTO';
|
||||
|
@ -10,8 +10,12 @@ const TextToImageTab = () => {
|
||||
return (
|
||||
<Box layerStyle="first" position="relative" w="full" h="full" p={2} borderRadius="base">
|
||||
<ControlLayersEditor />
|
||||
{imageViewer.isOpen && <ImageViewer />}
|
||||
<ImageComparisonDroppable />
|
||||
{imageViewer.isOpen && (
|
||||
<>
|
||||
<ImageViewer />
|
||||
<ImageComparisonDroppable />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user