mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): duplicate entity
This commit is contained in:
parent
3ea83574c0
commit
b0db9a3f56
@ -1662,6 +1662,7 @@
|
|||||||
"recalculateRects": "Recalculate Rects",
|
"recalculateRects": "Recalculate Rects",
|
||||||
"clipToBbox": "Clip Strokes to Bbox",
|
"clipToBbox": "Clip Strokes to Bbox",
|
||||||
"addLayer": "Add Layer",
|
"addLayer": "Add Layer",
|
||||||
|
"duplicate": "Duplicate",
|
||||||
"moveToFront": "Move to Front",
|
"moveToFront": "Move to Front",
|
||||||
"moveToBack": "Move to Back",
|
"moveToBack": "Move to Back",
|
||||||
"moveForward": "Move Forward",
|
"moveForward": "Move Forward",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MenuDivider } from '@invoke-ai/ui-library';
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
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 { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
|
||||||
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
|
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
|
||||||
import { ControlLayerMenuItemsControlToRaster } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItemsControlToRaster';
|
import { ControlLayerMenuItemsControlToRaster } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItemsControlToRaster';
|
||||||
@ -17,6 +18,7 @@ export const ControlLayerMenuItems = memo(() => {
|
|||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<CanvasEntityMenuItemsArrange />
|
<CanvasEntityMenuItemsArrange />
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsDuplicate />
|
||||||
<CanvasEntityMenuItemsDelete />
|
<CanvasEntityMenuItemsDelete />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MenuDivider } from '@invoke-ai/ui-library';
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
||||||
|
import { CanvasEntityMenuItemsDuplicate } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
export const IPAdapterMenuItems = memo(() => {
|
export const IPAdapterMenuItems = memo(() => {
|
||||||
@ -8,6 +9,7 @@ export const IPAdapterMenuItems = memo(() => {
|
|||||||
<>
|
<>
|
||||||
<CanvasEntityMenuItemsArrange />
|
<CanvasEntityMenuItemsArrange />
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsDuplicate />
|
||||||
<CanvasEntityMenuItemsDelete />
|
<CanvasEntityMenuItemsDelete />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MenuDivider } from '@invoke-ai/ui-library';
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
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 { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ export const InpaintMaskMenuItems = memo(() => {
|
|||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<CanvasEntityMenuItemsArrange />
|
<CanvasEntityMenuItemsArrange />
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsDuplicate />
|
||||||
<CanvasEntityMenuItemsDelete />
|
<CanvasEntityMenuItemsDelete />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MenuDivider } from '@invoke-ai/ui-library';
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
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 { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
|
||||||
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
|
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
|
||||||
import { RasterLayerMenuItemsRasterToControl } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItemsRasterToControl';
|
import { RasterLayerMenuItemsRasterToControl } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItemsRasterToControl';
|
||||||
@ -15,6 +16,7 @@ export const RasterLayerMenuItems = memo(() => {
|
|||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<CanvasEntityMenuItemsArrange />
|
<CanvasEntityMenuItemsArrange />
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsDuplicate />
|
||||||
<CanvasEntityMenuItemsDelete />
|
<CanvasEntityMenuItemsDelete />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MenuDivider } from '@invoke-ai/ui-library';
|
import { MenuDivider } from '@invoke-ai/ui-library';
|
||||||
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
|
||||||
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
|
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 { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
|
||||||
import { RegionalGuidanceMenuItemsAddPromptsAndIPAdapter } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter';
|
import { RegionalGuidanceMenuItemsAddPromptsAndIPAdapter } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter';
|
||||||
import { RegionalGuidanceMenuItemsAutoNegative } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative';
|
import { RegionalGuidanceMenuItemsAutoNegative } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative';
|
||||||
@ -16,6 +17,7 @@ export const RegionalGuidanceMenuItems = memo(() => {
|
|||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<CanvasEntityMenuItemsArrange />
|
<CanvasEntityMenuItemsArrange />
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
<CanvasEntityMenuItemsDuplicate />
|
||||||
<CanvasEntityMenuItemsDelete />
|
<CanvasEntityMenuItemsDelete />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -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';
|
@ -3,6 +3,7 @@ import { createAction, createSlice } from '@reduxjs/toolkit';
|
|||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/util/arrayUtils';
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import { bboxReducers } from 'features/controlLayers/store/bboxReducers';
|
import { bboxReducers } from 'features/controlLayers/store/bboxReducers';
|
||||||
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
|
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
|
||||||
import { controlLayersReducers } from 'features/controlLayers/store/controlLayersReducers';
|
import { controlLayersReducers } from 'features/controlLayers/store/controlLayersReducers';
|
||||||
@ -237,6 +238,42 @@ export const canvasV2Slice = createSlice({
|
|||||||
assert(false, 'Not implemented');
|
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>) => {
|
entityIsEnabledToggled: (state, action: PayloadAction<EntityIdentifierPayload>) => {
|
||||||
const { entityIdentifier } = action.payload;
|
const { entityIdentifier } = action.payload;
|
||||||
const entity = selectEntity(state, entityIdentifier);
|
const entity = selectEntity(state, entityIdentifier);
|
||||||
@ -460,6 +497,7 @@ export const {
|
|||||||
entityReset,
|
entityReset,
|
||||||
entityIsEnabledToggled,
|
entityIsEnabledToggled,
|
||||||
entityMoved,
|
entityMoved,
|
||||||
|
entityDuplicated,
|
||||||
entityRasterized,
|
entityRasterized,
|
||||||
entityBrushLineAdded,
|
entityBrushLineAdded,
|
||||||
entityEraserLineAdded,
|
entityEraserLineAdded,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user