fix(ui): do not allow drawing if layer disabled

This commit is contained in:
psychedelicious 2024-08-20 13:12:36 +10:00
parent 2e7ae6a07e
commit 8ffcf2a6be
2 changed files with 193 additions and 206 deletions

View File

@ -194,31 +194,36 @@ export class CanvasTool {
const tool = toolState.selected; const tool = toolState.selected;
const isDrawable = selectedEntity && isDrawableEntity(selectedEntity.state); const isDrawable = selectedEntity && selectedEntity.state.isEnabled && isDrawableEntity(selectedEntity.state);
// Update the stage's pointer style // Update the stage's pointer style
if (tool === 'view') { if (Boolean(this.manager.stateApi.$transformingEntity.get()) || renderedEntityCount === 0) {
// View gets a hand // 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'; stage.container().style.cursor = isMouseDown ? 'grabbing' : 'grab';
} else if (renderedEntityCount === 0) { // Bbox tool gets default
// We have no layers, so we should not render any tool
stage.container().style.cursor = 'default';
} else if (!isDrawable) {
// Non-drawable layers don't have tools
stage.container().style.cursor = 'not-allowed';
} else if (tool === 'move' || Boolean(this.manager.stateApi.$transformingEntity.get())) {
// Move tool gets a pointer
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';
} else if (tool === 'bbox') { } else if (tool === 'bbox') {
stage.container().style.cursor = 'default'; stage.container().style.cursor = 'default';
} else if (tool === 'eyeDropper') { } else if (tool === 'eyeDropper') {
// Eyedropper gets none
stage.container().style.cursor = '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';
}
} else {
// isDrawable === 'false'
// Non-drawable layers don't have tools
stage.container().style.cursor = 'not-allowed';
} }
stage.draggable(tool === 'view'); stage.draggable(tool === 'view');

View File

