feat(ui): updated canvas context menu

This commit is contained in:
psychedelicious
2024-09-16 16:13:50 +10:00
committed by Kent Keirsey
parent 26e23a43dc
commit 0ebe070be6
5 changed files with 177 additions and 72 deletions

View File

@@ -1678,6 +1678,18 @@
"newRasterLayerFromBbox": "New Raster Layer from Bbox",
"savedToGalleryOk": "Saved to Gallery",
"savedToGalleryError": "Error saving to gallery",
"newGlobalReferenceImageOk": "Created Global Reference Image",
"newGlobalReferenceImageError": "Problem Creating Global Reference Image",
"newRegionalReferenceImageOk": "Created Regional Reference Image",
"newRegionalReferenceImageError": "Problem Creating Regional Reference Image",
"newControlLayerOk": "Created Control Layer",
"newControlLayerError": "Problem Creating Control Layer",
"newRasterLayerOk": "Created Raster Layer",
"newRasterLayerError": "Problem Creating Raster Layer",
"pullBboxIntoLayerOk": "Bbox Pulled Into Layer",
"pullBboxIntoLayerError": "Problem Pulling BBox Into Layer",
"pullBboxIntoReferenceImageOk": "Bbox Pulled Into ReferenceImage",
"pullBboxIntoReferenceImageError": "Problem Pulling BBox Into ReferenceImage",
"regionIsEmpty": "Selected region is empty",
"mergeVisible": "Merge Visible",
"mergeVisibleOk": "Merged visible layers",
@@ -1932,6 +1944,16 @@
"isDisabled": "{{title}} is disabled",
"isEmpty": "{{title}} is empty"
}
},
"canvasContextMenu": {
"saveToGalleryGroup": "Save To Gallery",
"saveCanvasToGallery": "Save Canvas To Gallery",
"saveBboxToGallery": "Save Bbox To Gallery",
"bboxGroup": "Create From Bbox",
"newGlobalReferenceImage": "New Global Reference Image",
"newRegionalReferenceImage": "New Regional Reference Image",
"newControlLayer": "New Control Layer",
"newRasterLayer": "New Raster Layer"
}
},
"upscaling": {

View File

@@ -1,9 +1,9 @@
import { MenuGroup, MenuItem } from '@invoke-ai/ui-library';
import {
useNewControlLayerFromBbox,
useNewGlobalIPAdapterFromBbox,
useNewGlobalReferenceImageFromBbox,
useNewRasterLayerFromBbox,
useNewRegionalIPAdapterFromBbox,
useNewRegionalReferenceImageFromBbox,
useSaveBboxToGallery,
useSaveCanvasToGallery,
} from 'features/controlLayers/hooks/saveCanvasHooks';
@@ -17,32 +17,36 @@ export const CanvasContextMenuGlobalMenuItems = memo(() => {
const isBusy = useCanvasIsBusy();
const saveCanvasToGallery = useSaveCanvasToGallery();
const saveBboxToGallery = useSaveBboxToGallery();
const saveBboxAsRegionalGuidanceIPAdapter = useNewRegionalIPAdapterFromBbox();
const saveBboxAsIPAdapter = useNewGlobalIPAdapterFromBbox();
const saveBboxAsRasterLayer = useNewRasterLayerFromBbox();
const saveBboxAsControlLayer = useNewControlLayerFromBbox();
const newRegionalReferenceImageFromBbox = useNewRegionalReferenceImageFromBbox();
const newGlobalReferenceImageFromBbox = useNewGlobalReferenceImageFromBbox();
const newRasterLayerFromBbox = useNewRasterLayerFromBbox();
const newControlLayerFromBbox = useNewControlLayerFromBbox();
return (
<MenuGroup title={t('controlLayers.canvas')}>
<MenuItem icon={<PiFloppyDiskBold />} isDisabled={isBusy} onClick={saveCanvasToGallery}>
{t('controlLayers.saveCanvasToGallery')}
</MenuItem>
<MenuItem icon={<PiFloppyDiskBold />} isDisabled={isBusy} onClick={saveBboxToGallery}>
{t('controlLayers.saveBboxToGallery')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={saveBboxAsIPAdapter}>
{t('controlLayers.newGlobalIPAdapterFromBbox')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={saveBboxAsRegionalGuidanceIPAdapter}>
{t('controlLayers.newRegionalIPAdapterFromBbox')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={saveBboxAsControlLayer}>
{t('controlLayers.newControlLayerFromBbox')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={saveBboxAsRasterLayer}>
{t('controlLayers.newRasterLayerFromBbox')}
</MenuItem>
</MenuGroup>
<>
<MenuGroup title={t('controlLayers.canvasContextMenu.saveToGalleryGroup')}>
<MenuItem icon={<PiFloppyDiskBold />} isDisabled={isBusy} onClick={saveCanvasToGallery}>
{t('controlLayers.canvasContextMenu.saveCanvasToGallery')}
</MenuItem>
<MenuItem icon={<PiFloppyDiskBold />} isDisabled={isBusy} onClick={saveBboxToGallery}>
{t('controlLayers.canvasContextMenu.saveBboxToGallery')}
</MenuItem>
</MenuGroup>
<MenuGroup title={t('controlLayers.canvasContextMenu.bboxGroup')}>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newGlobalReferenceImageFromBbox}>
{t('controlLayers.canvasContextMenu.newGlobalReferenceImage')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newRegionalReferenceImageFromBbox}>
{t('controlLayers.canvasContextMenu.newRegionalReferenceImage')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newControlLayerFromBbox}>
{t('controlLayers.canvasContextMenu.newControlLayer')}
</MenuItem>
<MenuItem icon={<PiStackPlusFill />} isDisabled={isBusy} onClick={newRasterLayerFromBbox}>
{t('controlLayers.canvasContextMenu.newRasterLayer')}
</MenuItem>
</MenuGroup>
</>
);
});

View File

@@ -6,7 +6,7 @@ import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/c
import { Weight } from 'features/controlLayers/components/common/Weight';
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { usePullBboxIntoIPAdapter } from 'features/controlLayers/hooks/saveCanvasHooks';
import { usePullBboxIntoGlobalReferenceImage } from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import {
referenceImageIPAdapterBeginEndStepPctChanged,
@@ -87,7 +87,7 @@ export const IPAdapterSettings = memo(() => {
() => ({ type: 'SET_IPA_IMAGE', id: entityIdentifier.id }),
[entityIdentifier.id]
);
const pullBboxIntoIPAdapter = usePullBboxIntoIPAdapter(entityIdentifier);
const pullBboxIntoIPAdapter = usePullBboxIntoGlobalReferenceImage(entityIdentifier);
const isBusy = useCanvasIsBusy();
return (

View File

@@ -7,7 +7,7 @@ import { IPAdapterImagePreview } from 'features/controlLayers/components/IPAdapt
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
import { IPAdapterModel } from 'features/controlLayers/components/IPAdapter/IPAdapterModel';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { usePullBboxIntoRegionalGuidanceIPAdapter } from 'features/controlLayers/hooks/saveCanvasHooks';
import { usePullBboxIntoRegionalGuidanceReferenceImage } from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import {
rgIPAdapterBeginEndStepPctChanged,
@@ -104,7 +104,7 @@ export const RegionalGuidanceIPAdapterSettings = memo(({ referenceImageId, ipAda
() => ({ type: 'SET_RG_IP_ADAPTER_IMAGE', id: entityIdentifier.id, referenceImageId: referenceImageId }),
[entityIdentifier.id, referenceImageId]
);
const pullBboxIntoIPAdapter = usePullBboxIntoRegionalGuidanceIPAdapter(entityIdentifier, referenceImageId);
const pullBboxIntoIPAdapter = usePullBboxIntoRegionalGuidanceReferenceImage(entityIdentifier, referenceImageId);
const isBusy = useCanvasIsBusy();
return (

View File

@@ -35,10 +35,12 @@ const log = logger('canvas');
type UseSaveCanvasArg = {
region: 'canvas' | 'bbox';
saveToGallery: boolean;
toastOk: string;
toastError: string;
onSave?: (imageDTO: ImageDTO, rect: Rect) => void;
};
const useSaveCanvas = ({ region, saveToGallery, onSave }: UseSaveCanvasArg) => {
const useSaveCanvas = ({ region, saveToGallery, toastOk, toastError, onSave }: UseSaveCanvasArg) => {
const { t } = useTranslation();
const canvasManager = useCanvasManager();
@@ -49,7 +51,7 @@ const useSaveCanvas = ({ region, saveToGallery, onSave }: UseSaveCanvasArg) => {
if (rect.width === 0 || rect.height === 0) {
toast({
title: t('controlLayers.savedToGalleryError'),
title: toastError,
description: t('controlLayers.regionIsEmpty'),
status: 'warning',
});
@@ -64,36 +66,65 @@ const useSaveCanvas = ({ region, saveToGallery, onSave }: UseSaveCanvasArg) => {
if (onSave) {
onSave(result.value, rect);
}
toast({ title: t('controlLayers.savedToGalleryOk') });
toast({ title: toastOk });
} else {
log.error({ error: serializeError(result.error) }, 'Failed to save canvas to gallery');
toast({ title: t('controlLayers.savedToGalleryError'), status: 'error' });
toast({ title: toastError, status: 'error' });
}
}, [canvasManager.compositor, canvasManager.stage, canvasManager.stateApi, onSave, region, saveToGallery, t]);
}, [
canvasManager.compositor,
canvasManager.stage,
canvasManager.stateApi,
onSave,
region,
saveToGallery,
t,
toastError,
toastOk,
]);
return saveCanvas;
};
const saveCanvasToGalleryArg: UseSaveCanvasArg = { region: 'canvas', saveToGallery: true };
export const useSaveCanvasToGallery = () => {
const saveCanvasToGallery = useSaveCanvas(saveCanvasToGalleryArg);
return saveCanvasToGallery;
const { t } = useTranslation();
const arg: UseSaveCanvasArg = useMemo(
() => ({
region: 'canvas',
saveToGallery: true,
toastOk: t('controlLayers.savedToGalleryOk'),
toastError: t('controlLayers.savedToGalleryError'),
}),
[t]
);
const func = useSaveCanvas(arg);
return func;
};
const saveBboxToGalleryArg: UseSaveCanvasArg = { region: 'bbox', saveToGallery: true };
export const useSaveBboxToGallery = () => {
const saveBboxToGallery = useSaveCanvas(saveBboxToGalleryArg);
return saveBboxToGallery;
const { t } = useTranslation();
const arg: UseSaveCanvasArg = useMemo(
() => ({
region: 'bbox',
saveToGallery: true,
toastOk: t('controlLayers.savedToGalleryOk'),
toastError: t('controlLayers.savedToGalleryError'),
}),
[t]
);
const func = useSaveCanvas(arg);
return func;
};
export const useNewRegionalIPAdapterFromBbox = () => {
export const useNewRegionalReferenceImageFromBbox = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const defaultIPAdapter = useAppSelector(selectDefaultIPAdapter);
const arg = useMemo<UseSaveCanvasArg>(() => {
const onSave = (imageDTO: ImageDTO) => {
const ipAdapter: RegionalGuidanceReferenceImageState = {
id: getPrefixedId('regional_guidance_ip_adapter'),
id: getPrefixedId('regional_guidance_reference_image'),
ipAdapter: {
...deepClone(defaultIPAdapter),
image: imageDTOToImageWithDims(imageDTO),
@@ -106,13 +137,20 @@ export const useNewRegionalIPAdapterFromBbox = () => {
dispatch(rgAdded({ overrides, isSelected: true }));
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [defaultIPAdapter, dispatch]);
const newRegionalIPAdapterFromBbox = useSaveCanvas(arg);
return newRegionalIPAdapterFromBbox;
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.newRegionalReferenceImageOk'),
toastError: t('controlLayers.newRegionalReferenceImageError'),
};
}, [defaultIPAdapter, dispatch, t]);
const func = useSaveCanvas(arg);
return func;
};
export const useNewGlobalIPAdapterFromBbox = () => {
export const useNewGlobalReferenceImageFromBbox = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const defaultIPAdapter = useAppSelector(selectDefaultIPAdapter);
@@ -127,13 +165,20 @@ export const useNewGlobalIPAdapterFromBbox = () => {
dispatch(referenceImageAdded({ overrides, isSelected: true }));
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [defaultIPAdapter, dispatch]);
const newGlobalIPAdapterFromBbox = useSaveCanvas(arg);
return newGlobalIPAdapterFromBbox;
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.newGlobalReferenceImageOk'),
toastError: t('controlLayers.newGlobalReferenceImageError'),
};
}, [defaultIPAdapter, dispatch, t]);
const func = useSaveCanvas(arg);
return func;
};
export const useNewRasterLayerFromBbox = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const arg = useMemo<UseSaveCanvasArg>(() => {
const onSave = (imageDTO: ImageDTO, rect: Rect) => {
@@ -144,13 +189,20 @@ export const useNewRasterLayerFromBbox = () => {
dispatch(rasterLayerAdded({ overrides, isSelected: true }));
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [dispatch]);
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.newRasterLayerOk'),
toastError: t('controlLayers.newRasterLayerError'),
};
}, [dispatch, t]);
const newRasterLayerFromBbox = useSaveCanvas(arg);
return newRasterLayerFromBbox;
};
export const useNewControlLayerFromBbox = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const defaultControlAdapter = useAppSelector(selectDefaultControlAdapter);
@@ -164,13 +216,20 @@ export const useNewControlLayerFromBbox = () => {
dispatch(controlLayerAdded({ overrides, isSelected: true }));
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [defaultControlAdapter, dispatch]);
const newControlLayerFromBbox = useSaveCanvas(arg);
return newControlLayerFromBbox;
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.newControlLayerOk'),
toastError: t('controlLayers.newControlLayerError'),
};
}, [defaultControlAdapter, dispatch, t]);
const func = useSaveCanvas(arg);
return func;
};
export const usePullBboxIntoLayer = (entityIdentifier: CanvasEntityIdentifier<'control_layer' | 'raster_layer'>) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const arg = useMemo<UseSaveCanvasArg>(() => {
@@ -185,14 +244,21 @@ export const usePullBboxIntoLayer = (entityIdentifier: CanvasEntityIdentifier<'c
);
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [dispatch, entityIdentifier]);
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.pullBboxIntoLayerOk'),
toastError: t('controlLayers.pullBboxIntoLayerError'),
};
}, [dispatch, entityIdentifier, t]);
const pullBboxIntoLayer = useSaveCanvas(arg);
return pullBboxIntoLayer;
const func = useSaveCanvas(arg);
return func;
};
export const usePullBboxIntoIPAdapter = (entityIdentifier: CanvasEntityIdentifier<'reference_image'>) => {
export const usePullBboxIntoGlobalReferenceImage = (entityIdentifier: CanvasEntityIdentifier<'reference_image'>) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const arg = useMemo<UseSaveCanvasArg>(() => {
@@ -200,17 +266,24 @@ export const usePullBboxIntoIPAdapter = (entityIdentifier: CanvasEntityIdentifie
dispatch(referenceImageIPAdapterImageChanged({ entityIdentifier, imageDTO }));
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [dispatch, entityIdentifier]);
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.pullBboxIntoReferenceImageOk'),
toastError: t('controlLayers.pullBboxIntoReferenceImageError'),
};
}, [dispatch, entityIdentifier, t]);
const pullBboxIntoIPAdapter = useSaveCanvas(arg);
return pullBboxIntoIPAdapter;
const func = useSaveCanvas(arg);
return func;
};
export const usePullBboxIntoRegionalGuidanceIPAdapter = (
export const usePullBboxIntoRegionalGuidanceReferenceImage = (
entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>,
referenceImageId: string
) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const arg = useMemo<UseSaveCanvasArg>(() => {
@@ -218,9 +291,15 @@ export const usePullBboxIntoRegionalGuidanceIPAdapter = (
dispatch(rgIPAdapterImageChanged({ entityIdentifier, referenceImageId, imageDTO }));
};
return { region: 'bbox', saveToGallery: false, onSave };
}, [dispatch, entityIdentifier, referenceImageId]);
return {
region: 'bbox',
saveToGallery: false,
onSave,
toastOk: t('controlLayers.pullBboxIntoReferenceImageOk'),
toastError: t('controlLayers.pullBboxIntoReferenceImageError'),
};
}, [dispatch, entityIdentifier, referenceImageId, t]);
const pullBboxIntoRegionalGuidanceIPAdapter = useSaveCanvas(arg);
return pullBboxIntoRegionalGuidanceIPAdapter;
const func = useSaveCanvas(arg);
return func;
};