feat(ui): add toggle for clipToBbox

This commit is contained in:
psychedelicious 2024-06-20 10:41:11 +10:00
parent 488ca87787
commit ce8a7bc178
6 changed files with 66 additions and 40 deletions

View File

@ -11,7 +11,7 @@ import {
} from '@invoke-ai/ui-library'; } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { MaskOpacity } from 'features/controlLayers/components/MaskOpacity'; import { MaskOpacity } from 'features/controlLayers/components/MaskOpacity';
import { invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice'; import { clipToBboxChanged, invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -20,11 +20,16 @@ import { RiSettings4Fill } from 'react-icons/ri';
const ControlLayersSettingsPopover = () => { const ControlLayersSettingsPopover = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const clipToBbox = useAppSelector((s) => s.canvasV2.settings.clipToBbox);
const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll); const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll);
const onChangeInvertScroll = useCallback( const onChangeInvertScroll = useCallback(
(e: ChangeEvent<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)), (e: ChangeEvent<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)),
[dispatch] [dispatch]
); );
const onChangeClipToBbox = useCallback(
(e: ChangeEvent<HTMLInputElement>) => dispatch(clipToBboxChanged(e.target.checked)),
[dispatch]
);
return ( return (
<Popover isLazy> <Popover isLazy>
<PopoverTrigger> <PopoverTrigger>
@ -38,6 +43,10 @@ const ControlLayersSettingsPopover = () => {
<FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel> <FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
<Checkbox isChecked={invertScroll} onChange={onChangeInvertScroll} /> <Checkbox isChecked={invertScroll} onChange={onChangeInvertScroll} />
</FormControl> </FormControl>
<FormControl w="full">
<FormLabel flexGrow={1}>{t('unifiedCanvas.clipToBbox')}</FormLabel>
<Checkbox isChecked={clipToBbox} onChange={onChangeClipToBbox} />
</FormControl>
</Flex> </Flex>
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>

View File

@ -53,6 +53,7 @@ type Arg = {
setSpaceKey: (val: boolean) => void; setSpaceKey: (val: boolean) => void;
getDocument: () => CanvasV2State['document']; getDocument: () => CanvasV2State['document'];
getBbox: () => CanvasV2State['bbox']; getBbox: () => CanvasV2State['bbox'];
getSettings: () => CanvasV2State['settings'];
onBrushLineAdded: (arg: BrushLineAddedArg, entityType: CanvasEntity['type']) => void; onBrushLineAdded: (arg: BrushLineAddedArg, entityType: CanvasEntity['type']) => void;
onEraserLineAdded: (arg: EraserLineAddedArg, entityType: CanvasEntity['type']) => void; onEraserLineAdded: (arg: EraserLineAddedArg, entityType: CanvasEntity['type']) => void;
onPointAddedToLine: (arg: PointAddedToLineArg, entityType: CanvasEntity['type']) => void; onPointAddedToLine: (arg: PointAddedToLineArg, entityType: CanvasEntity['type']) => void;
@ -155,6 +156,7 @@ export const setStageEventHandlers = ({
setSpaceKey, setSpaceKey,
getDocument, getDocument,
getBbox, getBbox,
getSettings,
onBrushLineAdded, onBrushLineAdded,
onEraserLineAdded, onEraserLineAdded,
onPointAddedToLine, onPointAddedToLine,
@ -203,6 +205,17 @@ export const setStageEventHandlers = ({
if (toolState.selected === 'brush') { if (toolState.selected === 'brush') {
const bbox = getBbox(); const bbox = getBbox();
const settings = getSettings();
const clip = settings.clipToBbox
? {
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
}
: null;
if (e.evt.shiftKey) { if (e.evt.shiftKey) {
const lastAddedPoint = getLastAddedPoint(); const lastAddedPoint = getLastAddedPoint();
// Create a straight line if holding shift // Create a straight line if holding shift
@ -218,12 +231,7 @@ export const setStageEventHandlers = ({
], ],
color: getCurrentFill(), color: getCurrentFill(),
width: toolState.brush.width, width: toolState.brush.width,
clip: { clip,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
},
}, },
selectedEntity.type selectedEntity.type
); );
@ -240,12 +248,7 @@ export const setStageEventHandlers = ({
], ],
color: getCurrentFill(), color: getCurrentFill(),
width: toolState.brush.width, width: toolState.brush.width,
clip: { clip,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
},
}, },
selectedEntity.type selectedEntity.type
); );
@ -255,6 +258,16 @@ export const setStageEventHandlers = ({
if (toolState.selected === 'eraser') { if (toolState.selected === 'eraser') {
const bbox = getBbox(); const bbox = getBbox();
const settings = getSettings();
const clip = settings.clipToBbox
? {
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
}
: null;
if (e.evt.shiftKey) { if (e.evt.shiftKey) {
// Create a straight line if holding shift // Create a straight line if holding shift
const lastAddedPoint = getLastAddedPoint(); const lastAddedPoint = getLastAddedPoint();
@ -269,12 +282,7 @@ export const setStageEventHandlers = ({
pos.y - selectedEntity.y, pos.y - selectedEntity.y,
], ],
width: toolState.eraser.width, width: toolState.eraser.width,
clip: { clip,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
},
}, },
selectedEntity.type selectedEntity.type
); );
@ -290,12 +298,7 @@ export const setStageEventHandlers = ({
pos.y - selectedEntity.y, pos.y - selectedEntity.y,
], ],
width: toolState.eraser.width, width: toolState.eraser.width,
clip: { clip,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
},
}, },
selectedEntity.type selectedEntity.type
); );
@ -402,6 +405,16 @@ export const setStageEventHandlers = ({
); );
} else { } else {
const bbox = getBbox(); const bbox = getBbox();
const settings = getSettings();
const clip = settings.clipToBbox
? {
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
}
: null;
// Start a new line // Start a new line
onBrushLineAdded( onBrushLineAdded(
{ {
@ -414,12 +427,7 @@ export const setStageEventHandlers = ({
], ],
width: toolState.brush.width, width: toolState.brush.width,
color: getCurrentFill(), color: getCurrentFill(),
clip: { clip,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
},
}, },
selectedEntity.type selectedEntity.type
); );
@ -441,6 +449,16 @@ export const setStageEventHandlers = ({
); );
} else { } else {
const bbox = getBbox(); const bbox = getBbox();
const settings = getSettings();
const clip = settings.clipToBbox
? {
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
}
: null;
// Start a new line // Start a new line
onEraserLineAdded( onEraserLineAdded(
{ {
@ -452,12 +470,7 @@ export const setStageEventHandlers = ({
pos.y - selectedEntity.y, pos.y - selectedEntity.y,
], ],
width: toolState.eraser.width, width: toolState.eraser.width,
clip: { clip,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height,
},
}, },
selectedEntity.type selectedEntity.type
); );

View File

@ -33,8 +33,6 @@ const getControlAdapter = (manager: KonvaNodeManager, entity: ControlAdapterEnti
listening: false, listening: false,
}); });
const konvaObjectGroup = createObjectGroup(konvaLayer, CA_LAYER_OBJECT_GROUP_NAME); const konvaObjectGroup = createObjectGroup(konvaLayer, CA_LAYER_OBJECT_GROUP_NAME);
konvaLayer.add(konvaObjectGroup);
manager.stage.add(konvaLayer);
return manager.add(entity.id, konvaLayer, konvaObjectGroup); return manager.add(entity.id, konvaLayer, konvaObjectGroup);
}; };

