mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds Maintain Aspect Ratio
checkbox to ImageGallery
This commit is contained in:
parent
5cae8206f9
commit
38fd0668ba
@ -1,6 +1,8 @@
|
||||
.invokeai__checkbox {
|
||||
.chakra-checkbox__label {
|
||||
margin-top: 1px;
|
||||
color: var(--text-color-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.chakra-checkbox__control {
|
||||
|
@ -12,7 +12,6 @@
|
||||
.hoverable-image-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
Tooltip,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { RootState, useAppDispatch, useAppSelector } from '../../app/store';
|
||||
import { useAppDispatch, useAppSelector } from '../../app/store';
|
||||
import { setCurrentImage } from './gallerySlice';
|
||||
import { FaCheck, FaTrashAlt } from 'react-icons/fa';
|
||||
import DeleteImageModal from './DeleteImageModal';
|
||||
@ -40,7 +40,7 @@ const memoEqualityCheck = (
|
||||
*/
|
||||
const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { activeTabName } = useAppSelector(hoverableImageSelector);
|
||||
const { activeTabName, galleryImageObjectFit } = useAppSelector(hoverableImageSelector);
|
||||
|
||||
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||
|
||||
@ -148,7 +148,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
>
|
||||
<Image
|
||||
className="hoverable-image-image"
|
||||
objectFit="cover"
|
||||
objectFit={galleryImageObjectFit}
|
||||
rounded={'md'}
|
||||
src={url}
|
||||
loading={'lazy'}
|
||||
|
@ -62,10 +62,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
.image-gallery-size-popover {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, max-content);
|
||||
column-gap: 0.5rem;
|
||||
.image-gallery-settings-popover {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 0.5rem;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
column-gap: 0.5rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Button } from '@chakra-ui/button';
|
||||
import { NumberSize, Resizable, Size } from 're-resizable';
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { MdClear, MdPhotoLibrary } from 'react-icons/md';
|
||||
import { BsPinAngleFill } from 'react-icons/bs';
|
||||
@ -12,6 +12,7 @@ import {
|
||||
selectNextImage,
|
||||
selectPrevImage,
|
||||
setGalleryImageMinimumWidth,
|
||||
setGalleryImageObjectFit,
|
||||
setGalleryScrollPosition,
|
||||
setShouldPinGallery,
|
||||
} from './gallerySlice';
|
||||
@ -25,6 +26,7 @@ import { FaWrench } from 'react-icons/fa';
|
||||
import IAIPopover from '../../common/components/IAIPopover';
|
||||
import IAISlider from '../../common/components/IAISlider';
|
||||
import { BiReset } from 'react-icons/bi';
|
||||
import IAICheckbox from '../../common/components/IAICheckbox';
|
||||
|
||||
export default function ImageGallery() {
|
||||
const dispatch = useAppDispatch();
|
||||
@ -40,6 +42,7 @@ export default function ImageGallery() {
|
||||
galleryImageMinimumWidth,
|
||||
galleryGridTemplateColumns,
|
||||
activeTabName,
|
||||
galleryImageObjectFit,
|
||||
} = useAppSelector(imageGallerySelector);
|
||||
|
||||
const [gallerySize, setGallerySize] = useState<Size>({
|
||||
@ -310,6 +313,7 @@ export default function ImageGallery() {
|
||||
<IAIPopover
|
||||
trigger="click"
|
||||
hasArrow={activeTabName === 'inpainting' ? false : true}
|
||||
// styleClass="image-gallery-settings-popover"
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
size={'sm'}
|
||||
@ -319,29 +323,47 @@ export default function ImageGallery() {
|
||||
cursor={'pointer'}
|
||||
/>
|
||||
}
|
||||
styleClass="image-gallery-size-popover"
|
||||
>
|
||||
<IAISlider
|
||||
value={galleryImageMinimumWidth}
|
||||
onChange={handleChangeGalleryImageMinimumWidth}
|
||||
min={32}
|
||||
max={256}
|
||||
width={100}
|
||||
label={'Image Size'}
|
||||
formLabelProps={{ style: { fontSize: '0.9rem' } }}
|
||||
sliderThumbTooltipProps={{
|
||||
label: `${galleryImageMinimumWidth}px`,
|
||||
}}
|
||||
/>
|
||||
<IAIIconButton
|
||||
size={'sm'}
|
||||
aria-label={'Reset'}
|
||||
tooltip={'Reset Size'}
|
||||
onClick={() => dispatch(setGalleryImageMinimumWidth(64))}
|
||||
icon={<BiReset />}
|
||||
data-selected={shouldPinGallery}
|
||||
styleClass="image-gallery-icon-btn"
|
||||
/>
|
||||
<div className="image-gallery-settings-popover">
|
||||
<div>
|
||||
<IAISlider
|
||||
value={galleryImageMinimumWidth}
|
||||
onChange={handleChangeGalleryImageMinimumWidth}
|
||||
min={32}
|
||||
max={256}
|
||||
width={100}
|
||||
label={'Image Size'}
|
||||
formLabelProps={{ style: { fontSize: '0.9rem' } }}
|
||||
sliderThumbTooltipProps={{
|
||||
label: `${galleryImageMinimumWidth}px`,
|
||||
}}
|
||||
/>
|
||||
<IAIIconButton
|
||||
size={'sm'}
|
||||
aria-label={'Reset'}
|
||||
tooltip={'Reset Size'}
|
||||
onClick={() => dispatch(setGalleryImageMinimumWidth(64))}
|
||||
icon={<BiReset />}
|
||||
data-selected={shouldPinGallery}
|
||||
styleClass="image-gallery-icon-btn"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<IAICheckbox
|
||||
label="Maintain Aspect Ratio"
|
||||
isChecked={galleryImageObjectFit === 'contain'}
|
||||
onChange={() =>
|
||||
dispatch(
|
||||
setGalleryImageObjectFit(
|
||||
galleryImageObjectFit === 'contain'
|
||||
? 'cover'
|
||||
: 'contain'
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</IAIPopover>
|
||||
|
||||
<IAIIconButton
|
||||
|
@ -3,6 +3,8 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import _, { clamp } from 'lodash';
|
||||
import * as InvokeAI from '../../app/invokeai';
|
||||
|
||||
type GalleryImageObjectFitType = 'contain' | 'cover';
|
||||
|
||||
export interface GalleryState {
|
||||
currentImage?: InvokeAI.Image;
|
||||
currentImageUuid: string;
|
||||
@ -15,6 +17,7 @@ export interface GalleryState {
|
||||
shouldShowGallery: boolean;
|
||||
galleryScrollPosition: number;
|
||||
galleryImageMinimumWidth: number;
|
||||
galleryImageObjectFit: GalleryImageObjectFitType;
|
||||
}
|
||||
|
||||
const initialState: GalleryState = {
|
||||
@ -25,6 +28,7 @@ const initialState: GalleryState = {
|
||||
shouldShowGallery: true,
|
||||
galleryScrollPosition: 0,
|
||||
galleryImageMinimumWidth: 64,
|
||||
galleryImageObjectFit: 'contain',
|
||||
};
|
||||
|
||||
export const gallerySlice = createSlice({
|
||||
@ -171,6 +175,12 @@ export const gallerySlice = createSlice({
|
||||
setGalleryImageMinimumWidth: (state, action: PayloadAction<number>) => {
|
||||
state.galleryImageMinimumWidth = action.payload;
|
||||
},
|
||||
setGalleryImageObjectFit: (
|
||||
state,
|
||||
action: PayloadAction<GalleryImageObjectFitType>
|
||||
) => {
|
||||
state.galleryImageObjectFit = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -187,6 +197,7 @@ export const {
|
||||
setShouldShowGallery,
|
||||
setGalleryScrollPosition,
|
||||
setGalleryImageMinimumWidth,
|
||||
setGalleryImageObjectFit,
|
||||
} = gallerySlice.actions;
|
||||
|
||||
export default gallerySlice.reducer;
|
||||
|
@ -15,6 +15,7 @@ export const imageGallerySelector = createSelector(
|
||||
shouldShowGallery,
|
||||
galleryScrollPosition,
|
||||
galleryImageMinimumWidth,
|
||||
galleryImageObjectFit,
|
||||
} = gallery;
|
||||
|
||||
const { activeTab } = options;
|
||||
@ -27,6 +28,7 @@ export const imageGallerySelector = createSelector(
|
||||
shouldShowGallery,
|
||||
galleryScrollPosition,
|
||||
galleryImageMinimumWidth,
|
||||
galleryImageObjectFit,
|
||||
galleryGridTemplateColumns: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`,
|
||||
activeTabName: tabMap[activeTab],
|
||||
};
|
||||
@ -34,9 +36,10 @@ export const imageGallerySelector = createSelector(
|
||||
);
|
||||
|
||||
export const hoverableImageSelector = createSelector(
|
||||
(state: RootState) => state.options,
|
||||
(options: OptionsState) => {
|
||||
[(state: RootState) => state.options, (state: RootState) => state.gallery],
|
||||
(options: OptionsState, gallery: GalleryState) => {
|
||||
return {
|
||||
galleryImageObjectFit: gallery.galleryImageObjectFit,
|
||||
activeTabName: tabMap[options.activeTab],
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user