@ -15,7 +15,6 @@ import type {
RgbaColor, RgbaColor,
Tool, Tool,
} from 'features/controlLayers/store/types'; } from 'features/controlLayers/store/types';
import { isDrawableEntity, isDrawableEntityAdapter } from 'features/controlLayers/store/types';
import type Konva from 'konva'; import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node'; import type { KonvaEventObject } from 'konva/lib/Node';
import { clamp } from 'lodash-es'; import { clamp } from 'lodash-es';
@ -200,106 +199,101 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
if (color) { if (color) {
manager.stateApi.setFill(color); manager.stateApi.setFill(color);
} }
} manager.preview.tool.render();
} else {
const isDrawable = selectedEntity?.state.isEnabled;
if (pos && isDrawable && !$spaceKey.get() && getIsPrimaryMouseDown(e)) {
$lastMouseDownPos.set(pos);
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
if ( if (toolState.selected === 'brush') {
pos && const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity.state, toolState.selected);
selectedEntity && const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
isDrawableEntity(selectedEntity.state) && if (e.evt.shiftKey && lastLinePoint) {
!$spaceKey.get() && // Create a straight line from the last line point
getIsPrimaryMouseDown(e) if (selectedEntity.adapter.renderer.bufferState) {
) { selectedEntity.adapter.renderer.commitBuffer();
$lastMouseDownPos.set(pos); }
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
if (toolState.selected === 'brush') { await selectedEntity.adapter.renderer.setBuffer({
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity.state, toolState.selected); id: getObjectId('brush_line', true),
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width); type: 'brush_line',
if (e.evt.shiftKey && lastLinePoint) { points: [
// Create a straight line from the last line point // The last point of the last line is already normalized to the entity's coordinates
lastLinePoint.x,
lastLinePoint.y,
alignedPoint.x,
alignedPoint.y,
],
strokeWidth: toolState.brush.width,
color: getCurrentFill(),
clip: getClip(selectedEntity.state),
});
} else {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('brush_line', true),
type: 'brush_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.brush.width,
color: getCurrentFill(),
clip: getClip(selectedEntity.state),
});
}
$lastAddedPoint.set(alignedPoint);
}
if (toolState.selected === 'eraser') {
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity.state, toolState.selected);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
if (e.evt.shiftKey && lastLinePoint) {
// Create a straight line from the last line point
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('eraser_line', true),
type: 'eraser_line',
points: [
// The last point of the last line is already normalized to the entity's coordinates
lastLinePoint.x,
lastLinePoint.y,
alignedPoint.x,
alignedPoint.y,
],
strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity.state),
});
} else {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('eraser_line', true),
type: 'eraser_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity.state),
});
}
$lastAddedPoint.set(alignedPoint);
}
if (toolState.selected === 'rect') {
if (selectedEntity.adapter.renderer.bufferState) { if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer(); selectedEntity.adapter.renderer.commitBuffer();
} }
await selectedEntity.adapter.renderer.setBuffer({ await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('brush_line', true), id: getObjectId('rect', true),
type: 'brush_line', type: 'rect',
points: [ rect: { x: Math.round(normalizedPoint.x), y: Math.round(normalizedPoint.y), width: 0, height: 0 },
// The last point of the last line is already normalized to the entity's coordinates
lastLinePoint.x,
lastLinePoint.y,
alignedPoint.x,
alignedPoint.y,
],
strokeWidth: toolState.brush.width,
color: getCurrentFill(), color: getCurrentFill(),
clip: getClip(selectedEntity.state),
});
} else {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('brush_line', true),
type: 'brush_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.brush.width,
color: getCurrentFill(),
clip: getClip(selectedEntity.state),
}); });
} }
$lastAddedPoint.set(alignedPoint);
}
if (toolState.selected === 'eraser') {
const lastLinePoint = getLastPointOfLastLineOfEntity(selectedEntity.state, toolState.selected);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
if (e.evt.shiftKey && lastLinePoint) {
// Create a straight line from the last line point
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('eraser_line', true),
type: 'eraser_line',
points: [
// The last point of the last line is already normalized to the entity's coordinates
lastLinePoint.x,
lastLinePoint.y,
alignedPoint.x,
alignedPoint.y,
],
strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity.state),
});
} else {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('eraser_line', true),
type: 'eraser_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity.state),
});
}
$lastAddedPoint.set(alignedPoint);
}
if (toolState.selected === 'rect') {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('rect', true),
type: 'rect',
rect: { x: Math.round(normalizedPoint.x), y: Math.round(normalizedPoint.y), width: 0, height: 0 },
color: getCurrentFill(),
});
} }
} }
manager.preview.tool.render();
}); });
//#region mouseup //#region mouseup
@ -307,8 +301,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
$isMouseDown.set(false); $isMouseDown.set(false);
const pos = $lastCursorPos.get(); const pos = $lastCursorPos.get();
const selectedEntity = getSelectedEntity(); const selectedEntity = getSelectedEntity();
const isDrawable = selectedEntity?.state.isEnabled;
if (pos && selectedEntity && isDrawableEntity(selectedEntity.state) && !$spaceKey.get()) { if (pos && isDrawable && !$spaceKey.get()) {
const toolState = getToolState(); const toolState = getToolState();
if (toolState.selected === 'brush') { if (toolState.selected === 'brush') {
@ -340,7 +334,6 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
$lastMouseDownPos.set(null); $lastMouseDownPos.set(null);
} }
manager.preview.tool.render(); manager.preview.tool.render();
}); });
@ -353,104 +346,98 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
if (toolState.selected === 'eyeDropper') { if (toolState.selected === 'eyeDropper') {
const color = getColorUnderCursor(stage); const color = getColorUnderCursor(stage);
manager.stateApi.$colorUnderCursor.set(color); manager.stateApi.$colorUnderCursor.set(color);
} } else {
const isDrawable = selectedEntity?.state.isEnabled;
if ( if (pos && isDrawable && !$spaceKey.get() && getIsPrimaryMouseDown(e)) {
pos && if (toolState.selected === 'brush') {
selectedEntity && const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
isDrawableEntity(selectedEntity.state) && if (drawingBuffer) {
selectedEntity.adapter && if (drawingBuffer.type === 'brush_line') {
isDrawableEntityAdapter(selectedEntity.adapter) && const lastPoint = getLastPointOfLine(drawingBuffer.points);
!$spaceKey.get() && const nextPoint = getNextPoint(pos, toolState, lastPoint);
getIsPrimaryMouseDown(e) if (lastPoint && nextPoint) {
) { const normalizedPoint = offsetCoord(nextPoint, selectedEntity.state.position);
if (toolState.selected === 'brush') { const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
const drawingBuffer = selectedEntity.adapter.renderer.bufferState; // Do not add duplicate points
if (drawingBuffer) { if (lastPoint.x !== alignedPoint.x || lastPoint.y !== alignedPoint.y) {
if (drawingBuffer.type === 'brush_line') { drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
const lastPoint = getLastPointOfLine(drawingBuffer.points); await selectedEntity.adapter.renderer.setBuffer(drawingBuffer);
const nextPoint = getNextPoint(pos, toolState, lastPoint); $lastAddedPoint.set(alignedPoint);
if (lastPoint && nextPoint) { }
const normalizedPoint = offsetCoord(nextPoint, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
// Do not add duplicate points
if (lastPoint.x !== alignedPoint.x || lastPoint.y !== alignedPoint.y) {
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
await selectedEntity.adapter.renderer.setBuffer(drawingBuffer);
$lastAddedPoint.set(alignedPoint);
} }
} else {
selectedEntity.adapter.renderer.clearBuffer();
} }
} else { } else {
selectedEntity.adapter.renderer.clearBuffer(); if (selectedEntity.adapter.renderer.bufferState) {
} selectedEntity.adapter.renderer.commitBuffer();
} else {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('brush_line', true),
type: 'brush_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.brush.width,
color: getCurrentFill(),
clip: getClip(selectedEntity.state),
});
$lastAddedPoint.set(alignedPoint);
}
}
if (toolState.selected === 'eraser') {
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
if (drawingBuffer) {
if (drawingBuffer.type === 'eraser_line') {
const lastPoint = getLastPointOfLine(drawingBuffer.points);
const nextPoint = getNextPoint(pos, toolState, lastPoint);
if (lastPoint && nextPoint) {
const normalizedPoint = offsetCoord(nextPoint, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
// Do not add duplicate points
if (lastPoint.x !== alignedPoint.x || lastPoint.y !== alignedPoint.y) {
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
await selectedEntity.adapter.renderer.setBuffer(drawingBuffer);
$lastAddedPoint.set(alignedPoint);
}
} }
} else {
selectedEntity.adapter.renderer.clearBuffer();
}
} else {
if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('eraser_line', true),
type: 'eraser_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity.state),
});
$lastAddedPoint.set(alignedPoint);
}
}
if (toolState.selected === 'rect') {
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
if (drawingBuffer) {
if (drawingBuffer.type === 'rect') {
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
drawingBuffer.rect.width = Math.round(normalizedPoint.x - drawingBuffer.rect.x); const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
drawingBuffer.rect.height = Math.round(normalizedPoint.y - drawingBuffer.rect.y); await selectedEntity.adapter.renderer.setBuffer({
await selectedEntity.adapter.renderer.setBuffer(drawingBuffer); id: getObjectId('brush_line', true),
type: 'brush_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.brush.width,
color: getCurrentFill(),
clip: getClip(selectedEntity.state),
});
$lastAddedPoint.set(alignedPoint);
}
}
if (toolState.selected === 'eraser') {
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
if (drawingBuffer) {
if (drawingBuffer.type === 'eraser_line') {
const lastPoint = getLastPointOfLine(drawingBuffer.points);
const nextPoint = getNextPoint(pos, toolState, lastPoint);
if (lastPoint && nextPoint) {
const normalizedPoint = offsetCoord(nextPoint, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
// Do not add duplicate points
if (lastPoint.x !== alignedPoint.x || lastPoint.y !== alignedPoint.y) {
drawingBuffer.points.push(alignedPoint.x, alignedPoint.y);
await selectedEntity.adapter.renderer.setBuffer(drawingBuffer);
$lastAddedPoint.set(alignedPoint);
}
}
} else {
selectedEntity.adapter.renderer.clearBuffer();
}
} else { } else {
selectedEntity.adapter.renderer.clearBuffer(); if (selectedEntity.adapter.renderer.bufferState) {
selectedEntity.adapter.renderer.commitBuffer();
}
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
await selectedEntity.adapter.renderer.setBuffer({
id: getObjectId('eraser_line', true),
type: 'eraser_line',
points: [alignedPoint.x, alignedPoint.y],
strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity.state),
});
$lastAddedPoint.set(alignedPoint);
}
}
if (toolState.selected === 'rect') {
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
if (drawingBuffer) {
if (drawingBuffer.type === 'rect') {
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
drawingBuffer.rect.width = Math.round(normalizedPoint.x - drawingBuffer.rect.x);
drawingBuffer.rect.height = Math.round(normalizedPoint.y - drawingBuffer.rect.y);
await selectedEntity.adapter.renderer.setBuffer(drawingBuffer);
} else {
selectedEntity.adapter.renderer.clearBuffer();
}
} }
} }
} }
} }
manager.preview.tool.render(); manager.preview.tool.render();
}); });
@ -461,14 +448,9 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
$lastMouseDownPos.set(null); $lastMouseDownPos.set(null);
const selectedEntity = getSelectedEntity(); const selectedEntity = getSelectedEntity();
const toolState = getToolState(); const toolState = getToolState();
const isDrawable = selectedEntity?.state.isEnabled;
if ( if (pos && isDrawable && !$spaceKey.get() && getIsPrimaryMouseDown(e)) {
pos &&
selectedEntity &&
isDrawableEntity(selectedEntity.state) &&
!$spaceKey.get() &&
getIsPrimaryMouseDown(e)
) {
const drawingBuffer = selectedEntity.adapter.renderer.bufferState; const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') { if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') {