View File

@ -3,8 +3,8 @@ import type { Store } from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { $isDebugging } from 'app/store/nanostores/isDebugging'; import { $isDebugging } from 'app/store/nanostores/isDebugging';
import type { RootState } from 'app/store/store'; import type { RootState } from 'app/store/store';
import { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
import { setStageEventHandlers } from 'features/controlLayers/konva/events'; import { setStageEventHandlers } from 'features/controlLayers/konva/events';
import { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
import { arrangeEntities } from 'features/controlLayers/konva/renderers/arrange'; import { arrangeEntities } from 'features/controlLayers/konva/renderers/arrange';
import { renderBackgroundLayer } from 'features/controlLayers/konva/renderers/background'; import { renderBackgroundLayer } from 'features/controlLayers/konva/renderers/background';
import { updateBboxes } from 'features/controlLayers/konva/renderers/bbox'; import { updateBboxes } from 'features/controlLayers/konva/renderers/bbox';
@ -209,6 +209,7 @@ export const initializeRenderer = (
const getBbox = () => getState().canvasV2.bbox; const getBbox = () => getState().canvasV2.bbox;
const getDocument = () => getState().canvasV2.document; const getDocument = () => getState().canvasV2.document;
const getToolState = () => getState().canvasV2.tool; const getToolState = () => getState().canvasV2.tool;
const getSettings = () => getState().canvasV2.settings;
// Read-write state, ephemeral interaction state // Read-write state, ephemeral interaction state
let isDrawing = false; let isDrawing = false;
@ -268,6 +269,7 @@ export const initializeRenderer = (
setStageAttrs: $stageAttrs.set, setStageAttrs: $stageAttrs.set,
getDocument, getDocument,
getBbox, getBbox,
getSettings,
onBrushLineAdded, onBrushLineAdded,
onEraserLineAdded, onEraserLineAdded,
onPointAddedToLine, onPointAddedToLine,

View File

@ -184,6 +184,7 @@ export const {
allEntitiesDeleted, allEntitiesDeleted,
scaledBboxChanged, scaledBboxChanged,
bboxScaleMethodChanged, bboxScaleMethodChanged,
clipToBboxChanged,
// layers // layers
layerAdded, layerAdded,
layerRecalled, layerRecalled,

View File

@ -5,4 +5,7 @@ export const settingsReducers = {
maskOpacityChanged: (state, action: PayloadAction<number>) => { maskOpacityChanged: (state, action: PayloadAction<number>) => {
state.settings.maskOpacity = action.payload; state.settings.maskOpacity = action.payload;
}, },
clipToBboxChanged: (state, action: PayloadAction<boolean>) => {
state.settings.clipToBbox = action.payload;
},
} satisfies SliceCaseReducers<CanvasV2State>; } satisfies SliceCaseReducers<CanvasV2State>;