feat(ui): more interaction restrictions

This commit is contained in:
psychedelicious
2024-09-13 19:01:25 +10:00
parent fbe02e3d1d
commit 3aaaae4d1c
18 changed files with 92 additions and 91 deletions

View File

@@ -1,6 +1,5 @@
import { MenuItem } from '@invoke-ai/ui-library';
import {
useIsSavingCanvas,
useSaveBboxAsControlLayer,
useSaveBboxAsGlobalIPAdapter,
useSaveBboxAsRasterLayer,
@@ -8,13 +7,14 @@ import {
useSaveBboxToGallery,
useSaveCanvasToGallery,
} from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFloppyDiskBold, PiShareFatBold } from 'react-icons/pi';
export const CanvasContextMenuItems = memo(() => {
const { t } = useTranslation();
const isSaving = useIsSavingCanvas();
const isBusy = useCanvasIsBusy();
const saveCanvasToGallery = useSaveCanvasToGallery();
const saveBboxToGallery = useSaveBboxToGallery();
const saveBboxAsRegionalGuidanceIPAdapter = useSaveBboxAsRegionalGuidanceIPAdapter();
@@ -24,22 +24,22 @@ export const CanvasContextMenuItems = memo(() => {
return (
<>
<MenuItem icon={<PiFloppyDiskBold />} isLoading={isSaving.isTrue} onClick={saveCanvasToGallery}>
<MenuItem icon={<PiFloppyDiskBold />} isDisabled={isBusy} onClick={saveCanvasToGallery}>
{t('controlLayers.saveCanvasToGallery')}
</MenuItem>
<MenuItem icon={<PiFloppyDiskBold />} isLoading={isSaving.isTrue} onClick={saveBboxToGallery}>
<MenuItem icon={<PiFloppyDiskBold />} isDisabled={isBusy} onClick={saveBboxToGallery}>
{t('controlLayers.saveBboxToGallery')}
</MenuItem>
<MenuItem icon={<PiShareFatBold />} isLoading={isSaving.isTrue} onClick={saveBboxAsIPAdapter}>
<MenuItem icon={<PiShareFatBold />} isDisabled={isBusy} onClick={saveBboxAsIPAdapter}>
{t('controlLayers.sendBboxToGlobalIPAdapter')}
</MenuItem>
<MenuItem icon={<PiShareFatBold />} isLoading={isSaving.isTrue} onClick={saveBboxAsRegionalGuidanceIPAdapter}>
<MenuItem icon={<PiShareFatBold />} isDisabled={isBusy} onClick={saveBboxAsRegionalGuidanceIPAdapter}>
{t('controlLayers.sendBboxToRegionalIPAdapter')}
</MenuItem>
<MenuItem icon={<PiShareFatBold />} isLoading={isSaving.isTrue} onClick={saveBboxAsControlLayer}>
<MenuItem icon={<PiShareFatBold />} isDisabled={isBusy} onClick={saveBboxAsControlLayer}>
{t('controlLayers.sendBboxToControlLayer')}
</MenuItem>
<MenuItem icon={<PiShareFatBold />} isLoading={isSaving.isTrue} onClick={saveBboxAsRasterLayer}>
<MenuItem icon={<PiShareFatBold />} isDisabled={isBusy} onClick={saveBboxAsRasterLayer}>
{t('controlLayers.sendBboxToRasterLayer')}
</MenuItem>
</>

View File

@@ -1,5 +1,6 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { entityDuplicated } from 'features/controlLayers/store/canvasSlice';
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
@@ -9,6 +10,7 @@ import { PiCopyFill } from 'react-icons/pi';
export const EntityListSelectedEntityActionBarDuplicateButton = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isBusy = useCanvasIsBusy();
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
const onClick = useCallback(() => {
if (!selectedEntityIdentifier) {
@@ -20,7 +22,7 @@ export const EntityListSelectedEntityActionBarDuplicateButton = memo(() => {
return (
<IconButton
onClick={onClick}
isDisabled={!selectedEntityIdentifier}
isDisabled={!selectedEntityIdentifier || isBusy}
size="sm"
variant="link"
alignSelf="stretch"

View File

@@ -7,7 +7,8 @@ import { Weight } from 'features/controlLayers/components/common/Weight';
import { ControlLayerControlAdapterControlMode } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode';
import { ControlLayerControlAdapterModel } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { useIsSavingCanvas, usePullBboxIntoLayer } from 'features/controlLayers/hooks/saveCanvasHooks';
import { usePullBboxIntoLayer } from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { useEntityFilter } from 'features/controlLayers/hooks/useEntityFilter';
import {
controlLayerBeginEndStepPctChanged,
@@ -71,7 +72,7 @@ export const ControlLayerControlAdapter = memo(() => {
);
const pullBboxIntoLayer = usePullBboxIntoLayer(entityIdentifier);
const isSaving = useIsSavingCanvas();
const isBusy = useCanvasIsBusy();
const postUploadAction = useMemo<PostUploadAction>(
() => ({ type: 'REPLACE_LAYER_WITH_IMAGE', entityIdentifier }),
[entityIdentifier]
@@ -94,7 +95,7 @@ export const ControlLayerControlAdapter = memo(() => {
/>
<IconButton
onClick={pullBboxIntoLayer}
isLoading={isSaving.isTrue}
isDisabled={isBusy}
size="sm"
alignSelf="stretch"
variant="link"
@@ -103,7 +104,7 @@ export const ControlLayerControlAdapter = memo(() => {
icon={<PiBoundingBoxBold />}
/>
<IconButton
isLoading={isSaving.isTrue}
isDisabled={isBusy}
size="sm"
alignSelf="stretch"
variant="link"

View File

@@ -6,7 +6,8 @@ import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/c
import { Weight } from 'features/controlLayers/components/common/Weight';
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { useIsSavingCanvas, usePullBboxIntoIPAdapter } from 'features/controlLayers/hooks/saveCanvasHooks';
import { usePullBboxIntoIPAdapter } from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import {
ipaBeginEndStepPctChanged,
ipaCLIPVisionModelChanged,
@@ -87,7 +88,7 @@ export const IPAdapterSettings = memo(() => {
[entityIdentifier.id]
);
const pullBboxIntoIPAdapter = usePullBboxIntoIPAdapter(entityIdentifier);
const isSaving = useIsSavingCanvas();
const isBusy = useCanvasIsBusy();
return (
<CanvasEntitySettingsWrapper>
@@ -103,7 +104,7 @@ export const IPAdapterSettings = memo(() => {
</Box>
<IconButton
onClick={pullBboxIntoIPAdapter}
isLoading={isSaving.isTrue}
isDisabled={isBusy}
variant="ghost"
aria-label={t('controlLayers.pullBboxIntoIPAdapter')}
tooltip={t('controlLayers.pullBboxIntoIPAdapter')}

View File

@@ -7,10 +7,8 @@ import { IPAdapterImagePreview } from 'features/controlLayers/components/IPAdapt
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
import { IPAdapterModel } from 'features/controlLayers/components/IPAdapter/IPAdapterModel';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import {
useIsSavingCanvas,
usePullBboxIntoRegionalGuidanceIPAdapter,
} from 'features/controlLayers/hooks/saveCanvasHooks';
import { usePullBboxIntoRegionalGuidanceIPAdapter } from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import {
rgIPAdapterBeginEndStepPctChanged,
rgIPAdapterCLIPVisionModelChanged,
@@ -107,7 +105,7 @@ export const RegionalGuidanceIPAdapterSettings = memo(({ ipAdapterId, ipAdapterN
[entityIdentifier.id, ipAdapterId]
);
const pullBboxIntoIPAdapter = usePullBboxIntoRegionalGuidanceIPAdapter(entityIdentifier, ipAdapterId);
const isSaving = useIsSavingCanvas();
const isBusy = useCanvasIsBusy();
return (
<Flex flexDir="column" gap={3}>
@@ -135,7 +133,7 @@ export const RegionalGuidanceIPAdapterSettings = memo(({ ipAdapterId, ipAdapterN
</Box>
<IconButton
onClick={pullBboxIntoIPAdapter}
isLoading={isSaving.isTrue}
isDisabled={isBusy}
variant="ghost"
aria-label={t('controlLayers.pullBboxIntoIPAdapter')}
tooltip={t('controlLayers.pullBboxIntoIPAdapter')}

View File

@@ -1,5 +1,6 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowsOut } from 'react-icons/pi';
@@ -7,6 +8,7 @@ import { PiArrowsOut } from 'react-icons/pi';
export const CanvasToolbarFitBboxToLayersButton = memo(() => {
const { t } = useTranslation();
const canvasManager = useCanvasManager();
const isBusy = useCanvasIsBusy();
const onClick = useCallback(() => {
canvasManager.bbox.fitToLayers();
}, [canvasManager.bbox]);
@@ -18,6 +20,7 @@ export const CanvasToolbarFitBboxToLayersButton = memo(() => {
aria-label={t('controlLayers.fitBboxToLayers')}
tooltip={t('controlLayers.fitBboxToLayers')}
icon={<PiArrowsOut />}
isDisabled={isBusy}
/>
);
});

View File

@@ -1,9 +1,6 @@
import { IconButton, useShiftModifier } from '@invoke-ai/ui-library';
import {
useIsSavingCanvas,
useSaveBboxToGallery,
useSaveCanvasToGallery,
} from 'features/controlLayers/hooks/saveCanvasHooks';
import { useSaveBboxToGallery, useSaveCanvasToGallery } from 'features/controlLayers/hooks/saveCanvasHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFloppyDiskBold } from 'react-icons/pi';
@@ -11,7 +8,7 @@ import { PiFloppyDiskBold } from 'react-icons/pi';
export const CanvasToolbarSaveToGalleryButton = memo(() => {
const { t } = useTranslation();
const shift = useShiftModifier();
const isSaving = useIsSavingCanvas();
const isBusy = useCanvasIsBusy();
const saveCanvasToGallery = useSaveCanvasToGallery();
const saveBboxToGallery = useSaveBboxToGallery();
@@ -20,9 +17,9 @@ export const CanvasToolbarSaveToGalleryButton = memo(() => {
variant="ghost"
onClick={shift ? saveBboxToGallery : saveCanvasToGallery}
icon={<PiFloppyDiskBold />}
isLoading={isSaving.isTrue}
aria-label={shift ? t('controlLayers.saveBboxToGallery') : t('controlLayers.saveCanvasToGallery')}
tooltip={shift ? t('controlLayers.saveBboxToGallery') : t('controlLayers.saveCanvasToGallery')}
isDisabled={isBusy}
/>
);
});

View File

@@ -6,6 +6,7 @@ import {
useAddRasterLayer,
useAddRegionalGuidance,
} from 'features/controlLayers/hooks/addLayerHooks';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,6 +18,7 @@ type Props = {
export const CanvasEntityAddOfTypeButton = memo(({ type }: Props) => {
const { t } = useTranslation();
const isBusy = useCanvasIsBusy();
const addInpaintMask = useAddInpaintMask();
const addRegionalGuidance = useAddRegionalGuidance();
const addRasterLayer = useAddRasterLayer();
@@ -67,6 +69,7 @@ export const CanvasEntityAddOfTypeButton = memo(({ type }: Props) => {
icon={<PiPlusBold />}
onClick={onClick}
alignSelf="stretch"
isDisabled={isBusy}
/>
);
});

View File

@@ -1,6 +1,7 @@
import { IconButton } 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,6 +11,7 @@ export const CanvasEntityDeleteButton = memo(() => {
const { t } = useTranslation();
const entityIdentifier = useEntityIdentifierContext();
const dispatch = useAppDispatch();
const isBusy = useCanvasIsBusy();
const onClick = useCallback(() => {
dispatch(entityDeleted({ entityIdentifier }));
}, [dispatch, entityIdentifier]);
@@ -24,6 +26,7 @@ export const CanvasEntityDeleteButton = memo(() => {
icon={<PiTrashSimpleFill />}
onClick={onClick}
colorScheme="error"
isDisabled={isBusy}
/>
);
});

View File

@@ -1,6 +1,7 @@
import { IconButton } 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 { useEntityIsEnabled } from 'features/controlLayers/hooks/useEntityIsEnabled';
import { entityIsEnabledToggled } from 'features/controlLayers/store/canvasSlice';
import { memo, useCallback } from 'react';
@@ -11,6 +12,7 @@ export const CanvasEntityEnabledToggle = memo(() => {
const { t } = useTranslation();
const entityIdentifier = useEntityIdentifierContext();
const isEnabled = useEntityIsEnabled(entityIdentifier);
const isBusy = useCanvasIsBusy();
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
dispatch(entityIsEnabledToggled({ entityIdentifier }));
@@ -25,6 +27,7 @@ export const CanvasEntityEnabledToggle = memo(() => {
alignSelf="stretch"
icon={isEnabled ? <PiCircleFill /> : <PiCircleBold />}
onClick={onClick}
isDisabled={isBusy}
/>
);
});

View File

@@ -1,6 +1,7 @@
import { IconButton } 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 { useEntityIsLocked } from 'features/controlLayers/hooks/useEntityIsLocked';
import { entityIsLockedToggled } from 'features/controlLayers/store/canvasSlice';
import { memo, useCallback } from 'react';
@@ -11,6 +12,7 @@ export const CanvasEntityIsLockedToggle = memo(() => {
const { t } = useTranslation();
const entityIdentifier = useEntityIdentifierContext();
const isLocked = useEntityIsLocked(entityIdentifier);
const isBusy = useCanvasIsBusy();
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
dispatch(entityIsLockedToggled({ entityIdentifier }));
@@ -25,6 +27,7 @@ export const CanvasEntityIsLockedToggle = memo(() => {
alignSelf="stretch"
icon={isLocked ? <PiLockSimpleFill /> : <PiLockSimpleOpenBold />}
onClick={onClick}
isDisabled={isBusy}
/>
);
});

View File

@@ -2,7 +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 { useIsEntityInteractable } from 'features/controlLayers/hooks/useEntityIsInteractable';
import {
entityArrangedBackwardOne,
entityArrangedForwardOne,
@@ -56,7 +56,7 @@ export const CanvasEntityMenuItemsArrange = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const entityIdentifier = useEntityIdentifierContext();
const isBusy = useCanvasIsBusy();
const isInteractable = useIsEntityInteractable(entityIdentifier);
const selectValidActions = useMemo(
() =>
createMemoizedSelector(selectCanvasSlice, (canvas) => {
@@ -88,24 +88,32 @@ export const CanvasEntityMenuItemsArrange = memo(() => {
return (
<>
<MenuItem onClick={moveToFront} isDisabled={!validActions.canMoveToFront || isBusy} icon={<PiArrowLineUpBold />}>
<MenuItem
onClick={moveToFront}
isDisabled={!validActions.canMoveToFront || !isInteractable}
icon={<PiArrowLineUpBold />}
>
{t('controlLayers.moveToFront')}
</MenuItem>
<MenuItem
onClick={moveForwardOne}
isDisabled={!validActions.canMoveForwardOne || isBusy}
isDisabled={!validActions.canMoveForwardOne || !isInteractable}
icon={<PiArrowUpBold />}
>
{t('controlLayers.moveForward')}
</MenuItem>
<MenuItem
onClick={moveBackwardOne}
isDisabled={!validActions.canMoveBackwardOne || isBusy}
isDisabled={!validActions.canMoveBackwardOne || !isInteractable}
icon={<PiArrowDownBold />}
>
{t('controlLayers.moveBackward')}
</MenuItem>
<MenuItem onClick={moveToBack} isDisabled={!validActions.canMoveToBack || isBusy} icon={<PiArrowLineDownBold />}>
<MenuItem
onClick={moveToBack}
isDisabled={!validActions.canMoveToBack || !isInteractable}
icon={<PiArrowLineDownBold />}
>
{t('controlLayers.moveToBack')}
</MenuItem>
</>

View File

@@ -1,6 +1,5 @@
import { logger } from 'app/logging/logger';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { buildUseBoolean } from 'common/hooks/useBoolean';
import { isOk, withResultAsync } from 'common/util/result';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { selectDefaultControlAdapter, selectDefaultIPAdapter } from 'features/controlLayers/hooks/addLayerHooks';
@@ -32,8 +31,6 @@ import type { ImageDTO } from 'services/api/types';
const log = logger('canvas');
export const [useIsSavingCanvas] = buildUseBoolean(false);
type UseSaveCanvasArg = {
region: 'canvas' | 'bbox';
saveToGallery: boolean;
@@ -44,11 +41,8 @@ const useSaveCanvas = ({ region, saveToGallery, onSave }: UseSaveCanvasArg) => {
const { t } = useTranslation();
const canvasManager = useCanvasManager();
const isSaving = useIsSavingCanvas();
const saveCanvas = useCallback(async () => {
isSaving.setTrue();
const rect =
region === 'bbox' ? canvasManager.stateApi.getBbox().rect : canvasManager.stage.getVisibleRect('raster_layer');
@@ -65,18 +59,7 @@ const useSaveCanvas = ({ region, saveToGallery, onSave }: UseSaveCanvasArg) => {
log.error({ error: serializeError(result.error) }, 'Failed to save canvas to gallery');
toast({ title: t('controlLayers.savedToGalleryError'), status: 'error' });
}
isSaving.setFalse();
}, [
canvasManager.compositor,
canvasManager.stage,
canvasManager.stateApi,
isSaving,
onSave,
region,
saveToGallery,
t,
]);
}, [canvasManager.compositor, canvasManager.stage, canvasManager.stateApi, onSave, region, saveToGallery, t]);
return saveCanvas;
};

View File

@@ -15,6 +15,7 @@ export const useEntityFilter = (entityIdentifier: CanvasEntityIdentifier | null)
const adapter = useEntityAdapterSafe(entityIdentifier);
const isBusy = useCanvasIsBusy();
const isInteractable = useStore(adapter?.$isInteractable ?? $fallbackFalse);
const isEmpty = useStore(adapter?.$isEmpty ?? $fallbackFalse);
const isDisabled = useMemo(() => {
if (!entityIdentifier) {
@@ -32,8 +33,11 @@ export const useEntityFilter = (entityIdentifier: CanvasEntityIdentifier | null)
if (!isInteractable) {
return true;
}
if (isEmpty) {
return true;
}
return false;
}, [entityIdentifier, adapter, isBusy, isInteractable]);
}, [entityIdentifier, adapter, isBusy, isInteractable, isEmpty]);
const start = useCallback(() => {
if (isDisabled) {

View File

@@ -15,20 +15,7 @@ export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | nu
const adapter = useEntityAdapterSafe(entityIdentifier);
const isBusy = useCanvasIsBusy();
const isInteractable = useStore(adapter?.$isInteractable ?? $fallbackFalse);
const start = useCallback(() => {
if (!entityIdentifier) {
return;
}
if (!isTransformableEntityIdentifier(entityIdentifier)) {
return;
}
const adapter = canvasManager.getAdapter(entityIdentifier);
if (!adapter) {
return;
}
adapter.transformer.startTransform();
}, [entityIdentifier, canvasManager]);
const isEmpty = useStore(adapter?.$isEmpty ?? $fallbackFalse);
const isDisabled = useMemo(() => {
if (!entityIdentifier) {
@@ -46,8 +33,28 @@ export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | nu
if (!isInteractable) {
return true;
}
if (isEmpty) {
return true;
}
return false;
}, [entityIdentifier, adapter, isBusy, isInteractable]);
}, [entityIdentifier, adapter, isBusy, isInteractable, isEmpty]);
const start = useCallback(() => {
if (isDisabled) {
return;
}
if (!entityIdentifier) {
return;
}
if (!isTransformableEntityIdentifier(entityIdentifier)) {
return;
}
const adapter = canvasManager.getAdapter(entityIdentifier);
if (!adapter) {
return;
}
adapter.transformer.startTransform();
}, [isDisabled, entityIdentifier, canvasManager]);
return { isDisabled, start } as const;
};

View File

@@ -106,18 +106,15 @@ export abstract class CanvasEntityAdapterBase<
*/
$isHidden = atom(false);
/**
* Whether this entity has objects. This is computed based on the entity's objects.
* Whether this entity is empty. This is computed based on the entity's objects.
*/
$hasObjects = atom(false);
$isEmpty = atom(true);
/**
* Whether this entity is interactable. This is computed based on the entity's locked, disabled, and hidden states.
*/
$isInteractable = computed(
[this.$isLocked, this.$isDisabled, this.$isHidden, this.$hasObjects],
(isLocked, isDisabled, isHidden, hasObjects) => {
return !isLocked && !isDisabled && !isHidden && hasObjects;
}
);
$isInteractable = computed([this.$isLocked, this.$isDisabled, this.$isHidden], (isLocked, isDisabled, isHidden) => {
return !isLocked && !isDisabled && !isHidden;
});
constructor(entityIdentifier: CanvasEntityIdentifier<T['type']>, manager: CanvasManager, adapterType: U) {
super();
@@ -205,7 +202,7 @@ export abstract class CanvasEntityAdapterBase<
* Synchronizes the entity's objects with the canvas.
*/
syncObjects = async () => {
this.$hasObjects.set(this.state.objects.length > 0);
this.$isEmpty.set(this.state.objects.length === 0);
const didRender = await this.renderer.render();
if (didRender) {
// If the objects have changed, we need to recalculate the transformer's bounding box.

View File

@@ -325,18 +325,6 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
return didRender;
};
hideObjects = (except: string[] = []) => {
for (const renderer of this.renderers.values()) {
renderer.setVisibility(except.includes(renderer.id));
}
};
showObjects = (except: string[] = []) => {
for (const renderer of this.renderers.values()) {
renderer.setVisibility(!except.includes(renderer.id));
}
};
/**
* Determines if the objects in the renderer require a pixel bbox calculation.
*

View File

@@ -487,7 +487,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
const tool = this.manager.tool.$tool.get();
const isSelected = this.manager.stateApi.getIsSelected(this.parent.id);
if (!this.parent.$isInteractable.get()) {
if (this.parent.$isEmpty.get()) {
// The layer is totally empty, we can just disable the layer
this.parent.konva.layer.listening(false);
this._setInteractionMode('off');