Adds gallery drag and drop to img2img/canvas

This commit is contained in:
psychedelicious 2022-11-25 10:48:38 +11:00 committed by blessedcoolant
parent 3aebe754fa
commit 916e795c26
5 changed files with 77 additions and 10 deletions

View File

@ -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] =

View File

@ -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"

View 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;

View File

@ -95,3 +95,5 @@ export const hoverableImageSelector = createSelector(
},
}
);
export const gallerySelector = (state: RootState) => state.gallery;

View File

@ -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">