mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add fill mode for slider comparison
This commit is contained in:
parent
e4ce188500
commit
3501636018
@ -384,7 +384,9 @@
|
||||
"selectAnImageToCompare": "Select an Image to Compare",
|
||||
"slider": "Slider",
|
||||
"sideBySide": "Side-by-Side",
|
||||
"swapImages": "Swap"
|
||||
"swapImages": "Swap Images",
|
||||
"compareOptions": "Comparison Options",
|
||||
"sliderFitLabel": "Stretch second image to fit"
|
||||
},
|
||||
"hotkeys": {
|
||||
"searchHotkeys": "Search Hotkeys",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Box, Flex, Icon, Image, Text } from '@invoke-ai/ui-library';
|
||||
import { useMeasure } from '@reactuses/core';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import type { Dimensions } from 'features/canvas/store/canvasTypes';
|
||||
import { STAGE_BG_DATAURL } from 'features/controlLayers/util/renderers';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
@ -29,6 +30,7 @@ type Props = {
|
||||
|
||||
export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const sliderFit = useAppSelector((s) => s.gallery.sliderFit);
|
||||
// How far the handle is from the left - this will be a CSS calculation that takes into account the handle width
|
||||
const [left, setLeft] = useState(HANDLE_LEFT_INITIAL_PX);
|
||||
// How wide the first image is
|
||||
@ -165,11 +167,11 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) =
|
||||
<Image
|
||||
src={secondImage.image_url}
|
||||
fallbackSrc={secondImage.thumbnail_url}
|
||||
w={secondImage.width}
|
||||
h={secondImage.height}
|
||||
w={sliderFit === 'fill' ? fittedSize.width : secondImage.width}
|
||||
h={sliderFit === 'fill' ? fittedSize.height : secondImage.height}
|
||||
maxW={fittedSize.width}
|
||||
maxH={fittedSize.height}
|
||||
objectFit="contain"
|
||||
objectFit={sliderFit}
|
||||
objectPosition="top left"
|
||||
/>
|
||||
<Text
|
||||
|
@ -1,13 +1,28 @@
|
||||
import { Button, ButtonGroup } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Popover,
|
||||
PopoverBody,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Switch,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { comparedImagesSwapped, comparisonModeChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { comparedImagesSwapped, comparisonModeChanged, sliderFitChanged } from 'features/gallery/store/gallerySlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiGearBold } from 'react-icons/pi';
|
||||
|
||||
export const ImageComparisonToolbarButtons = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const comparisonMode = useAppSelector((s) => s.gallery.comparisonMode);
|
||||
const sliderFit = useAppSelector((s) => s.gallery.sliderFit);
|
||||
const setComparisonModeSlider = useCallback(() => {
|
||||
dispatch(comparisonModeChanged('slider'));
|
||||
}, [dispatch]);
|
||||
@ -17,6 +32,12 @@ export const ImageComparisonToolbarButtons = memo(() => {
|
||||
const swapImages = useCallback(() => {
|
||||
dispatch(comparedImagesSwapped());
|
||||
}, [dispatch]);
|
||||
const onSliderFitChanged = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(sliderFitChanged(e.target.checked ? 'fill' : 'contain'));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -31,6 +52,30 @@ export const ImageComparisonToolbarButtons = memo(() => {
|
||||
{t('gallery.sideBySide')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Popover isLazy>
|
||||
<PopoverTrigger>
|
||||
<IconButton
|
||||
aria-label={t('gallery.compareOptions')}
|
||||
tooltip={t('gallery.compareOptions')}
|
||||
icon={<PiGearBold />}
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverBody>
|
||||
<Flex direction="column" gap={2}>
|
||||
<FormControl>
|
||||
<FormLabel>{t('gallery.sliderFitLabel')}</FormLabel>
|
||||
<Switch
|
||||
isChecked={sliderFit === 'fill'}
|
||||
isDisabled={comparisonMode !== 'slider'}
|
||||
onChange={onSliderFitChanged}
|
||||
/>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<Button onClick={swapImages}>{t('gallery.swapImages')}</Button>
|
||||
</>
|
||||
);
|
||||
|
@ -24,6 +24,7 @@ const initialGalleryState: GalleryState = {
|
||||
viewerMode: 'view',
|
||||
imageToCompare: null,
|
||||
comparisonMode: 'slider',
|
||||
sliderFit: 'fill',
|
||||
};
|
||||
|
||||
export const gallerySlice = createSlice({
|
||||
@ -97,6 +98,9 @@ export const gallerySlice = createSlice({
|
||||
state.imageToCompare = oldSelection[0] ?? null;
|
||||
}
|
||||
},
|
||||
sliderFitChanged: (state, action: PayloadAction<'contain' | 'fill'>) => {
|
||||
state.sliderFit = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addMatcher(isAnyBoardDeleted, (state, action) => {
|
||||
@ -138,6 +142,7 @@ export const {
|
||||
imageToCompareChanged,
|
||||
comparisonModeChanged,
|
||||
comparedImagesSwapped,
|
||||
sliderFitChanged,
|
||||
} = gallerySlice.actions;
|
||||
|
||||
const isAnyBoardDeleted = isAnyOf(
|
||||
|
@ -24,5 +24,6 @@ export type GalleryState = {
|
||||
alwaysShowImageSizeBadge: boolean;
|
||||
imageToCompare: ImageDTO | null;
|
||||
comparisonMode: ComparisonMode;
|
||||
sliderFit: 'contain' | 'fill';
|
||||
viewerMode: ViewerMode;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user