feat(ui): implement interaction locking on layers

This commit is contained in:
psychedelicious 2024-08-27 19:27:38 +10:00
parent 377db3f726
commit e470eaf8f3
4 changed files with 39 additions and 26 deletions

View File

@ -85,24 +85,30 @@ export class CanvasEntityLayerAdapter extends CanvasModuleABC {
update = async (arg?: { state: CanvasEntityLayerAdapter['state'] }) => { update = async (arg?: { state: CanvasEntityLayerAdapter['state'] }) => {
const state = get(arg, 'state', this.state); const state = get(arg, 'state', this.state);
if (!this.isFirstRender && state === this.state) { const prevState = this.state;
this.state = state;
if (!this.isFirstRender && prevState === state) {
this.log.trace('State unchanged, skipping update'); this.log.trace('State unchanged, skipping update');
return; return;
} }
this.log.debug('Updating'); this.log.debug('Updating');
const { position, objects, opacity, isEnabled } = state; const { position, objects, opacity, isEnabled, isLocked } = state;
if (this.isFirstRender || isEnabled !== this.state.isEnabled) { if (this.isFirstRender || isEnabled !== prevState.isEnabled) {
this.updateVisibility({ isEnabled }); this.updateVisibility({ isEnabled });
} }
if (this.isFirstRender || objects !== this.state.objects) { if (this.isFirstRender || isLocked !== prevState.isLocked) {
this.transformer.syncInteractionState();
}
if (this.isFirstRender || objects !== prevState.objects) {
await this.updateObjects({ objects }); await this.updateObjects({ objects });
} }
if (this.isFirstRender || position !== this.state.position) { if (this.isFirstRender || position !== prevState.position) {
this.transformer.updatePosition({ position }); this.transformer.updatePosition({ position });
} }
if (this.isFirstRender || opacity !== this.state.opacity) { if (this.isFirstRender || opacity !== prevState.opacity) {
this.renderer.updateOpacity(opacity); this.renderer.updateOpacity(opacity);
} }
@ -117,7 +123,6 @@ export class CanvasEntityLayerAdapter extends CanvasModuleABC {
this.transformer.updateBbox(); this.transformer.updateBbox();
} }
this.state = state;
this.isFirstRender = false; this.isFirstRender = false;
}; };

View File

@ -85,28 +85,33 @@ export class CanvasEntityMaskAdapter extends CanvasModuleABC {
update = async (arg?: { state: CanvasEntityMaskAdapter['state'] }) => { update = async (arg?: { state: CanvasEntityMaskAdapter['state'] }) => {
const state = get(arg, 'state', this.state); const state = get(arg, 'state', this.state);
if (!this.isFirstRender && state === this.state && state.fill === this.state.fill) { const prevState = this.state;
this.state = state;
if (!this.isFirstRender && prevState === state && prevState.fill === state.fill) {
this.log.trace('State unchanged, skipping update'); this.log.trace('State unchanged, skipping update');
return; return;
} }
this.log.debug('Updating'); this.log.debug('Updating');
const { position, objects, isEnabled, opacity } = state; const { position, objects, isEnabled, isLocked, opacity } = state;
if (this.isFirstRender || objects !== this.state.objects) { if (this.isFirstRender || objects !== prevState.objects) {
await this.updateObjects({ objects }); await this.updateObjects({ objects });
} }
if (this.isFirstRender || position !== this.state.position) { if (this.isFirstRender || position !== prevState.position) {
this.transformer.updatePosition({ position }); this.transformer.updatePosition({ position });
} }
if (this.isFirstRender || opacity !== this.state.opacity) { if (this.isFirstRender || opacity !== prevState.opacity) {
this.renderer.updateOpacity(opacity); this.renderer.updateOpacity(opacity);
} }
if (this.isFirstRender || isEnabled !== this.state.isEnabled) { if (this.isFirstRender || isEnabled !== prevState.isEnabled) {
this.updateVisibility({ isEnabled }); this.updateVisibility({ isEnabled });
} }
if (this.isFirstRender || isLocked !== prevState.isLocked) {
if (this.isFirstRender || state.fill !== this.state.fill) { this.transformer.syncInteractionState();
}
if (this.isFirstRender || state.fill !== prevState.fill) {
this.renderer.updateCompositingRectFill(state.fill); this.renderer.updateCompositingRectFill(state.fill);
} }
@ -118,7 +123,6 @@ export class CanvasEntityMaskAdapter extends CanvasModuleABC {
this.transformer.updateBbox(); this.transformer.updateBbox();
} }
this.state = state;
this.isFirstRender = false; this.isFirstRender = false;
}; };

View File

