mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): prevent layer interactions when transforming or filtering
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { controlLayerConvertedToRasterLayer } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,6 +10,7 @@ import { PiLightningBold } from 'react-icons/pi';
|
||||
export const ControlLayerMenuItemsControlToRaster = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isBusy = useCanvasIsBusy();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
|
||||
const convertControlLayerToRasterLayer = useCallback(() => {
|
||||
@@ -16,7 +18,7 @@ export const ControlLayerMenuItemsControlToRaster = memo(() => {
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={convertControlLayerToRasterLayer} icon={<PiLightningBold />}>
|
||||
<MenuItem onClick={convertControlLayerToRasterLayer} icon={<PiLightningBold />} isDisabled={isBusy}>
|
||||
{t('controlLayers.convertToRasterLayer')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { rasterLayerConvertedToControlLayer } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,13 +11,14 @@ export const RasterLayerMenuItemsRasterToControl = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext('raster_layer');
|
||||
const isBusy = useCanvasIsBusy();
|
||||
|
||||
const convertRasterLayerToControlLayer = useCallback(() => {
|
||||
dispatch(rasterLayerConvertedToControlLayer({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={convertRasterLayerToControlLayer} icon={<PiLightningBold />}>
|
||||
<MenuItem onClick={convertRasterLayerToControlLayer} icon={<PiLightningBold />} isDisabled={isBusy}>
|
||||
{t('controlLayers.convertToControlLayer')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
@@ -15,6 +16,7 @@ export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isBusy = useCanvasIsBusy();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
@@ -39,13 +41,15 @@ export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem onClick={addPositivePrompt} isDisabled={!validActions.canAddPositivePrompt}>
|
||||
<MenuItem onClick={addPositivePrompt} isDisabled={!validActions.canAddPositivePrompt || isBusy}>
|
||||
{t('controlLayers.addPositivePrompt')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={addNegativePrompt} isDisabled={!validActions.canAddNegativePrompt}>
|
||||
<MenuItem onClick={addNegativePrompt} isDisabled={!validActions.canAddNegativePrompt || isBusy}>
|
||||
{t('controlLayers.addNegativePrompt')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={addIPAdapter}>{t('controlLayers.addIPAdapter')}</MenuItem>
|
||||
<MenuItem onClick={addIPAdapter} isDisabled={isBusy}>
|
||||
{t('controlLayers.addIPAdapter')}
|
||||
</MenuItem>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import {
|
||||
entityArrangedBackwardOne,
|
||||
entityArrangedForwardOne,
|
||||
@@ -55,6 +56,7 @@ export const CanvasEntityMenuItemsArrange = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const isBusy = useCanvasIsBusy();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
@@ -86,16 +88,24 @@ export const CanvasEntityMenuItemsArrange = memo(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem onClick={moveToFront} isDisabled={!validActions.canMoveToFront} icon={<PiArrowLineUpBold />}>
|
||||
<MenuItem onClick={moveToFront} isDisabled={!validActions.canMoveToFront || isBusy} icon={<PiArrowLineUpBold />}>
|
||||
{t('controlLayers.moveToFront')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={moveForwardOne} isDisabled={!validActions.canMoveForwardOne} icon={<PiArrowUpBold />}>
|
||||
<MenuItem
|
||||
onClick={moveForwardOne}
|
||||
isDisabled={!validActions.canMoveForwardOne || isBusy}
|
||||
icon={<PiArrowUpBold />}
|
||||
>
|
||||
{t('controlLayers.moveForward')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={moveBackwardOne} isDisabled={!validActions.canMoveBackwardOne} icon={<PiArrowDownBold />}>
|
||||
<MenuItem
|
||||
onClick={moveBackwardOne}
|
||||
isDisabled={!validActions.canMoveBackwardOne || isBusy}
|
||||
icon={<PiArrowDownBold />}
|
||||
>
|
||||
{t('controlLayers.moveBackward')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={moveToBack} isDisabled={!validActions.canMoveToBack} icon={<PiArrowLineDownBold />}>
|
||||
<MenuItem onClick={moveToBack} isDisabled={!validActions.canMoveToBack || isBusy} icon={<PiArrowLineDownBold />}>
|
||||
{t('controlLayers.moveToBack')}
|
||||
</MenuItem>
|
||||
</>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,13 +11,14 @@ export const CanvasEntityMenuItemsDelete = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const isBusy = useCanvasIsBusy();
|
||||
|
||||
const deleteEntity = useCallback(() => {
|
||||
dispatch(entityDeleted({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={deleteEntity} icon={<PiTrashSimpleBold />} color="error.300">
|
||||
<MenuItem onClick={deleteEntity} icon={<PiTrashSimpleBold />} isDestructive isDisabled={isBusy}>
|
||||
{t('common.delete')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { entityDuplicated } from 'features/controlLayers/store/canvasSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,13 +11,14 @@ export const CanvasEntityMenuItemsDuplicate = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const isBusy = useCanvasIsBusy();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(entityDuplicated({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={onClick} icon={<PiCopyFill />}>
|
||||
<MenuItem onClick={onClick} icon={<PiCopyFill />} isDisabled={isBusy}>
|
||||
{t('controlLayers.duplicate')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiShootingStarBold } from 'react-icons/pi';
|
||||
@@ -9,13 +10,14 @@ export const CanvasEntityMenuItemsFilter = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const canvasManager = useCanvasManager();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const isBusy = useCanvasIsBusy();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
canvasManager.filter.initialize(entityIdentifier);
|
||||
}, [entityIdentifier, canvasManager.filter]);
|
||||
}, [canvasManager.filter, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={onClick} icon={<PiShootingStarBold />}>
|
||||
<MenuItem onClick={onClick} icon={<PiShootingStarBold />} isDisabled={isBusy}>
|
||||
{t('controlLayers.filter.filter')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
||||
import { useEntityAdapter } from 'features/controlLayers/hooks/useEntityAdapter';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,16 +9,15 @@ import { PiFrameCornersBold } from 'react-icons/pi';
|
||||
export const CanvasEntityMenuItemsTransform = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const canvasManager = useCanvasManager();
|
||||
const adapter = useEntityAdapter(entityIdentifier);
|
||||
const isTransforming = useStore(canvasManager.stateApi.$isTranforming);
|
||||
const isBusy = useCanvasIsBusy();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
adapter.transformer.startTransform();
|
||||
}, [adapter.transformer]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={onClick} icon={<PiFrameCornersBold />} isDisabled={isTransforming}>
|
||||
<MenuItem onClick={onClick} icon={<PiFrameCornersBold />} isDisabled={isBusy}>
|
||||
{t('controlLayers.transform.transform')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
|
||||
export const useCanvasIsBusy = () => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const isBusy = useStore(canvasManager.$isBusy);
|
||||
|
||||
return isBusy;
|
||||
};
|
||||
@@ -126,7 +126,8 @@ export class CanvasBboxModule extends CanvasModuleBase {
|
||||
this.konva.group.visible(true);
|
||||
|
||||
// We need to reach up to the preview layer to enable/disable listening so that the bbox can be interacted with.
|
||||
this.manager.konva.previewLayer.listening(tool === 'bbox');
|
||||
// If the mangaer is busy, we disable listening so the bbox cannot be interacted with.
|
||||
this.manager.konva.previewLayer.listening(tool === 'bbox' && !this.manager.$isBusy.get());
|
||||
|
||||
this.konva.proxyRect.setAttrs({
|
||||
x,
|
||||
|
||||
@@ -207,6 +207,10 @@ export class CanvasEntityLayerAdapter extends CanvasModuleBase {
|
||||
return null;
|
||||
};
|
||||
|
||||
isInteractable = (): boolean => {
|
||||
return this.state.isEnabled && !this.state.isLocked;
|
||||
};
|
||||
|
||||
destroy = (): void => {
|
||||
this.log.debug('Destroying module');
|
||||
this.renderer.destroy();
|
||||
|
||||
@@ -170,6 +170,10 @@ export class CanvasEntityMaskAdapter extends CanvasModuleBase {
|
||||
return canvas;
|
||||
};
|
||||
|
||||
isInteractable = (): boolean => {
|
||||
return this.state.isEnabled && !this.state.isLocked;
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.debug('Destroying module');
|
||||
this.transformer.destroy();
|
||||
|
||||
@@ -500,6 +500,13 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
syncInteractionState = () => {
|
||||
this.log.trace('Syncing interaction state');
|
||||
|
||||
if (this.manager.$isBusy.get()) {
|
||||
// If the canvas is busy, we can't interact with the transformer
|
||||
this.parent.konva.layer.listening(false);
|
||||
this.setInteractionMode('off');
|
||||
return;
|
||||
}
|
||||
|
||||
const pixelRect = this.$pixelRect.get();
|
||||
const isPendingRectCalculation = this.$isPendingRectCalculation.get();
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import { CanvasToolModule } from 'features/controlLayers/konva/CanvasToolModule'
|
||||
import { CanvasWorkerModule } from 'features/controlLayers/konva/CanvasWorkerModule.js';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import Konva from 'konva';
|
||||
import { atom } from 'nanostores';
|
||||
import type { Atom } from 'nanostores';
|
||||
import { atom, computed } from 'nanostores';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
import { CanvasBackgroundModule } from './CanvasBackgroundModule';
|
||||
@@ -73,6 +74,11 @@ export class CanvasManager extends CanvasModuleBase {
|
||||
|
||||
_isDebugging: boolean = false;
|
||||
|
||||
/**
|
||||
* Whether the canvas is currently busy with a transformation or filtering operation.
|
||||
*/
|
||||
$isBusy: Atom<boolean>;
|
||||
|
||||
constructor(stage: Konva.Stage, container: HTMLDivElement, store: AppStore, socket: AppSocket) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
@@ -120,6 +126,10 @@ export class CanvasManager extends CanvasModuleBase {
|
||||
this.konva.previewLayer.add(this.progressImage.konva.group);
|
||||
this.konva.previewLayer.add(this.bbox.konva.group);
|
||||
this.konva.previewLayer.add(this.tool.konva.group);
|
||||
|
||||
this.$isBusy = computed([this.filter.$isFiltering, this.stateApi.$isTranforming], (isFiltering, isTransforming) => {
|
||||
return isFiltering || isTransforming;
|
||||
});
|
||||
}
|
||||
|
||||
enableDebugging() {
|
||||
|
||||
@@ -71,6 +71,7 @@ export class CanvasRenderingModule extends CanvasModuleBase {
|
||||
await this.renderInpaintMasks(state, prevState);
|
||||
await this.renderBbox(state, prevState);
|
||||
this.arrangeEntities(state, prevState);
|
||||
this.manager.tool.syncCursorStyle();
|
||||
};
|
||||
|
||||
renderSettings = () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Property } from 'csstype';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { getPrefixedId, getRectUnion } from 'features/controlLayers/konva/util';
|
||||
@@ -306,6 +307,10 @@ export class CanvasStageModule extends CanvasModuleBase {
|
||||
return pixels / this.getScale();
|
||||
};
|
||||
|
||||
setCursor = (cursor: Property.Cursor) => {
|
||||
this.container.style.cursor = cursor;
|
||||
};
|
||||
|
||||
setIsDraggable = (isDraggable: boolean) => {
|
||||
this.konva.stage.draggable(isDraggable);
|
||||
};
|
||||
|
||||
@@ -143,46 +143,29 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
syncCursorStyle = () => {
|
||||
this.log.trace('Syncing cursor style');
|
||||
const stage = this.manager.stage;
|
||||
const renderedEntityCount = this.manager.stateApi.getRenderedEntityCount();
|
||||
const selectedEntity = this.manager.stateApi.getSelectedEntity();
|
||||
const isMouseDown = this.$isMouseDown.get();
|
||||
const tool = this.$tool.get();
|
||||
|
||||
const isDrawable =
|
||||
!!selectedEntity &&
|
||||
selectedEntity.state.isEnabled &&
|
||||
!selectedEntity.state.isLocked &&
|
||||
isDrawableEntity(selectedEntity.state);
|
||||
|
||||
// Update the stage's pointer style
|
||||
if (this.manager.stateApi.$isTranforming.get() || renderedEntityCount === 0) {
|
||||
// We are transforming and/or have no layers, so we should not render any tool
|
||||
stage.container.style.cursor = 'default';
|
||||
} else if (tool === 'view') {
|
||||
// view tool gets a hand
|
||||
stage.container.style.cursor = isMouseDown ? 'grabbing' : 'grab';
|
||||
// Bbox tool gets default
|
||||
} else if (tool === 'bbox') {
|
||||
stage.container.style.cursor = 'default';
|
||||
} else if (tool === 'colorPicker') {
|
||||
// Color picker gets none
|
||||
stage.container.style.cursor = 'none';
|
||||
} else if (isDrawable) {
|
||||
if (tool === 'move') {
|
||||
// Move gets default arrow
|
||||
stage.container.style.cursor = 'default';
|
||||
} else if (tool === 'rect') {
|
||||
// Rect gets a crosshair
|
||||
stage.container.style.cursor = 'crosshair';
|
||||
} else if (tool === 'brush' || tool === 'eraser') {
|
||||
// Hide the native cursor and use the konva-rendered brush preview
|
||||
stage.container.style.cursor = 'none';
|
||||
}
|
||||
if (tool === 'view') {
|
||||
stage.setCursor(isMouseDown ? 'grabbing' : 'grab');
|
||||
} else if (this.manager.stateApi.getRenderedEntityCount() === 0) {
|
||||
stage.setCursor('not-allowed');
|
||||
} else if (this.manager.stateApi.$isTranforming.get()) {
|
||||
stage.setCursor('not-allowed');
|
||||
} else if (this.manager.filter.$isFiltering.get()) {
|
||||
stage.setCursor('not-allowed');
|
||||
} else if (!this.manager.stateApi.getSelectedEntity()?.adapter.isInteractable()) {
|
||||
stage.setCursor('not-allowed');
|
||||
} else if (tool === 'colorPicker' || tool === 'brush' || tool === 'eraser') {
|
||||
stage.setCursor('none');
|
||||
} else if (tool === 'move' || tool === 'bbox') {
|
||||
stage.setCursor('default');
|
||||
} else if (tool === 'rect') {
|
||||
stage.setCursor('crosshair');
|
||||
} else {
|
||||
// isDrawable === 'false'
|
||||
// Non-drawable layers don't have tools
|
||||
stage.container.style.cursor = 'not-allowed';
|
||||
stage.setCursor('not-allowed');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -302,7 +285,25 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
};
|
||||
|
||||
getCanDraw = (): boolean => {
|
||||
if (this.manager.stateApi.getRenderedEntityCount() === 0) {
|
||||
return false;
|
||||
} else if (this.manager.stateApi.$isTranforming.get()) {
|
||||
return false;
|
||||
} else if (this.manager.filter.$isFiltering.get()) {
|
||||
return false;
|
||||
} else if (!this.manager.stateApi.getSelectedEntity()?.adapter.isInteractable()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
onStageMouseEnter = async (_: KonvaEventObject<MouseEvent>) => {
|
||||
if (!this.getCanDraw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cursorPos = this.syncLastCursorPos();
|
||||
try {
|
||||
const isMouseDown = this.$isMouseDown.get();
|
||||
@@ -356,6 +357,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
onStageMouseDown = async (e: KonvaEventObject<MouseEvent>) => {
|
||||
if (!this.getCanDraw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$isMouseDown.set(getIsPrimaryMouseDown(e));
|
||||
const cursorPos = this.syncLastCursorPos();
|
||||
|
||||
@@ -473,6 +478,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
onStageMouseUp = (_: KonvaEventObject<MouseEvent>) => {
|
||||
if (!this.getCanDraw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.$isMouseDown.set(false);
|
||||
const cursorPos = this.syncLastCursorPos();
|
||||
@@ -516,6 +525,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
onStageMouseMove = async (_: KonvaEventObject<MouseEvent>) => {
|
||||
if (!this.getCanDraw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tool = this.$tool.get();
|
||||
const cursorPos = this.syncLastCursorPos();
|
||||
@@ -595,6 +608,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
onStageMouseLeave = (_: KonvaEventObject<MouseEvent>) => {
|
||||
if (!this.getCanDraw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$lastCursorPos.set(null);
|
||||
this.$lastMouseDownPos.set(null);
|
||||
const selectedEntity = this.manager.stateApi.getSelectedEntity();
|
||||
@@ -607,6 +624,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
};
|
||||
|
||||
onStageMouseWheel = (e: KonvaEventObject<WheelEvent>) => {
|
||||
if (!this.getCanDraw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.evt.preventDefault();
|
||||
|
||||
if (!e.evt.ctrlKey && !e.evt.metaKey) {
|
||||
|
||||
Reference in New Issue
Block a user