0

Thanks to James_D, i have a perfectly working undo/redo function when i move or resize my group. Unfortunately i can't make it work with the rotate too.

EDIT: I have made a custom bounding box and the undo/redo is working for the rotate too. But exception message is thrown every time i undo any change:

Exception in thread "JavaFX Application Thread"

java.lang.IllegalArgumentException: Unexpected change received.

Expected: test.Model.Undo.UndoChange@8c4318ec Received:

test.Model.Undo.UndoChange@35c7d8eb at

org.fxmisc.undo.impl.UndoManagerImpl.changeObserved(UndoManagerImpl.java:185)

    public class CustomBoundingBox {

    private double minX;
    private double minY;
    private double width;
    private double height;
    private double rotation;
    private Paint fill;

    public CustomBoundingBox(double minX, double minY, double width, double height, double rotation, Paint fill) {
        this.minX=minX;
        this.minY=minY;
        this.width=width;
        this.height=height;
        this.rotation=rotation;
        this.fill=fill;
    }

    public double getMinX() {
        return minX;
    }

    public void setMinX(double minX) {
        this.minX = minX;
    }

    public double getMinY() {
        return minY;
    }

    public void setMinY(double minY) {
        this.minY = minY;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public double getRotation() {
        return rotation;
    }

    public void setRotation(double rotation) {
        this.rotation = rotation;
    }

    public Paint getFill() {
        return fill;
    }

    public void setColor(Paint fill) {
        this.fill = fill;
    }
}

EventStreamers:

{
...
        EventStream<UndoChange<CustomBoundingBox>> rotationChanges = makeEventStream(rect.rotateProperty(),
                rRect -> new CustomBoundingBox(rect.getRect().getX(), rect.getRect().getY(), rect.getRect().getWidth(), rect.getRect().getHeight(), rRect.doubleValue(), rect.getRect().getFill()));


        EventStream<UndoChange<CustomBoundingBox>> boundsChanges = EventStreams
                .merge(xChanges, yChanges, widthChanges, heightChanges, rotationChanges).reducible(UndoChange::merge)
                .suspendWhen(animationRunning);

        rect.undoManager = UndoManagerFactory.unlimitedHistoryUndoManager(boundsChanges, UndoChange::invert, c -> {
            rect.getRedoAnimation().getKeyFrames()
                    .setAll(new KeyFrame(RectangleModel.getRedoAnimationTime(), 
                            new KeyValue(rect.getRect().xProperty(), c.getNewValue().getMinX()),
                            new KeyValue(rect.getRect().yProperty(), c.getNewValue().getMinY()),
                            new KeyValue(rect.getRect().widthProperty(), c.getNewValue().getWidth()),
                            new KeyValue(rect.getRect().heightProperty(), c.getNewValue().getHeight()),
                            new KeyValue(rect.rotateProperty(), c.getNewValue().getRotation())));
            rect.getRedoAnimation().play();
        }, (c1, c2) -> Optional.of(c1.merge(c2)));

    }

    private <S, T> EventStream<UndoChange<T>> makeEventStream(ObservableValue<S> property, Function<S, T> objectSupplier) {
        return EventStreams.changesOf(property).map(
                c -> new UndoChange<>(objectSupplier.apply(c.getOldValue()), objectSupplier.apply(c.getNewValue())));
    }
Senpai
  • 515
  • 6
  • 18
  • 1
    I just used `BoundingBox` there because it conveniently encapsulated all the properties of interest (x, y, width, and height). If you need a different set of properties (e.g. x, y, width, height, and rotation), just define your own class encapsulating those and replace `BoundingBox` with it. – James_D Jun 06 '16 at 14:13
  • Thanks James_D, i'll try that and post back for the answer or furthermore questions. – Senpai Jun 06 '16 at 14:18
  • @James_D Hey i have now a working undo/redo for rotate too, but i'm getting an exception: Unexpected change received. Do you know what is probably causing it? I just made an entity "CustomBoundingBox" with x,y,width,height,rotation,color – Senpai Jun 06 '16 at 15:31
  • 1
    I also suggest making the change objects immutable by removing the setters and making the fields final. You probably don't want the possibility that a change object stored in the UndoManager will be modified without notice. – Tomas Mikula Jun 06 '16 at 17:20
  • @TomasMikula Thanks for you suggestion. – Senpai Jun 06 '16 at 18:17

1 Answers1

0

I found the issue, i had to implement the equals() method:

public boolean equals(Object obj) {
    if (obj == this) return true;
    if (obj instanceof CustomBoundingBox) {
        CustomBoundingBox other = (CustomBoundingBox) obj;
        return getMinX() == other.getMinX()
            && getMinY() == other.getMinY()
            && getWidth() == other.getWidth()
            && getHeight() == other.getHeight()
            && getRotation() == other.getRotation()
            && getFill() == other.getFill();
    } else return false;
}
Senpai
  • 515
  • 6
  • 18