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:
psychedelicious 2023-09-27 17:12:57 +10:00
parent a35087ee6e
commit fbccce7573
3 changed files with 74 additions and 71 deletions

View File

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

View File

@ -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}
/> />

View File

@ -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) {