feat(ui): duplicate entity

This commit is contained in:
psychedelicious 2024-08-24 12:20:35 +10:00
parent 3ea83574c0
commit b0db9a3f56
8 changed files with 74 additions and 0 deletions

View File

@ -1662,6 +1662,7 @@
"recalculateRects": "Recalculate Rects",
"clipToBbox": "Clip Strokes to Bbox",
"addLayer": "Add Layer",
"duplicate": "Duplicate",
"moveToFront": "Move to Front",
"moveToBack": "Move to Back",
"moveForward": "Move Forward",

View File

@ -1,6 +1,7 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
import { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { ControlLayerMenuItemsControlToRaster } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItemsControlToRaster';
@ -17,6 +18,7 @@ export const ControlLayerMenuItems = memo(() => {
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDuplicate />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@ -1,6 +1,7 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
import { memo } from 'react';
export const IPAdapterMenuItems = memo(() => {
@ -8,6 +9,7 @@ export const IPAdapterMenuItems = memo(() => {
<>
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDuplicate />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@ -1,6 +1,7 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { memo } from 'react';
@ -11,6 +12,7 @@ export const InpaintMaskMenuItems = memo(() => {
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDuplicate />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@ -1,6 +1,7 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
import { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { RasterLayerMenuItemsRasterToControl } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItemsRasterToControl';
@ -15,6 +16,7 @@ export const RasterLayerMenuItems = memo(() => {
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDuplicate />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@ -1,6 +1,7 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { RegionalGuidanceMenuItemsAddPromptsAndIPAdapter } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter';
import { RegionalGuidanceMenuItemsAutoNegative } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative';
@ -16,6 +17,7 @@ export const RegionalGuidanceMenuItems = memo(() => {
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDuplicate />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@ -0,0 +1,25 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { entityDuplicated } from 'features/controlLayers/store/canvasV2Slice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCopyFill } from 'react-icons/pi';
export const CanvasEntityMenuItemsDuplicate = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const entityIdentifier = useEntityIdentifierContext();
const onClick = useCallback(() => {
dispatch(entityDuplicated({ entityIdentifier }));
}, [dispatch, entityIdentifier]);
return (
<MenuItem onClick={onClick} icon={<PiCopyFill />}>
{t('controlLayers.duplicate')}
</MenuItem>
);
});
CanvasEntityMenuItemsDuplicate.displayName = 'CanvasEntityMenuItemsDuplicate';

View File

@ -3,6 +3,7 @@ import { createAction, createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
import { deepClone } from 'common/util/deepClone';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { bboxReducers } from 'features/controlLayers/store/bboxReducers';
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
import { controlLayersReducers } from 'features/controlLayers/store/controlLayersReducers';
@ -237,6 +238,42 @@ export const canvasV2Slice = createSlice({
assert(false, 'Not implemented');
}
},
entityDuplicated: (state, action: PayloadAction<EntityIdentifierPayload>) => {
const { entityIdentifier } = action.payload;
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
}
const newEntity = deepClone(entity);
if (newEntity.name) {
newEntity.name = `${newEntity.name} (Copy)`;
}
switch (newEntity.type) {
case 'raster_layer':
newEntity.id = getPrefixedId('raster_layer');
state.rasterLayers.entities.push(newEntity);
break;
case 'control_layer':
newEntity.id = getPrefixedId('control_layer');
state.controlLayers.entities.push(newEntity);
break;
case 'regional_guidance':
newEntity.id = getPrefixedId('regional_guidance');
state.regions.entities.push(newEntity);
break;
case 'ip_adapter':
newEntity.id = getPrefixedId('ip_adapter');
state.ipAdapters.entities.push(newEntity);
break;
case 'inpaint_mask':
newEntity.id = getPrefixedId('inpaint_mask');
state.inpaintMasks.entities.push(newEntity);
break;
}
state.selectedEntityIdentifier = getEntityIdentifier(newEntity);
},
entityIsEnabledToggled: (state, action: PayloadAction<EntityIdentifierPayload>) => {
const { entityIdentifier } = action.payload;
const entity = selectEntity(state, entityIdentifier);
@ -460,6 +497,7 @@ export const {
entityReset,
entityIsEnabledToggled,
entityMoved,
entityDuplicated,
entityRasterized,
entityBrushLineAdded,
entityEraserLineAdded,