mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds next/prev image buttons/hotkeys
This commit is contained in:
parent
0c1c220bb9
commit
58d73f5cae
@ -67,6 +67,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.current-image-next-prev-buttons {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: calc(100% - 2rem);
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
z-index: 1;
|
||||||
|
height: calc($app-metadata-height - 1rem);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-prev-button-trigger-area {
|
||||||
|
width: 7rem;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
&.prev-button-trigger-area {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.next-button-trigger-area {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-prev-button {
|
||||||
|
font-size: 5rem;
|
||||||
|
fill: var(--text-color-secondary);
|
||||||
|
filter: drop-shadow(0 0 1rem var(--text-color-secondary));
|
||||||
|
opacity: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
.current-image-metadata-viewer {
|
.current-image-metadata-viewer {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
import { Image } from '@chakra-ui/react';
|
import { IconButton, Image } from '@chakra-ui/react';
|
||||||
import { useAppSelector } from '../../app/store';
|
import { useAppDispatch, useAppSelector } from '../../app/store';
|
||||||
import { RootState } from '../../app/store';
|
import { RootState } from '../../app/store';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ImageMetadataViewer from './ImageMetadataViewer';
|
import ImageMetadataViewer from './ImageMetadataViewer';
|
||||||
import CurrentImageButtons from './CurrentImageButtons';
|
import CurrentImageButtons from './CurrentImageButtons';
|
||||||
import { MdPhoto } from 'react-icons/md';
|
import { MdPhoto } from 'react-icons/md';
|
||||||
|
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
|
||||||
|
import { selectNextImage, selectPrevImage } from './gallerySlice';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the current image if there is one, plus associated actions.
|
* Displays the current image if there is one, plus associated actions.
|
||||||
*/
|
*/
|
||||||
const CurrentImageDisplay = () => {
|
const CurrentImageDisplay = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
const { currentImage, intermediateImage } = useAppSelector(
|
const { currentImage, intermediateImage } = useAppSelector(
|
||||||
(state: RootState) => state.gallery
|
(state: RootState) => state.gallery
|
||||||
);
|
);
|
||||||
@ -19,6 +25,22 @@ const CurrentImageDisplay = () => {
|
|||||||
|
|
||||||
const imageToDisplay = intermediateImage || currentImage;
|
const imageToDisplay = intermediateImage || currentImage;
|
||||||
|
|
||||||
|
const handleCurrentImagePreviewMouseOver = () => {
|
||||||
|
setShouldShowNextPrevButtons(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCurrentImagePreviewMouseOut = () => {
|
||||||
|
setShouldShowNextPrevButtons(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickPrevButton = () => {
|
||||||
|
dispatch(selectPrevImage());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickNextButton = () => {
|
||||||
|
dispatch(selectNextImage());
|
||||||
|
};
|
||||||
|
|
||||||
return imageToDisplay ? (
|
return imageToDisplay ? (
|
||||||
<div className="current-image-display">
|
<div className="current-image-display">
|
||||||
<div className="current-image-tools">
|
<div className="current-image-tools">
|
||||||
@ -40,6 +62,38 @@ const CurrentImageDisplay = () => {
|
|||||||
<ImageMetadataViewer image={imageToDisplay} />
|
<ImageMetadataViewer image={imageToDisplay} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!shouldShowImageDetails && (
|
||||||
|
<div className="current-image-next-prev-buttons">
|
||||||
|
<div
|
||||||
|
className="next-prev-button-trigger-area prev-button-trigger-area"
|
||||||
|
onMouseOver={handleCurrentImagePreviewMouseOver}
|
||||||
|
onMouseOut={handleCurrentImagePreviewMouseOut}
|
||||||
|
>
|
||||||
|
{shouldShowNextPrevButtons && (
|
||||||
|
<IconButton
|
||||||
|
aria-label="Previous image"
|
||||||
|
icon={<FaAngleLeft className="next-prev-button" />}
|
||||||
|
variant="unstyled"
|
||||||
|
onClick={handleClickPrevButton}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="next-prev-button-trigger-area next-button-trigger-area"
|
||||||
|
onMouseOver={handleCurrentImagePreviewMouseOver}
|
||||||
|
onMouseOut={handleCurrentImagePreviewMouseOut}
|
||||||
|
>
|
||||||
|
{shouldShowNextPrevButtons && (
|
||||||
|
<IconButton
|
||||||
|
aria-label="Next image"
|
||||||
|
icon={<FaAngleRight className="next-prev-button" />}
|
||||||
|
variant="unstyled"
|
||||||
|
onClick={handleClickNextButton}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Button } from '@chakra-ui/react';
|
import { Button } from '@chakra-ui/react';
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { MdPhotoLibrary } from 'react-icons/md';
|
import { MdPhotoLibrary } from 'react-icons/md';
|
||||||
import { requestImages } from '../../app/socketio/actions';
|
import { requestImages } from '../../app/socketio/actions';
|
||||||
import { RootState, useAppDispatch } from '../../app/store';
|
import { RootState, useAppDispatch } from '../../app/store';
|
||||||
import { useAppSelector } from '../../app/store';
|
import { useAppSelector } from '../../app/store';
|
||||||
|
import { selectNextImage, selectPrevImage } from './gallerySlice';
|
||||||
import HoverableImage from './HoverableImage';
|
import HoverableImage from './HoverableImage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,6 +27,22 @@ const ImageGallery = () => {
|
|||||||
dispatch(requestImages());
|
dispatch(requestImages());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
'left',
|
||||||
|
() => {
|
||||||
|
dispatch(selectPrevImage());
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
'right',
|
||||||
|
() => {
|
||||||
|
dispatch(selectNextImage());
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="image-gallery-container">
|
<div className="image-gallery-container">
|
||||||
{images.length ? (
|
{images.length ? (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { clamp } from 'lodash';
|
import _, { clamp } from 'lodash';
|
||||||
import * as InvokeAI from '../../app/invokeai';
|
import * as InvokeAI from '../../app/invokeai';
|
||||||
|
|
||||||
export interface GalleryState {
|
export interface GalleryState {
|
||||||
@ -85,6 +85,32 @@ export const gallerySlice = createSlice({
|
|||||||
clearIntermediateImage: (state) => {
|
clearIntermediateImage: (state) => {
|
||||||
state.intermediateImage = undefined;
|
state.intermediateImage = undefined;
|
||||||
},
|
},
|
||||||
|
selectNextImage: (state) => {
|
||||||
|
const { images, currentImage } = state;
|
||||||
|
if (currentImage) {
|
||||||
|
const currentImageIndex = images.findIndex(
|
||||||
|
(i) => i.uuid === currentImage.uuid
|
||||||
|
);
|
||||||
|
if (_.inRange(currentImageIndex, 0, images.length)) {
|
||||||
|
const newCurrentImage = images[currentImageIndex + 1];
|
||||||
|
state.currentImage = newCurrentImage;
|
||||||
|
state.currentImageUuid = newCurrentImage.uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectPrevImage: (state) => {
|
||||||
|
const { images, currentImage } = state;
|
||||||
|
if (currentImage) {
|
||||||
|
const currentImageIndex = images.findIndex(
|
||||||
|
(i) => i.uuid === currentImage.uuid
|
||||||
|
);
|
||||||
|
if (_.inRange(currentImageIndex, 1, images.length + 1)) {
|
||||||
|
const newCurrentImage = images[currentImageIndex - 1];
|
||||||
|
state.currentImage = newCurrentImage;
|
||||||
|
state.currentImageUuid = newCurrentImage.uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
addGalleryImages: (
|
addGalleryImages: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<{
|
action: PayloadAction<{
|
||||||
@ -122,6 +148,8 @@ export const {
|
|||||||
setCurrentImage,
|
setCurrentImage,
|
||||||
addGalleryImages,
|
addGalleryImages,
|
||||||
setIntermediateImage,
|
setIntermediateImage,
|
||||||
|
selectNextImage,
|
||||||
|
selectPrevImage,
|
||||||
} = gallerySlice.actions;
|
} = gallerySlice.actions;
|
||||||
|
|
||||||
export default gallerySlice.reducer;
|
export default gallerySlice.reducer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user