mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): staging area image visibility toggle
This commit is contained in:
parent
ec6361e5cb
commit
b823c31ec6
@ -1,6 +1,8 @@
|
|||||||
import { Button, ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
import { Button, ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
||||||
|
import { useStore } from '@nanostores/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
|
$shouldShowStagedImage,
|
||||||
stagingAreaImageAccepted,
|
stagingAreaImageAccepted,
|
||||||
stagingAreaImageDiscarded,
|
stagingAreaImageDiscarded,
|
||||||
stagingAreaNextImageSelected,
|
stagingAreaNextImageSelected,
|
||||||
@ -11,7 +13,16 @@ import type { CanvasV2State } from 'features/controlLayers/store/types';
|
|||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiArrowLeftBold, PiArrowRightBold, PiCheckBold, PiTrashSimpleBold, PiXBold } from 'react-icons/pi';
|
import {
|
||||||
|
PiArrowLeftBold,
|
||||||
|
PiArrowRightBold,
|
||||||
|
PiCheckBold,
|
||||||
|
PiEyeBold,
|
||||||
|
PiEyeSlashBold,
|
||||||
|
PiFloppyDiskBold,
|
||||||
|
PiTrashSimpleBold,
|
||||||
|
PiXBold,
|
||||||
|
} from 'react-icons/pi';
|
||||||
|
|
||||||
export const StagingAreaToolbar = memo(() => {
|
export const StagingAreaToolbar = memo(() => {
|
||||||
const stagingArea = useAppSelector((s) => s.canvasV2.stagingArea);
|
const stagingArea = useAppSelector((s) => s.canvasV2.stagingArea);
|
||||||
@ -31,6 +42,7 @@ type Props = {
|
|||||||
|
|
||||||
export const StagingAreaToolbarContent = memo(({ stagingArea }: Props) => {
|
export const StagingAreaToolbarContent = memo(({ stagingArea }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const shouldShowStagedImage = useStore($shouldShowStagedImage);
|
||||||
const images = useMemo(() => stagingArea.images, [stagingArea]);
|
const images = useMemo(() => stagingArea.images, [stagingArea]);
|
||||||
const imageDTO = useMemo(() => {
|
const imageDTO = useMemo(() => {
|
||||||
if (stagingArea.selectedImageIndex === null) {
|
if (stagingArea.selectedImageIndex === null) {
|
||||||
@ -74,6 +86,12 @@ export const StagingAreaToolbarContent = memo(({ stagingArea }: Props) => {
|
|||||||
dispatch(stagingAreaReset());
|
dispatch(stagingAreaReset());
|
||||||
}, [dispatch, stagingArea]);
|
}, [dispatch, stagingArea]);
|
||||||
|
|
||||||
|
const onToggleShouldShowStagedImage = useCallback(() => {
|
||||||
|
$shouldShowStagedImage.set(!shouldShowStagedImage);
|
||||||
|
}, [shouldShowStagedImage]);
|
||||||
|
|
||||||
|
const onSaveStagingImage = useCallback(() => {}, []);
|
||||||
|
|
||||||
useHotkeys(['left'], onPrev, {
|
useHotkeys(['left'], onPrev, {
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
});
|
});
|
||||||
@ -95,6 +113,7 @@ export const StagingAreaToolbarContent = memo(({ stagingArea }: Props) => {
|
|||||||
icon={<PiArrowLeftBold />}
|
icon={<PiArrowLeftBold />}
|
||||||
onClick={onPrev}
|
onClick={onPrev}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
|
isDisabled={images.length <= 1 || !shouldShowStagedImage}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="base"
|
colorScheme="base"
|
||||||
@ -107,6 +126,7 @@ export const StagingAreaToolbarContent = memo(({ stagingArea }: Props) => {
|
|||||||
icon={<PiArrowRightBold />}
|
icon={<PiArrowRightBold />}
|
||||||
onClick={onNext}
|
onClick={onNext}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
|
isDisabled={images.length <= 1 || !shouldShowStagedImage}
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<ButtonGroup borderRadius="base" shadow="dark-lg">
|
<ButtonGroup borderRadius="base" shadow="dark-lg">
|
||||||
@ -117,14 +137,22 @@ export const StagingAreaToolbarContent = memo(({ stagingArea }: Props) => {
|
|||||||
onClick={onAccept}
|
onClick={onAccept}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
/>
|
/>
|
||||||
{/* <IconButton
|
<IconButton
|
||||||
|
tooltip={shouldShowStagedImage ? t('unifiedCanvas.showResultsOn') : t('unifiedCanvas.showResultsOff')}
|
||||||
|
aria-label={shouldShowStagedImage ? t('unifiedCanvas.showResultsOn') : t('unifiedCanvas.showResultsOff')}
|
||||||
|
data-alert={!shouldShowStagedImage}
|
||||||
|
icon={shouldShowStagedImage ? <PiEyeBold /> : <PiEyeSlashBold />}
|
||||||
|
onClick={onToggleShouldShowStagedImage}
|
||||||
|
colorScheme="invokeBlue"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
tooltip={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
tooltip={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
||||||
aria-label={t('unifiedCanvas.saveToGallery')}
|
aria-label={t('unifiedCanvas.saveToGallery')}
|
||||||
isDisabled={!imageDTO || !imageDTO.is_intermediate}
|
isDisabled={!imageDTO || !imageDTO.is_intermediate}
|
||||||
icon={<PiFloppyDiskBold />}
|
icon={<PiFloppyDiskBold />}
|
||||||
onClick={handleSaveToGallery}
|
onClick={onSaveStagingImage}
|
||||||
colorScheme="invokeBlue"
|
colorScheme="invokeBlue"
|
||||||
/> */}
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
tooltip={`${t('unifiedCanvas.discardCurrent')}`}
|
tooltip={`${t('unifiedCanvas.discardCurrent')}`}
|
||||||
aria-label={t('unifiedCanvas.discardCurrent')}
|
aria-label={t('unifiedCanvas.discardCurrent')}
|
||||||
|
@ -52,6 +52,7 @@ export type StateApi = {
|
|||||||
getSelectedEntity: () => CanvasEntity | null;
|
getSelectedEntity: () => CanvasEntity | null;
|
||||||
getSpaceKey: () => boolean;
|
getSpaceKey: () => boolean;
|
||||||
setSpaceKey: (val: boolean) => void;
|
setSpaceKey: (val: boolean) => void;
|
||||||
|
getShouldShowStagedImage: () => boolean;
|
||||||
getBbox: () => CanvasV2State['bbox'];
|
getBbox: () => CanvasV2State['bbox'];
|
||||||
getSettings: () => CanvasV2State['settings'];
|
getSettings: () => CanvasV2State['settings'];
|
||||||
onBrushLineAdded: (arg: BrushLineAddedArg, entityType: CanvasEntity['type']) => void;
|
onBrushLineAdded: (arg: BrushLineAddedArg, entityType: CanvasEntity['type']) => void;
|
||||||
@ -275,7 +276,7 @@ export class KonvaNodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderStagingArea() {
|
renderStagingArea() {
|
||||||
this.preview.stagingArea.render(this.stateApi.getStagingAreaState());
|
this.preview.stagingArea.render(this.stateApi.getStagingAreaState(), this.stateApi.getShouldShowStagedImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
fitDocument() {
|
fitDocument() {
|
||||||
|
@ -7,6 +7,7 @@ import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
|||||||
import { KonvaNodeManager, setNodeManager } from 'features/controlLayers/konva/nodeManager';
|
import { KonvaNodeManager, setNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||||
import { updateBboxes } from 'features/controlLayers/konva/renderers/entityBbox';
|
import { updateBboxes } from 'features/controlLayers/konva/renderers/entityBbox';
|
||||||
import {
|
import {
|
||||||
|
$shouldShowStagedImage,
|
||||||
$stageAttrs,
|
$stageAttrs,
|
||||||
bboxChanged,
|
bboxChanged,
|
||||||
brushWidthChanged,
|
brushWidthChanged,
|
||||||
@ -303,6 +304,7 @@ export const initializeRenderer = (
|
|||||||
getMaskOpacity,
|
getMaskOpacity,
|
||||||
getInpaintMaskState,
|
getInpaintMaskState,
|
||||||
getStagingAreaState,
|
getStagingAreaState,
|
||||||
|
getShouldShowStagedImage: $shouldShowStagedImage.get,
|
||||||
|
|
||||||
// Read-write state
|
// Read-write state
|
||||||
setTool,
|
setTool,
|
||||||
@ -443,6 +445,9 @@ export const initializeRenderer = (
|
|||||||
|
|
||||||
const unsubscribeRenderer = subscribe(renderCanvas);
|
const unsubscribeRenderer = subscribe(renderCanvas);
|
||||||
|
|
||||||
|
// When we this flag, we need to render the staging area
|
||||||
|
$shouldShowStagedImage.subscribe(manager.renderStagingArea.bind(manager));
|
||||||
|
|
||||||
logIfDebugging('First render of konva stage');
|
logIfDebugging('First render of konva stage');
|
||||||
// On first render, the document should be fit to the stage.
|
// On first render, the document should be fit to the stage.
|
||||||
manager.renderDocumentSizeOverlay();
|
manager.renderDocumentSizeOverlay();
|
||||||
@ -454,6 +459,7 @@ export const initializeRenderer = (
|
|||||||
logIfDebugging('Cleaning up konva renderer');
|
logIfDebugging('Cleaning up konva renderer');
|
||||||
unsubscribeRenderer();
|
unsubscribeRenderer();
|
||||||
cleanupListeners();
|
cleanupListeners();
|
||||||
|
$shouldShowStagedImage.off();
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,6 @@ import type { CanvasV2State } from 'features/controlLayers/store/types';
|
|||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
|
||||||
export class CanvasStagingArea {
|
export class CanvasStagingArea {
|
||||||
group: Konva.Group;
|
group: Konva.Group;
|
||||||
image: KonvaImage | null;
|
image: KonvaImage | null;
|
||||||
@ -13,7 +12,7 @@ export class CanvasStagingArea {
|
|||||||
this.image = null;
|
this.image = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(stagingArea: CanvasV2State['stagingArea']) {
|
async render(stagingArea: CanvasV2State['stagingArea'], shouldShowStagedImage: boolean) {
|
||||||
if (!stagingArea || stagingArea.selectedImageIndex === null) {
|
if (!stagingArea || stagingArea.selectedImageIndex === null) {
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
this.image.destroy();
|
this.image.destroy();
|
||||||
@ -29,6 +28,7 @@ export class CanvasStagingArea {
|
|||||||
if (!this.image.isLoading && !this.image.isError && this.image.imageName !== imageDTO.image_name) {
|
if (!this.image.isLoading && !this.image.isError && this.image.imageName !== imageDTO.image_name) {
|
||||||
await this.image.updateImageSource(imageDTO.image_name);
|
await this.image.updateImageSource(imageDTO.image_name);
|
||||||
}
|
}
|
||||||
|
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
||||||
} else {
|
} else {
|
||||||
const { image_name, width, height } = imageDTO;
|
const { image_name, width, height } = imageDTO;
|
||||||
this.image = new KonvaImage({
|
this.image = new KonvaImage({
|
||||||
@ -49,6 +49,7 @@ export class CanvasStagingArea {
|
|||||||
});
|
});
|
||||||
this.group.add(this.image.konvaImageGroup);
|
this.group.add(this.image.konvaImageGroup);
|
||||||
await this.image.updateImageSource(imageDTO.image_name);
|
await this.image.updateImageSource(imageDTO.image_name);
|
||||||
|
this.image.konvaImageGroup.visible(shouldShowStagedImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,6 +357,7 @@ export const $stageAttrs = atom<StageAttrs>({
|
|||||||
height: 0,
|
height: 0,
|
||||||
scale: 0,
|
scale: 0,
|
||||||
});
|
});
|
||||||
|
export const $shouldShowStagedImage = atom(true);
|
||||||
|
|
||||||
export const canvasV2PersistConfig: PersistConfig<CanvasV2State> = {
|
export const canvasV2PersistConfig: PersistConfig<CanvasV2State> = {
|
||||||
name: canvasV2Slice.name,
|
name: canvasV2Slice.name,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user