Fix undo handling of group and some other IO tools (#2931)

* Fix undo handling of group and some other IO tools

* Emit change signal inside onObjectModified

* Fix group lost after moving group then undoing

* Skip undo entry if canvas has not changed

The onObjectModified() call I added in a previous commit to deleteDuplicateTools results in a duplicate undo entry for the delete tool. Checking for duplicate entries seems simpler than having to think about where onObjectModified() should be called exactly

* Fix extra undo entry added after ungroup
This commit is contained in:
Abdo 2024-01-09 04:19:46 +03:00 committed by GitHub
parent 5d1fc9a591
commit cc81db0f9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 17 additions and 4 deletions

View file

@ -301,6 +301,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tooltip="{tool.tooltip()} ({getPlatformString(tool.shortcut)})" tooltip="{tool.tooltip()} ({getPlatformString(tool.shortcut)})"
on:click={() => { on:click={() => {
tool.action(canvas); tool.action(canvas);
undoStack.onObjectModified();
}} }}
> >
{@html tool.icon} {@html tool.icon}
@ -328,7 +329,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tooltip="{tool.tooltip()} ({getPlatformString(tool.shortcut)})" tooltip="{tool.tooltip()} ({getPlatformString(tool.shortcut)})"
on:click={() => { on:click={() => {
tool.action(canvas); tool.action(canvas);
emitChangeSignal(); undoStack.onObjectModified();
}} }}
> >
{@html tool.icon} {@html tool.icon}
@ -368,6 +369,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
)})" )})"
on:click={() => { on:click={() => {
alignTool.action(canvas); alignTool.action(canvas);
undoStack.onObjectModified();
}} }}
> >
{@html alignTool.icon} {@html alignTool.icon}

View file

@ -81,6 +81,7 @@ export const unGroupShapes = (canvas: fabric.Canvas): void => {
const group = canvas.getActiveObject(); const group = canvas.getActiveObject();
const items = group.getObjects(); const items = group.getObjects();
group._restoreObjectsState(); group._restoreObjectsState();
group.destroyed = true;
canvas.remove(group); canvas.remove(group);
items.forEach((item) => { items.forEach((item) => {

View file

@ -19,7 +19,7 @@ type UndoState = {
redoable: boolean; redoable: boolean;
}; };
const shapeType = ["rect", "ellipse", "i-text"]; const shapeType = ["rect", "ellipse", "i-text", "group"];
const validShape = (shape: fabric.Object): boolean => { const validShape = (shape: fabric.Object): boolean => {
if (shape.width <= 5 || shape.height <= 5) { if (shape.width <= 5 || shape.height <= 5) {
@ -49,7 +49,12 @@ class UndoStack {
setCanvas(canvas: fabric.Canvas): void { setCanvas(canvas: fabric.Canvas): void {
this.canvas = canvas; this.canvas = canvas;
this.canvas.on("object:modified", (opts) => this.maybePush(opts)); this.canvas.on("object:modified", (opts) => this.maybePush(opts));
this.canvas.on("object:removed", (opts) => this.maybePush(opts)); this.canvas.on("object:removed", (opts) => {
// `destroyed` is a custom property set on groups in the ungrouping routine to avoid adding a spurious undo entry
if (!opts.target.group && !opts.target.destroyed) {
this.maybePush(opts);
}
});
} }
reset(): void { reset(): void {
@ -94,6 +99,7 @@ class UndoStack {
onObjectModified(): void { onObjectModified(): void {
this.push(); this.push();
emitChangeSignal();
} }
private maybePush(opts): void { private maybePush(opts): void {
@ -103,8 +109,12 @@ class UndoStack {
} }
private push(): void { private push(): void {
const entry = JSON.stringify(this.canvas);
if (entry === this.stack[this.stack.length - 1]) {
return;
}
this.stack.length = this.index + 1; this.stack.length = this.index + 1;
this.stack.push(JSON.stringify(this.canvas)); this.stack.push(entry);
this.index++; this.index++;
this.updateState(); this.updateState();
} }