mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): staging area toolbar enhancements
- Current image number & total are displayed - Left/right wrap around instead of stopping on first/last image - Disable the left/right/number buttons when showing base layer - improved translations
This commit is contained in:
parent
a35087ee6e
commit
fbccce7573
@ -1444,6 +1444,8 @@
|
|||||||
"showCanvasDebugInfo": "Show Additional Canvas Info",
|
"showCanvasDebugInfo": "Show Additional Canvas Info",
|
||||||
"showGrid": "Show Grid",
|
"showGrid": "Show Grid",
|
||||||
"showHide": "Show/Hide",
|
"showHide": "Show/Hide",
|
||||||
|
"showResultsOn": "Show Results (On)",
|
||||||
|
"showResultsOff": "Show Results (Off)",
|
||||||
"showIntermediates": "Show Intermediates",
|
"showIntermediates": "Show Intermediates",
|
||||||
"snapToGrid": "Snap to Grid",
|
"snapToGrid": "Snap to Grid",
|
||||||
"undo": "Undo"
|
"undo": "Undo"
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
|
|
||||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -23,8 +24,8 @@ import {
|
|||||||
FaCheck,
|
FaCheck,
|
||||||
FaEye,
|
FaEye,
|
||||||
FaEyeSlash,
|
FaEyeSlash,
|
||||||
FaPlus,
|
|
||||||
FaSave,
|
FaSave,
|
||||||
|
FaTimes,
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||||
import { stagingAreaImageSaved } from '../store/actions';
|
import { stagingAreaImageSaved } from '../store/actions';
|
||||||
@ -41,10 +42,10 @@ const selector = createSelector(
|
|||||||
} = canvas;
|
} = canvas;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
currentIndex: selectedImageIndex,
|
||||||
|
total: images.length,
|
||||||
currentStagingAreaImage:
|
currentStagingAreaImage:
|
||||||
images.length > 0 ? images[selectedImageIndex] : undefined,
|
images.length > 0 ? images[selectedImageIndex] : undefined,
|
||||||
isOnFirstImage: selectedImageIndex === 0,
|
|
||||||
isOnLastImage: selectedImageIndex === images.length - 1,
|
|
||||||
shouldShowStagingImage,
|
shouldShowStagingImage,
|
||||||
shouldShowStagingOutline,
|
shouldShowStagingOutline,
|
||||||
};
|
};
|
||||||
@ -55,10 +56,10 @@ const selector = createSelector(
|
|||||||
const IAICanvasStagingAreaToolbar = () => {
|
const IAICanvasStagingAreaToolbar = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const {
|
||||||
isOnFirstImage,
|
|
||||||
isOnLastImage,
|
|
||||||
currentStagingAreaImage,
|
currentStagingAreaImage,
|
||||||
shouldShowStagingImage,
|
shouldShowStagingImage,
|
||||||
|
currentIndex,
|
||||||
|
total,
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -71,39 +72,6 @@ const IAICanvasStagingAreaToolbar = () => {
|
|||||||
dispatch(setShouldShowStagingOutline(false));
|
dispatch(setShouldShowStagingOutline(false));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
['left'],
|
|
||||||
() => {
|
|
||||||
handlePrevImage();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: () => true,
|
|
||||||
preventDefault: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
['right'],
|
|
||||||
() => {
|
|
||||||
handleNextImage();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: () => true,
|
|
||||||
preventDefault: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
['enter'],
|
|
||||||
() => {
|
|
||||||
handleAccept();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: () => true,
|
|
||||||
preventDefault: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlePrevImage = useCallback(
|
const handlePrevImage = useCallback(
|
||||||
() => dispatch(prevStagingAreaImage()),
|
() => dispatch(prevStagingAreaImage()),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
@ -119,10 +87,45 @@ const IAICanvasStagingAreaToolbar = () => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useHotkeys(['left'], handlePrevImage, {
|
||||||
|
enabled: () => true,
|
||||||
|
preventDefault: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
useHotkeys(['right'], handleNextImage, {
|
||||||
|
enabled: () => true,
|
||||||
|
preventDefault: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
useHotkeys(['enter'], () => handleAccept, {
|
||||||
|
enabled: () => true,
|
||||||
|
preventDefault: true,
|
||||||
|
});
|
||||||
|
|
||||||
const { data: imageDTO } = useGetImageDTOQuery(
|
const { data: imageDTO } = useGetImageDTOQuery(
|
||||||
currentStagingAreaImage?.imageName ?? skipToken
|
currentStagingAreaImage?.imageName ?? skipToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleToggleShouldShowStagingImage = useCallback(() => {
|
||||||
|
dispatch(setShouldShowStagingImage(!shouldShowStagingImage));
|
||||||
|
}, [dispatch, shouldShowStagingImage]);
|
||||||
|
|
||||||
|
const handleSaveToGallery = useCallback(() => {
|
||||||
|
if (!imageDTO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
stagingAreaImageSaved({
|
||||||
|
imageDTO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [dispatch, imageDTO]);
|
||||||
|
|
||||||
|
const handleDiscardStagingArea = useCallback(() => {
|
||||||
|
dispatch(discardStagedImages());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
if (!currentStagingAreaImage) {
|
if (!currentStagingAreaImage) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -134,8 +137,8 @@ const IAICanvasStagingAreaToolbar = () => {
|
|||||||
w="100%"
|
w="100%"
|
||||||
align="center"
|
align="center"
|
||||||
justify="center"
|
justify="center"
|
||||||
onMouseOver={handleMouseOver}
|
onMouseEnter={handleMouseOver}
|
||||||
onMouseOut={handleMouseOut}
|
onMouseLeave={handleMouseOut}
|
||||||
>
|
>
|
||||||
<ButtonGroup isAttached borderRadius="base" shadow="dark-lg">
|
<ButtonGroup isAttached borderRadius="base" shadow="dark-lg">
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
@ -144,15 +147,20 @@ const IAICanvasStagingAreaToolbar = () => {
|
|||||||
icon={<FaArrowLeft />}
|
icon={<FaArrowLeft />}
|
||||||
onClick={handlePrevImage}
|
onClick={handlePrevImage}
|
||||||
colorScheme="accent"
|
colorScheme="accent"
|
||||||
isDisabled={isOnFirstImage}
|
isDisabled={!shouldShowStagingImage}
|
||||||
/>
|
/>
|
||||||
|
<IAIButton
|
||||||
|
colorScheme="accent"
|
||||||
|
pointerEvents="none"
|
||||||
|
isDisabled={!shouldShowStagingImage}
|
||||||
|
>{`${currentIndex + 1}/${total}`}</IAIButton>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
tooltip={`${t('unifiedCanvas.next')} (Right)`}
|
tooltip={`${t('unifiedCanvas.next')} (Right)`}
|
||||||
aria-label={`${t('unifiedCanvas.next')} (Right)`}
|
aria-label={`${t('unifiedCanvas.next')} (Right)`}
|
||||||
icon={<FaArrowRight />}
|
icon={<FaArrowRight />}
|
||||||
onClick={handleNextImage}
|
onClick={handleNextImage}
|
||||||
colorScheme="accent"
|
colorScheme="accent"
|
||||||
isDisabled={isOnLastImage}
|
isDisabled={!shouldShowStagingImage}
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
tooltip={`${t('unifiedCanvas.accept')} (Enter)`}
|
tooltip={`${t('unifiedCanvas.accept')} (Enter)`}
|
||||||
@ -162,13 +170,19 @@ const IAICanvasStagingAreaToolbar = () => {
|
|||||||
colorScheme="accent"
|
colorScheme="accent"
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
tooltip={t('unifiedCanvas.showHide')}
|
tooltip={
|
||||||
aria-label={t('unifiedCanvas.showHide')}
|
shouldShowStagingImage
|
||||||
|
? t('unifiedCanvas.showResultsOn')
|
||||||
|
: t('unifiedCanvas.showResultsOff')
|
||||||
|
}
|
||||||
|
aria-label={
|
||||||
|
shouldShowStagingImage
|
||||||
|
? t('unifiedCanvas.showResultsOn')
|
||||||
|
: t('unifiedCanvas.showResultsOff')
|
||||||
|
}
|
||||||
data-alert={!shouldShowStagingImage}
|
data-alert={!shouldShowStagingImage}
|
||||||
icon={shouldShowStagingImage ? <FaEye /> : <FaEyeSlash />}
|
icon={shouldShowStagingImage ? <FaEye /> : <FaEyeSlash />}
|
||||||
onClick={() =>
|
onClick={handleToggleShouldShowStagingImage}
|
||||||
dispatch(setShouldShowStagingImage(!shouldShowStagingImage))
|
|
||||||
}
|
|
||||||
colorScheme="accent"
|
colorScheme="accent"
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
@ -176,24 +190,14 @@ const IAICanvasStagingAreaToolbar = () => {
|
|||||||
aria-label={t('unifiedCanvas.saveToGallery')}
|
aria-label={t('unifiedCanvas.saveToGallery')}
|
||||||
isDisabled={!imageDTO || !imageDTO.is_intermediate}
|
isDisabled={!imageDTO || !imageDTO.is_intermediate}
|
||||||
icon={<FaSave />}
|
icon={<FaSave />}
|
||||||
onClick={() => {
|
onClick={handleSaveToGallery}
|
||||||
if (!imageDTO) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
stagingAreaImageSaved({
|
|
||||||
imageDTO,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
colorScheme="accent"
|
colorScheme="accent"
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
tooltip={t('unifiedCanvas.discardAll')}
|
tooltip={t('unifiedCanvas.discardAll')}
|
||||||
aria-label={t('unifiedCanvas.discardAll')}
|
aria-label={t('unifiedCanvas.discardAll')}
|
||||||
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
|
icon={<FaTimes />}
|
||||||
onClick={() => dispatch(discardStagedImages())}
|
onClick={handleDiscardStagingArea}
|
||||||
colorScheme="error"
|
colorScheme="error"
|
||||||
fontSize={20}
|
fontSize={20}
|
||||||
/>
|
/>
|
||||||
|
@ -621,25 +621,22 @@ export const canvasSlice = createSlice({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = state.layerState.stagingArea.selectedImageIndex;
|
const nextIndex = state.layerState.stagingArea.selectedImageIndex + 1;
|
||||||
const length = state.layerState.stagingArea.images.length;
|
const lastIndex = state.layerState.stagingArea.images.length - 1;
|
||||||
|
|
||||||
state.layerState.stagingArea.selectedImageIndex = Math.min(
|
state.layerState.stagingArea.selectedImageIndex =
|
||||||
currentIndex + 1,
|
nextIndex > lastIndex ? 0 : nextIndex;
|
||||||
length - 1
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
prevStagingAreaImage: (state) => {
|
prevStagingAreaImage: (state) => {
|
||||||
if (!state.layerState.stagingArea.images.length) {
|
if (!state.layerState.stagingArea.images.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = state.layerState.stagingArea.selectedImageIndex;
|
const prevIndex = state.layerState.stagingArea.selectedImageIndex - 1;
|
||||||
|
const lastIndex = state.layerState.stagingArea.images.length - 1;
|
||||||
|
|
||||||
state.layerState.stagingArea.selectedImageIndex = Math.max(
|
state.layerState.stagingArea.selectedImageIndex =
|
||||||
currentIndex - 1,
|
prevIndex < 0 ? lastIndex : prevIndex;
|
||||||
0
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
commitStagingAreaImage: (state) => {
|
commitStagingAreaImage: (state) => {
|
||||||
if (!state.layerState.stagingArea.images.length) {
|
if (!state.layerState.stagingArea.images.length) {
|
||||||
|
Loading…
Reference in New Issue
Block a user