Image Editor - Allow undoing during croping.

This commit is contained in:
Alan Evans 2019-05-15 14:31:16 -03:00 committed by Greyson Parrelli
parent 95304fe001
commit 068ffc2167
4 changed files with 94 additions and 19 deletions

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.imageeditor.model;
import android.support.annotation.NonNull;
/**
* Flags for an {@link EditorElement}.
* <p>
@ -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;
}
}

View File

@ -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<UUID, EditorElement> fromMap = getElementMap(fromRootElement);
Map<UUID, EditorElement> 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);
}

View File

@ -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<UndoRedoStacks> CREATOR = new Creator<UndoRedoStacks>() {
@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;
}
}

View File

@ -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<View> views : visibilityModeMap.values()) {
allViews.addAll(views);