forked from invoke-ai/InvokeAI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRasterLayerMenuItemsBooleanSubMenu.tsx
More file actions
76 lines (70 loc) · 3.78 KB
/
RasterLayerMenuItemsBooleanSubMenu.tsx
File metadata and controls
76 lines (70 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { useEntityIdentifierBelowThisOne } from 'features/controlLayers/hooks/useNextRenderableEntityIdentifier';
import { rasterLayerGlobalCompositeOperationChanged } from 'features/controlLayers/store/canvasSlice';
import type { CanvasEntityIdentifier, CompositeOperation } from 'features/controlLayers/store/types';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { CgPathBack, CgPathExclude, CgPathFront, CgPathIntersect } from 'react-icons/cg';
import { PiIntersectSquareBold } from 'react-icons/pi';
export const RasterLayerMenuItemsBooleanSubMenu = memo(() => {
const { t } = useTranslation();
const subMenu = useSubMenu();
const canvasManager = useCanvasManager();
const isBusy = useCanvasIsBusy();
const dispatch = useAppDispatch();
const entityIdentifier = useEntityIdentifierContext<'raster_layer'>();
const entityIdentifierBelowThisOne = useEntityIdentifierBelowThisOne(entityIdentifier as CanvasEntityIdentifier);
const perform = useCallback(
async (op: CompositeOperation) => {
if (!entityIdentifierBelowThisOne) {
return;
}
dispatch(rasterLayerGlobalCompositeOperationChanged({ entityIdentifier, globalCompositeOperation: op }));
try {
// Use boolean-specific merge which disables the source raster layers instead of deleting them
await canvasManager.compositor.mergeBooleanRasterLayers(
entityIdentifierBelowThisOne as CanvasEntityIdentifier<'raster_layer'>,
entityIdentifier as CanvasEntityIdentifier<'raster_layer'>,
true
);
} finally {
dispatch(rasterLayerGlobalCompositeOperationChanged({ entityIdentifier, globalCompositeOperation: undefined }));
}
},
[canvasManager.compositor, dispatch, entityIdentifier, entityIdentifierBelowThisOne]
);
const onIntersect = useCallback(() => perform('source-in'), [perform]);
const onCutOut = useCallback(() => perform('destination-in'), [perform]);
const onCutAway = useCallback(() => perform('source-out'), [perform]);
const onExclude = useCallback(() => perform('xor'), [perform]);
const disabled = isBusy || !entityIdentifierBelowThisOne;
return (
<MenuItem {...subMenu.parentMenuItemProps} isDisabled={disabled} icon={<PiIntersectSquareBold />}>
<Menu {...subMenu.menuProps}>
<MenuButton {...subMenu.menuButtonProps}>
<SubMenuButtonContent label={t('controlLayers.booleanOps.label')} />
</MenuButton>
<MenuList {...subMenu.menuListProps}>
<MenuItem onClick={onIntersect} isDisabled={disabled} icon={<CgPathIntersect size={18} />}>
{t('controlLayers.booleanOps.intersect')}
</MenuItem>
<MenuItem onClick={onCutOut} isDisabled={disabled} icon={<CgPathBack size={18} />}>
{t('controlLayers.booleanOps.cutout')}
</MenuItem>
<MenuItem onClick={onCutAway} isDisabled={disabled} icon={<CgPathFront size={18} />}>
{t('controlLayers.booleanOps.cutaway')}
</MenuItem>
<MenuItem onClick={onExclude} isDisabled={disabled} icon={<CgPathExclude size={18} />}>
{t('controlLayers.booleanOps.exclude')}
</MenuItem>
</MenuList>
</Menu>
</MenuItem>
);
});
RasterLayerMenuItemsBooleanSubMenu.displayName = 'RasterLayerMenuItemsBooleanSubMenu';