From 068ffc216761dda501c9519c51099ed840c67451 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Wed, 15 May 2019 14:31:16 -0300 Subject: [PATCH] Image Editor - Allow undoing during croping. --- .../imageeditor/model/EditorFlags.java | 7 +++ .../imageeditor/model/EditorModel.java | 51 +++++++++++------- .../imageeditor/model/UndoRedoStacks.java | 53 +++++++++++++++++++ .../securesms/scribbles/ImageEditorHud.java | 2 +- 4 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java diff --git a/src/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java b/src/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java index 700ddaa86..5887f8540 100644 --- a/src/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java +++ b/src/org/thoughtcrime/securesms/imageeditor/model/EditorFlags.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.imageeditor.model; +import android.support.annotation.NonNull; + /** * Flags for an {@link EditorElement}. *

@@ -113,4 +115,9 @@ public final class EditorFlags { void restoreState(int flags) { this.flags = flags; } + + public void set(@NonNull EditorFlags from) { + this.persistedFlags = from.persistedFlags; + this.flags = from.flags; + } } diff --git a/src/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java b/src/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java index 1a1b6644d..9f178c1a3 100644 --- a/src/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java +++ b/src/org/thoughtcrime/securesms/imageeditor/model/EditorModel.java @@ -40,8 +40,8 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { @NonNull private Runnable invalidate = NULL_RUNNABLE; - private final ElementStack undoStack; - private final ElementStack redoStack; + private final UndoRedoStacks undoRedoStacks; + private final UndoRedoStacks cropUndoRedoStacks; private EditorElementHierarchy editorElementHierarchy; @@ -51,16 +51,16 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { public EditorModel() { this.size = new Point(1024, 1024); this.editorElementHierarchy = EditorElementHierarchy.create(); - this.undoStack = new ElementStack(50); - this.redoStack = new ElementStack(50); + this.undoRedoStacks = new UndoRedoStacks(50); + this.cropUndoRedoStacks = new UndoRedoStacks(50); } private EditorModel(Parcel in) { ClassLoader classLoader = getClass().getClassLoader(); this.size = new Point(in.readInt(), in.readInt()); this.editorElementHierarchy = EditorElementHierarchy.create(in.readParcelable(classLoader)); - this.undoStack = in.readParcelable(classLoader); - this.redoStack = in.readParcelable(classLoader); + this.undoRedoStacks = in.readParcelable(classLoader); + this.cropUndoRedoStacks = in.readParcelable(classLoader); } public void setInvalidate(@Nullable Runnable invalidate) { @@ -107,28 +107,35 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { } public void pushUndoPoint() { - if (undoStack.tryPush(editorElementHierarchy.getRoot())) { - redoStack.clear(); + UndoRedoStacks stacks = isCropping() ? cropUndoRedoStacks : undoRedoStacks; + + if (stacks.getUndoStack().tryPush(editorElementHierarchy.getRoot())) { + stacks.getRedoStack().clear(); } } public void undo() { - undoRedo(undoStack, redoStack); + boolean cropping = isCropping(); + UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks; + + undoRedo(stacks.getUndoStack(), stacks.getRedoStack(), cropping); } public void redo() { - undoRedo(redoStack, undoStack); + boolean cropping = isCropping(); + UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks; + + undoRedo(stacks.getRedoStack(), stacks.getUndoStack(), cropping); } - private void undoRedo(@NonNull ElementStack fromStack, @NonNull ElementStack toStack) { + private void undoRedo(@NonNull ElementStack fromStack, @NonNull ElementStack toStack, boolean keepEditorState) { final EditorElement popped = fromStack.pop(); - if (popped != null) { EditorElement oldRootElement = editorElementHierarchy.getRoot(); editorElementHierarchy = EditorElementHierarchy.create(popped); toStack.tryPush(oldRootElement); - restoreStateWithAnimations(oldRootElement, editorElementHierarchy.getRoot(), invalidate); + restoreStateWithAnimations(oldRootElement, editorElementHierarchy.getRoot(), invalidate, keepEditorState); invalidate.run(); // re-zoom image root as the view port might be different now @@ -136,7 +143,7 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { } } - private static void restoreStateWithAnimations(@NonNull EditorElement fromRootElement, @NonNull EditorElement toRootElement, @NonNull Runnable onInvalidate) { + private static void restoreStateWithAnimations(@NonNull EditorElement fromRootElement, @NonNull EditorElement toRootElement, @NonNull Runnable onInvalidate, boolean keepEditorState) { Map fromMap = getElementMap(fromRootElement); Map toMap = getElementMap(toRootElement); @@ -145,6 +152,11 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { EditorElement toElement = toMap.get(fromElement.getId()); if (toElement != null) { toElement.animateFrom(fromElement.getLocalMatrixAnimating(), onInvalidate); + + if (keepEditorState) { + toElement.getEditorMatrix().set(fromElement.getEditorMatrix()); + toElement.getFlags().set(fromElement.getFlags()); + } } else { // element is removed EditorElement parentFrom = fromRootElement.parentOf(fromElement); @@ -173,6 +185,8 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { public void startCrop() { pushUndoPoint(); + cropUndoRedoStacks.getUndoStack().clear(); + cropUndoRedoStacks.getUndoStack().clear(); editorElementHierarchy.startCrop(invalidate); } @@ -236,8 +250,8 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { dest.writeInt(size.x); dest.writeInt(size.y); dest.writeParcelable(editorElementHierarchy.getRoot(), flags); - dest.writeParcelable(undoStack, flags); - dest.writeParcelable(redoStack, flags); + dest.writeParcelable(undoRedoStacks, flags); + dest.writeParcelable(cropUndoRedoStacks, flags); } /** @@ -337,11 +351,12 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { parent.addElement(element); if (parent != mainImage) { - undoStack.clear(); + undoRedoStacks.getUndoStack().clear(); } } public boolean isChanged() { + ElementStack undoStack = undoRedoStacks.getUndoStack(); return !undoStack.isEmpty() || undoStack.isOverflowed(); } @@ -381,7 +396,7 @@ public final class EditorModel implements Parcelable, RendererContext.Ready { editorElementHierarchy.flipRotate(0, -1, 1, visibleViewPort, invalidate); } - public void flipVerticle() { + public void flipVertical() { pushUndoPoint(); editorElementHierarchy.flipRotate(0, 1, -1, visibleViewPort, invalidate); } diff --git a/src/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java b/src/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java new file mode 100644 index 000000000..c0820e0d6 --- /dev/null +++ b/src/org/thoughtcrime/securesms/imageeditor/model/UndoRedoStacks.java @@ -0,0 +1,53 @@ +package org.thoughtcrime.securesms.imageeditor.model; + +import android.os.Parcel; +import android.os.Parcelable; + +final class UndoRedoStacks implements Parcelable { + + private final ElementStack undoStack; + private final ElementStack redoStack; + + public UndoRedoStacks(int limit) { + this(new ElementStack(limit), new ElementStack(limit)); + } + + private UndoRedoStacks(ElementStack undoStack, ElementStack redoStack) { + this.undoStack = undoStack; + this.redoStack = redoStack; + } + + public static final Creator CREATOR = new Creator() { + @Override + public UndoRedoStacks createFromParcel(Parcel in) { + return new UndoRedoStacks( + in.readParcelable(ElementStack.class.getClassLoader()), + in.readParcelable(ElementStack.class.getClassLoader()) + ); + } + + @Override + public UndoRedoStacks[] newArray(int size) { + return new UndoRedoStacks[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(undoStack, flags); + dest.writeParcelable(redoStack, flags); + } + + @Override + public int describeContents() { + return 0; + } + + ElementStack getUndoStack() { + return undoStack; + } + + ElementStack getRedoStack() { + return redoStack; + } +} diff --git a/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java b/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java index 7b36e82dd..c00a753e3 100644 --- a/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java +++ b/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java @@ -107,7 +107,7 @@ public final class ImageEditorHud extends LinearLayout { setVisibleViewsWhenInMode(Mode.MOVE_DELETE, confirmButton, deleteButton); - setVisibleViewsWhenInMode(Mode.CROP, confirmButton, cropFlipButton, cropRotateButton, cropAspectLock); + setVisibleViewsWhenInMode(Mode.CROP, confirmButton, cropFlipButton, cropRotateButton, cropAspectLock, undoButton); for (Set views : visibilityModeMap.values()) { allViews.addAll(views);