|
|
@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.imageeditor.Bounds;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.ColorableRenderer;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.ColorableRenderer;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.Renderer;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.Renderer;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.RendererContext;
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.RendererContext;
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.imageeditor.UndoRedoStackListener;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.LinkedHashSet;
|
|
|
|
import java.util.LinkedHashSet;
|
|
|
@ -42,6 +43,8 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
@NonNull
|
|
|
|
@NonNull
|
|
|
|
private Runnable invalidate = NULL_RUNNABLE;
|
|
|
|
private Runnable invalidate = NULL_RUNNABLE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private UndoRedoStackListener undoRedoStackListener;
|
|
|
|
|
|
|
|
|
|
|
|
private final UndoRedoStacks undoRedoStacks;
|
|
|
|
private final UndoRedoStacks undoRedoStacks;
|
|
|
|
private final UndoRedoStacks cropUndoRedoStacks;
|
|
|
|
private final UndoRedoStacks cropUndoRedoStacks;
|
|
|
|
private final InBoundsMemory inBoundsMemory = new InBoundsMemory();
|
|
|
|
private final InBoundsMemory inBoundsMemory = new InBoundsMemory();
|
|
|
@ -70,6 +73,10 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
this.invalidate = invalidate != null ? invalidate : NULL_RUNNABLE;
|
|
|
|
this.invalidate = invalidate != null ? invalidate : NULL_RUNNABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setUndoRedoStackListener(UndoRedoStackListener undoRedoStackListener) {
|
|
|
|
|
|
|
|
this.undoRedoStackListener = undoRedoStackListener;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Renders tree with the following matrix:
|
|
|
|
* Renders tree with the following matrix:
|
|
|
|
* <p>
|
|
|
|
* <p>
|
|
|
@ -117,9 +124,7 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
|
|
|
|
|
|
|
|
UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
|
|
|
|
|
|
|
|
if (stacks.getUndoStack().tryPush(editorElementHierarchy.getRoot())) {
|
|
|
|
stacks.pushState(editorElementHierarchy.getRoot());
|
|
|
|
stacks.getRedoStack().clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void undo() {
|
|
|
|
public void undo() {
|
|
|
@ -127,6 +132,8 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
|
|
|
|
|
|
|
|
undoRedo(stacks.getUndoStack(), stacks.getRedoStack(), cropping);
|
|
|
|
undoRedo(stacks.getUndoStack(), stacks.getRedoStack(), cropping);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateUndoRedoAvailableState(stacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void redo() {
|
|
|
|
public void redo() {
|
|
|
@ -134,12 +141,15 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
UndoRedoStacks stacks = cropping ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
|
|
|
|
|
|
|
|
undoRedo(stacks.getRedoStack(), stacks.getUndoStack(), cropping);
|
|
|
|
undoRedo(stacks.getRedoStack(), stacks.getUndoStack(), cropping);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateUndoRedoAvailableState(stacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void undoRedo(@NonNull ElementStack fromStack, @NonNull ElementStack toStack, boolean keepEditorState) {
|
|
|
|
private void undoRedo(@NonNull ElementStack fromStack, @NonNull ElementStack toStack, boolean keepEditorState) {
|
|
|
|
final EditorElement popped = fromStack.pop();
|
|
|
|
final EditorElement oldRootElement = editorElementHierarchy.getRoot();
|
|
|
|
|
|
|
|
final EditorElement popped = fromStack.pop(oldRootElement);
|
|
|
|
|
|
|
|
|
|
|
|
if (popped != null) {
|
|
|
|
if (popped != null) {
|
|
|
|
EditorElement oldRootElement = editorElementHierarchy.getRoot();
|
|
|
|
|
|
|
|
editorElementHierarchy = EditorElementHierarchy.create(popped);
|
|
|
|
editorElementHierarchy = EditorElementHierarchy.create(popped);
|
|
|
|
toStack.tryPush(oldRootElement);
|
|
|
|
toStack.tryPush(oldRootElement);
|
|
|
|
|
|
|
|
|
|
|
@ -187,6 +197,14 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void updateUndoRedoAvailableState(UndoRedoStacks currentStack) {
|
|
|
|
|
|
|
|
if (undoRedoStackListener == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EditorElement root = editorElementHierarchy.getRoot();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
undoRedoStackListener.onAvailabilityChanged(currentStack.canUndo(root), currentStack.canRedo(root));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static Map<UUID, EditorElement> getElementMap(@NonNull EditorElement element) {
|
|
|
|
private static Map<UUID, EditorElement> getElementMap(@NonNull EditorElement element) {
|
|
|
|
final Map<UUID, EditorElement> result = new HashMap<>();
|
|
|
|
final Map<UUID, EditorElement> result = new HashMap<>();
|
|
|
|
element.buildMap(result);
|
|
|
|
element.buildMap(result);
|
|
|
@ -195,14 +213,15 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
|
|
|
|
|
|
|
|
public void startCrop() {
|
|
|
|
public void startCrop() {
|
|
|
|
pushUndoPoint();
|
|
|
|
pushUndoPoint();
|
|
|
|
cropUndoRedoStacks.getUndoStack().clear();
|
|
|
|
cropUndoRedoStacks.clear(editorElementHierarchy.getRoot());
|
|
|
|
cropUndoRedoStacks.getUndoStack().clear();
|
|
|
|
|
|
|
|
editorElementHierarchy.startCrop(invalidate);
|
|
|
|
editorElementHierarchy.startCrop(invalidate);
|
|
|
|
inBoundsMemory.push(editorElementHierarchy.getMainImage(), editorElementHierarchy.getCropEditorElement());
|
|
|
|
inBoundsMemory.push(editorElementHierarchy.getMainImage(), editorElementHierarchy.getCropEditorElement());
|
|
|
|
|
|
|
|
updateUndoRedoAvailableState(cropUndoRedoStacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void doneCrop() {
|
|
|
|
public void doneCrop() {
|
|
|
|
editorElementHierarchy.doneCrop(visibleViewPort, invalidate);
|
|
|
|
editorElementHierarchy.doneCrop(visibleViewPort, invalidate);
|
|
|
|
|
|
|
|
updateUndoRedoAvailableState(undoRedoStacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setCropAspectLock(boolean locked) {
|
|
|
|
public void setCropAspectLock(boolean locked) {
|
|
|
@ -223,6 +242,9 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
if (isCropping()) {
|
|
|
|
if (isCropping()) {
|
|
|
|
ensureFitsBounds(allowScaleToRepairCrop);
|
|
|
|
ensureFitsBounds(allowScaleToRepairCrop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UndoRedoStacks stacks = isCropping() ? cropUndoRedoStacks : undoRedoStacks;
|
|
|
|
|
|
|
|
updateUndoRedoAvailableState(stacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ensureFitsBounds(boolean allowScaleToRepairCrop) {
|
|
|
|
private void ensureFitsBounds(boolean allowScaleToRepairCrop) {
|
|
|
@ -467,13 +489,14 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
parent.addElement(element);
|
|
|
|
parent.addElement(element);
|
|
|
|
|
|
|
|
|
|
|
|
if (parent != mainImage) {
|
|
|
|
if (parent != mainImage) {
|
|
|
|
undoRedoStacks.getUndoStack().clear();
|
|
|
|
undoRedoStacks.clear(editorElementHierarchy.getRoot());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateUndoRedoAvailableState(undoRedoStacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isChanged() {
|
|
|
|
public boolean isChanged() {
|
|
|
|
ElementStack undoStack = undoRedoStacks.getUndoStack();
|
|
|
|
return undoRedoStacks.isChanged(editorElementHierarchy.getRoot());
|
|
|
|
return !undoStack.isEmpty() || undoStack.isOverflowed();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public RectF findCropRelativeToRoot() {
|
|
|
|
public RectF findCropRelativeToRoot() {
|
|
|
@ -578,4 +601,5 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|
|
|
public boolean isCropping() {
|
|
|
|
public boolean isCropping() {
|
|
|
|
return editorElementHierarchy.getCropEditorElement().getFlags().isVisible();
|
|
|
|
return editorElementHierarchy.getCropEditorElement().getFlags().isVisible();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|