anki/ts/image-occlusion/tools/tool-ellipse.ts
Hikaru Y 3742fa9f0c
Fix some issues with undo/redo in mask editor (#2649)
Issues:
- The `change` event was not dispatched in MaskEditor.svelte when an
undo/redo was performed. Therefore, if the user then closed the editor
or switched to another note without performing an operation that would
cause the `change` event to be dispatched, the undone or redone changes
were not saved to DB.
- When `IOMode.kind === "edit"` (i.e., Edit Current or Browse), the
beginning of the undo history was a blank canvas, not a canvas with
existing masks. Therefore, if you continued to undo to the beginning of
the history, the masks that existed when you opened the editor would be
lost, and they would not be restored even when you performed a redo.
- In the 'Add' dialog, the undo history was not reset when starting to
create a new IO note after adding an IO note.

Also add a small UI improvement:
The undo/redo buttons are now disabled when there is no action to
undo/redo.
2023-09-10 13:26:41 +10:00

121 lines
3.3 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { fabric } from "fabric";
import { BORDER_COLOR, disableRotation, SHAPE_MASK_COLOR, stopDraw } from "./lib";
import { undoStack } from "./tool-undo-redo";
export const drawEllipse = (canvas: fabric.Canvas): void => {
canvas.selectionColor = "rgba(0, 0, 0, 0)";
let ellipse, isDown, origX, origY;
stopDraw(canvas);
canvas.on("mouse:down", function(o) {
if (o.target) {
return;
}
isDown = true;
const pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
ellipse = new fabric.Ellipse({
id: "ellipse-" + new Date().getTime(),
left: origX,
top: origY,
originX: "left",
originY: "top",
rx: pointer.x - origX,
ry: pointer.y - origY,
fill: SHAPE_MASK_COLOR,
transparentCorners: false,
selectable: true,
stroke: BORDER_COLOR,
strokeWidth: 1,
strokeUniform: true,
noScaleCache: false,
});
disableRotation(ellipse);
canvas.add(ellipse);
});
canvas.on("mouse:move", function(o) {
if (!isDown) return;
const pointer = canvas.getPointer(o.e);
let rx = Math.abs(origX - pointer.x) / 2;
let ry = Math.abs(origY - pointer.y) / 2;
const x = pointer.x;
const y = pointer.y;
if (rx > ellipse.strokeWidth) {
rx -= ellipse.strokeWidth / 2;
}
if (ry > ellipse.strokeWidth) {
ry -= ellipse.strokeWidth / 2;
}
if (x < origX) {
ellipse.set({ originX: "right" });
} else {
ellipse.set({ originX: "left" });
}
if (y < origY) {
ellipse.set({ originY: "bottom" });
} else {
ellipse.set({ originY: "top" });
}
// do not draw outside of canvas
if (x < ellipse.strokeWidth) {
rx = (origX + ellipse.strokeWidth + 0.5) / 2;
}
if (y < ellipse.strokeWidth) {
ry = (origY + ellipse.strokeWidth + 0.5) / 2;
}
if (x >= canvas.width - ellipse.strokeWidth) {
rx = (canvas.width - origX) / 2 - ellipse.strokeWidth + 0.5;
}
if (y > canvas.height - ellipse.strokeWidth) {
ry = (canvas.height - origY) / 2 - ellipse.strokeWidth + 0.5;
}
ellipse.set({ rx: rx, ry: ry });
canvas.renderAll();
});
canvas.on("mouse:up", function() {
isDown = false;
// probably changed from ellipse to rectangle
if (!ellipse) {
return;
}
if (ellipse.width < 5 || ellipse.height < 5) {
canvas.remove(ellipse);
return;
}
if (ellipse.originX === "right") {
ellipse.set({
originX: "left",
left: ellipse.left - ellipse.width + ellipse.strokeWidth,
});
}
if (ellipse.originY === "bottom") {
ellipse.set({
originY: "top",
top: ellipse.top - ellipse.height + ellipse.strokeWidth,
});
}
ellipse.setCoords();
undoStack.onObjectAdded(ellipse.id);
});
};