@ -436,7 +436,7 @@ export class CanvasEntityTransformer extends CanvasModuleABC {
const tool = this.manager.stateApi.$tool.get(); const tool = this.manager.stateApi.$tool.get();
const isSelected = this.manager.stateApi.getIsSelected(this.parent.id); const isSelected = this.manager.stateApi.getIsSelected(this.parent.id);
if (!this.parent.renderer.hasObjects()) { if (!this.parent.renderer.hasObjects() || this.parent.state.isLocked || !this.parent.state.isEnabled) {
// The layer is totally empty, we can just disable the layer // The layer is totally empty, we can just disable the layer
this.parent.konva.layer.listening(false); this.parent.konva.layer.listening(false);
this.setInteractionMode('off'); this.setInteractionMode('off');

View File

@ -244,9 +244,9 @@ export class CanvasToolModule extends CanvasModuleABC {
this.subscriptions.add(cleanupListeners); this.subscriptions.add(cleanupListeners);
} }
setToolVisibility = (tool: Tool) => { setToolVisibility = (tool: Tool, isDrawable: boolean) => {
this.konva.brush.group.visible(tool === 'brush'); this.konva.brush.group.visible(isDrawable && tool === 'brush');
this.konva.eraser.group.visible(tool === 'eraser'); this.konva.eraser.group.visible(isDrawable && tool === 'eraser');
this.konva.colorPicker.group.visible(tool === 'colorPicker'); this.konva.colorPicker.group.visible(tool === 'colorPicker');
}; };
@ -259,7 +259,11 @@ export class CanvasToolModule extends CanvasModuleABC {
const isMouseDown = this.manager.stateApi.$isMouseDown.get(); const isMouseDown = this.manager.stateApi.$isMouseDown.get();
const tool = this.manager.stateApi.$tool.get(); const tool = this.manager.stateApi.$tool.get();
const isDrawable = selectedEntity && selectedEntity.state.isEnabled && isDrawableEntity(selectedEntity.state); const isDrawable =
!!selectedEntity &&
selectedEntity.state.isEnabled &&
!selectedEntity.state.isLocked &&
isDrawableEntity(selectedEntity.state);
// Update the stage's pointer style // Update the stage's pointer style
if (Boolean(this.manager.stateApi.$transformingEntity.get()) || renderedEntityCount === 0) { if (Boolean(this.manager.stateApi.$transformingEntity.get()) || renderedEntityCount === 0) {
@ -433,7 +437,7 @@ export class CanvasToolModule extends CanvasModuleABC {
}); });
} }
this.setToolVisibility(tool); this.setToolVisibility(tool, isDrawable);
} }
}; };
@ -539,7 +543,7 @@ export class CanvasToolModule extends CanvasModuleABC {
} }
this.render(); this.render();
} else { } else {
const isDrawable = selectedEntity?.state.isEnabled; const isDrawable = selectedEntity?.state.isEnabled && !selectedEntity.state.isLocked;
if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get() && getIsPrimaryMouseDown(e)) { if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get() && getIsPrimaryMouseDown(e)) {
this.manager.stateApi.$lastMouseDownPos.set(pos); this.manager.stateApi.$lastMouseDownPos.set(pos);
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
@ -638,7 +642,7 @@ export class CanvasToolModule extends CanvasModuleABC {
this.manager.stateApi.$isMouseDown.set(false); this.manager.stateApi.$isMouseDown.set(false);
const pos = this.manager.stateApi.$lastCursorPos.get(); const pos = this.manager.stateApi.$lastCursorPos.get();
const selectedEntity = this.manager.stateApi.getSelectedEntity(); const selectedEntity = this.manager.stateApi.getSelectedEntity();
const isDrawable = selectedEntity?.state.isEnabled; const isDrawable = selectedEntity?.state.isEnabled && !selectedEntity.state.isLocked;
const tool = this.manager.stateApi.$tool.get(); const tool = this.manager.stateApi.$tool.get();
if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get()) { if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get()) {
@ -686,7 +690,7 @@ export class CanvasToolModule extends CanvasModuleABC {
this.manager.stateApi.$colorUnderCursor.set(color); this.manager.stateApi.$colorUnderCursor.set(color);
} }
} else { } else {
const isDrawable = selectedEntity?.state.isEnabled; const isDrawable = selectedEntity?.state.isEnabled && !selectedEntity.state.isLocked;
if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get() && getIsPrimaryMouseDown(e)) { if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get() && getIsPrimaryMouseDown(e)) {
if (tool === 'brush') { if (tool === 'brush') {
const drawingBuffer = selectedEntity.adapter.renderer.bufferState; const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
@ -786,7 +790,7 @@ export class CanvasToolModule extends CanvasModuleABC {
this.manager.stateApi.$lastMouseDownPos.set(null); this.manager.stateApi.$lastMouseDownPos.set(null);
const selectedEntity = this.manager.stateApi.getSelectedEntity(); const selectedEntity = this.manager.stateApi.getSelectedEntity();
const toolState = this.manager.stateApi.getToolState(); const toolState = this.manager.stateApi.getToolState();
const isDrawable = selectedEntity?.state.isEnabled; const isDrawable = selectedEntity?.state.isEnabled && !selectedEntity.state.isLocked;
const tool = this.manager.stateApi.$tool.get(); const tool = this.manager.stateApi.$tool.get();
if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get() && getIsPrimaryMouseDown(e)) { if (pos && isDrawable && !this.manager.stateApi.$spaceKey.get() && getIsPrimaryMouseDown(e)) {