mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds gallery drag and drop to img2img/canvas
This commit is contained in:
parent
3aebe754fa
commit
916e795c26
@ -1,5 +1,5 @@
|
||||
import { IconButton, Image } from '@chakra-ui/react';
|
||||
import { useState } from 'react';
|
||||
import { DragEvent, useState } from 'react';
|
||||
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import {
|
||||
@ -12,13 +12,19 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
OptionsState,
|
||||
setInitialImage,
|
||||
setIsLightBoxOpen,
|
||||
} from 'features/options/store/optionsSlice';
|
||||
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
|
||||
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
|
||||
|
||||
export const imagesSelector = createSelector(
|
||||
[(state: RootState) => state.gallery, (state: RootState) => state.options],
|
||||
(gallery: GalleryState, options: OptionsState) => {
|
||||
[
|
||||
(state: RootState) => state.gallery,
|
||||
(state: RootState) => state.options,
|
||||
activeTabNameSelector,
|
||||
],
|
||||
(gallery: GalleryState, options: OptionsState, activeTabName) => {
|
||||
const { currentCategory, currentImage, intermediateImage } = gallery;
|
||||
const { shouldShowImageDetails } = options;
|
||||
|
||||
@ -32,6 +38,7 @@ export const imagesSelector = createSelector(
|
||||
const imagesLength = tempImages.length;
|
||||
|
||||
return {
|
||||
activeTabName,
|
||||
imageToDisplay: intermediateImage ? intermediateImage : currentImage,
|
||||
isIntermediate: Boolean(intermediateImage),
|
||||
viewerImageToDisplay: currentImage,
|
||||
@ -61,6 +68,7 @@ export default function CurrentImagePreview() {
|
||||
shouldShowImageDetails,
|
||||
imageToDisplay,
|
||||
isIntermediate,
|
||||
activeTabName,
|
||||
} = useAppSelector(imagesSelector);
|
||||
|
||||
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
|
||||
import DeleteImageModal from './DeleteImageModal';
|
||||
import { memo, useState } from 'react';
|
||||
import { DragEvent, memo, useState } from 'react';
|
||||
import {
|
||||
setActiveTab,
|
||||
setAllImageToImageParameters,
|
||||
@ -129,7 +129,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
};
|
||||
|
||||
const handleUseInitialImage = async () => {
|
||||
// check if the image exists before setting it as initial image
|
||||
if (metadata?.image?.init_image_path) {
|
||||
const response = await fetch(metadata.image.init_image_path);
|
||||
if (response.ok) {
|
||||
@ -155,6 +154,11 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
|
||||
const handleSelectImage = () => dispatch(setCurrentImage(image));
|
||||
|
||||
const handleDragStart = (e: DragEvent<HTMLDivElement>) => {
|
||||
e.dataTransfer.setData('invokeai/imageUuid', uuid);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
};
|
||||
|
||||
return (
|
||||
<ContextMenu.Root
|
||||
onOpenChange={(open: boolean) => {
|
||||
@ -169,6 +173,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
userSelect={'none'}
|
||||
draggable={true}
|
||||
onDragStart={handleDragStart}
|
||||
>
|
||||
<Image
|
||||
className="hoverable-image-image"
|
||||
|
28
frontend/src/features/gallery/hooks/useGetImageByUuid.ts
Normal file
28
frontend/src/features/gallery/hooks/useGetImageByUuid.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import { gallerySelector } from '../store/gallerySliceSelectors';
|
||||
|
||||
const selector = createSelector(gallerySelector, (gallery) => ({
|
||||
resultImages: gallery.categories.result.images,
|
||||
userImages: gallery.categories.user.images,
|
||||
}));
|
||||
|
||||
const useGetImageByUuid = () => {
|
||||
const { resultImages, userImages } = useAppSelector(selector);
|
||||
|
||||
return (uuid: string) => {
|
||||
const resultImagesResult = resultImages.find(
|
||||
(image) => image.uuid === uuid
|
||||
);
|
||||
if (resultImagesResult) {
|
||||
return resultImagesResult;
|
||||
}
|
||||
|
||||
const userImagesResult = userImages.find((image) => image.uuid === uuid);
|
||||
if (userImagesResult) {
|
||||
return userImagesResult;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default useGetImageByUuid;
|
@ -95,3 +95,5 @@ export const hoverableImageSelector = createSelector(
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export const gallerySelector = (state: RootState) => state.gallery;
|
||||
|
@ -1,16 +1,21 @@
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { ReactNode } from 'react';
|
||||
import { DragEvent, ReactNode } from 'react';
|
||||
import { VscSplitHorizontal } from 'react-icons/vsc';
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import ImageGallery from 'features/gallery/components/ImageGallery';
|
||||
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
|
||||
import {
|
||||
OptionsState,
|
||||
setInitialImage,
|
||||
setShowDualDisplay,
|
||||
} from 'features/options/store/optionsSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
setDoesCanvasNeedScaling,
|
||||
setInitialCanvasImage,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import _ from 'lodash';
|
||||
import useGetImageByUuid from 'features/gallery/hooks/useGetImageByUuid';
|
||||
|
||||
const workareaSelector = createSelector(
|
||||
[(state: RootState) => state.options, activeTabNameSelector],
|
||||
@ -21,6 +26,7 @@ const workareaSelector = createSelector(
|
||||
shouldPinOptionsPanel,
|
||||
isLightBoxOpen,
|
||||
shouldShowDualDisplayButton: ['inpainting'].includes(activeTabName),
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -39,14 +45,31 @@ type InvokeWorkareaProps = {
|
||||
const InvokeWorkarea = (props: InvokeWorkareaProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { optionsPanel, children, styleClass } = props;
|
||||
const { showDualDisplay, isLightBoxOpen, shouldShowDualDisplayButton } =
|
||||
useAppSelector(workareaSelector);
|
||||
const {
|
||||
activeTabName,
|
||||
showDualDisplay,
|
||||
isLightBoxOpen,
|
||||
shouldShowDualDisplayButton,
|
||||
} = useAppSelector(workareaSelector);
|
||||
|
||||
const getImageByUuid = useGetImageByUuid();
|
||||
|
||||
const handleDualDisplay = () => {
|
||||
dispatch(setShowDualDisplay(!showDualDisplay));
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
};
|
||||
|
||||
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
|
||||
const uuid = e.dataTransfer.getData('invokeai/imageUuid');
|
||||
const image = getImageByUuid(uuid);
|
||||
if (!image) return;
|
||||
if (activeTabName === 'img2img') {
|
||||
dispatch(setInitialImage(image));
|
||||
} else if (activeTabName === 'unifiedCanvas') {
|
||||
dispatch(setInitialCanvasImage(image));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
@ -55,7 +78,7 @@ const InvokeWorkarea = (props: InvokeWorkareaProps) => {
|
||||
>
|
||||
<div className="workarea-main">
|
||||
{optionsPanel}
|
||||
<div className="workarea-children-wrapper">
|
||||
<div className="workarea-children-wrapper" onDrop={handleDrop}>
|
||||
{children}
|
||||
{shouldShowDualDisplayButton && (
|
||||
<Tooltip label="Toggle Split View">
|
||||
|
Loading…
Reference in New Issue
Block a user