Compare commits
19 Commits
4b07626155
...
0.1.0
Author | SHA1 | Date | |
---|---|---|---|
4872fd3db3
![]() |
|||
833c346914
|
|||
8ef4a9a572
|
|||
ae2f2e8a84
![]() |
|||
7101523584
|
|||
4a70d954ef
![]() |
|||
d649f24ad8
![]() |
|||
9c2971a078
![]() |
|||
d26c0fe256
|
|||
a39d03abf8
|
|||
0fc3577750
|
|||
d484839c8b
|
|||
5d1ef84770
|
|||
1155541350
|
|||
fa5c2419bf
![]() |
|||
0f1c3e06d8
|
|||
1b6d7f3dde
|
|||
d49772a127
|
|||
d80dd94e90
|
1
Jenkinsfile
vendored
1
Jenkinsfile
vendored
@ -18,6 +18,7 @@ pipeline {
|
|||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
junit '*/target/surefire-reports/*.xml'
|
junit '*/target/surefire-reports/*.xml'
|
||||||
|
publishCoverage adapters: [jacocoAdapter(mergeToOneReport: true, path: '*/target/site/jacoco/jacoco.xml')]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>dev.kske</groupId>
|
<groupId>dev.kske</groupId>
|
||||||
<artifactId>undo-redo</artifactId>
|
<artifactId>undo-redo</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.1.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -26,6 +26,8 @@ public interface Change {
|
|||||||
Change invert();
|
Change invert();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @apiNote If this method returns {@code true} when adding this change to a change manager, it
|
||||||
|
* will be discarded immediately and therefore can be garbage collected.
|
||||||
* @return whether the application of this change would result in an identical state
|
* @return whether the application of this change would result in an identical state
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package dev.kske.undoredo.core;
|
package dev.kske.undoredo.core;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A change manager keeps track of subsequent changes and allows un- and redoing them. A specific
|
* A change manager keeps track of subsequent changes and allows un- and redoing them. A specific
|
||||||
* change can be marked using {@link #mark()} to keep track of a saved state in the application that
|
* change can be marked using {@link #mark()} to keep track of a saved state in the application that
|
||||||
* uses the manager.
|
* uses the manager.
|
||||||
|
* <p>
|
||||||
|
* If you intend to listen to the state of a change manager, consider writing a wrapper
|
||||||
|
* implementation for an existing change manager that adds the necessary hooks. If you use JavaFX,
|
||||||
|
* take a look at the {@code dev.kske.undoredo.javafx} module.
|
||||||
*
|
*
|
||||||
* @param <C> the change type to store in this change manager
|
* @param <C> the change type to store in this change manager
|
||||||
* @author Maximilian Käfer
|
* @author Maximilian Käfer
|
||||||
@ -15,12 +19,20 @@ import java.util.List;
|
|||||||
public interface ChangeManager<C extends Change> {
|
public interface ChangeManager<C extends Change> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the given change and appends it to the change list.
|
* Applies the given change and appends it to the change list. If the change is an identity, no
|
||||||
|
* action is taken.
|
||||||
*
|
*
|
||||||
* @param change the change to add
|
* @param change the change to add
|
||||||
|
* @return whether the change has been added
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
void addChange(C change);
|
boolean addChange(C change);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the change that was applied last
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
Optional<C> getLastChange();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undoes the current change.
|
* Undoes the current change.
|
||||||
@ -45,11 +57,18 @@ public interface ChangeManager<C extends Change> {
|
|||||||
*/
|
*/
|
||||||
void mark();
|
void mark();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the marked index so that no change is marked.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
void unmark();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether the current change is marked
|
* @return whether the current change is marked
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
boolean isAtMarkedIndex();
|
boolean isAtMarkedChange();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether a change is present that can be undone
|
* @return whether a change is present that can be undone
|
||||||
|
@ -3,6 +3,8 @@ package dev.kske.undoredo.core;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A simple change manager with a linear history model backed by an array list.
|
||||||
|
*
|
||||||
* @param <C> the change type to store in this change manager
|
* @param <C> the change type to store in this change manager
|
||||||
* @author Maximilian Käfer
|
* @author Maximilian Käfer
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
@ -15,11 +17,28 @@ public final class UnlimitedChangeManager<C extends Change> implements ChangeMan
|
|||||||
private int index = -1;
|
private int index = -1;
|
||||||
private int markedIndex = -1;
|
private int markedIndex = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @implNote As this change manager uses a linear history model, all changes behind the last
|
||||||
|
* applied change will be discarded and therefore can be garbage collected.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addChange(C change) {
|
public boolean addChange(C change) {
|
||||||
|
if (!change.isIdentity()) {
|
||||||
change.apply();
|
change.apply();
|
||||||
|
|
||||||
|
// Remove divergent changes
|
||||||
|
changes.subList(index + 1, changes.size()).clear();
|
||||||
|
|
||||||
changes.add(change);
|
changes.add(change);
|
||||||
++index;
|
++index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<C> getLastChange() {
|
||||||
|
return index < 0 ? Optional.empty() : Optional.of(changes.get(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -48,7 +67,12 @@ public final class UnlimitedChangeManager<C extends Change> implements ChangeMan
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAtMarkedIndex() {
|
public void unmark() {
|
||||||
|
markedIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAtMarkedChange() {
|
||||||
return markedIndex == index;
|
return markedIndex == index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,22 @@ class ChangeManagerTest {
|
|||||||
IntWrapper wrapper;
|
IntWrapper wrapper;
|
||||||
IntChange change;
|
IntChange change;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an int change with the given value.
|
||||||
|
*
|
||||||
|
* @param value the value of the change
|
||||||
|
* @return the created change
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
IntChange change(int value) {
|
||||||
|
return new IntChange(wrapper, value);
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void prepareChangeManager() {
|
void prepareChangeManager() {
|
||||||
manager = new UnlimitedChangeManager<>();
|
manager = new UnlimitedChangeManager<>();
|
||||||
wrapper = new IntWrapper();
|
wrapper = new IntWrapper();
|
||||||
change = new IntChange(wrapper, 1);
|
change = change(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,20 +38,46 @@ class ChangeManagerTest {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(10)
|
||||||
void testAddChange() {
|
void testAddChange() {
|
||||||
assertSame(0, wrapper.value);
|
assertSame(0, wrapper.value);
|
||||||
manager.addChange(change);
|
assertTrue(manager.addChange(change));
|
||||||
assertSame(1, wrapper.value);
|
assertSame(1, wrapper.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether identity changes are ignored when adding them to the change manager.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(15)
|
||||||
|
void testDiscardIdentity() {
|
||||||
|
manager.addChange(change);
|
||||||
|
assertFalse(manager.addChange(change(0)));
|
||||||
|
assertSame(1, wrapper.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests retrieving the last change.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(20)
|
||||||
|
void testLastChange() {
|
||||||
|
assertTrue(manager.getLastChange().isEmpty());
|
||||||
|
manager.addChange(change);
|
||||||
|
assertEquals(change, manager.getLastChange().get());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the consistency of the change list.
|
* Tests the consistency of the change list.
|
||||||
*
|
*
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Order(2)
|
@Order(30)
|
||||||
void testGetChanges() {
|
void testGetChanges() {
|
||||||
assertTrue(manager.getChanges().isEmpty());
|
assertTrue(manager.getChanges().isEmpty());
|
||||||
manager.addChange(change);
|
manager.addChange(change);
|
||||||
@ -54,7 +91,7 @@ class ChangeManagerTest {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Order(2)
|
@Order(40)
|
||||||
void testUndo() {
|
void testUndo() {
|
||||||
assertFalse(manager.isUndoAvailable());
|
assertFalse(manager.isUndoAvailable());
|
||||||
assertFalse(manager.undo());
|
assertFalse(manager.undo());
|
||||||
@ -63,6 +100,7 @@ class ChangeManagerTest {
|
|||||||
assertTrue(manager.undo());
|
assertTrue(manager.undo());
|
||||||
assertFalse(manager.isUndoAvailable());
|
assertFalse(manager.isUndoAvailable());
|
||||||
assertFalse(manager.undo());
|
assertFalse(manager.undo());
|
||||||
|
assertTrue(manager.getLastChange().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +109,7 @@ class ChangeManagerTest {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Order(4)
|
@Order(50)
|
||||||
void testRedo() {
|
void testRedo() {
|
||||||
assertFalse(manager.isRedoAvailable());
|
assertFalse(manager.isRedoAvailable());
|
||||||
assertFalse(manager.redo());
|
assertFalse(manager.redo());
|
||||||
@ -83,6 +121,25 @@ class ChangeManagerTest {
|
|||||||
assertTrue(manager.redo());
|
assertTrue(manager.redo());
|
||||||
assertFalse(manager.isRedoAvailable());
|
assertFalse(manager.isRedoAvailable());
|
||||||
assertFalse(manager.redo());
|
assertFalse(manager.redo());
|
||||||
|
assertEquals(change, manager.getLastChange().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the changes after the current index are discarded when adding a change.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(55)
|
||||||
|
void testDiscardDivergentHistory() {
|
||||||
|
manager.addChange(change);
|
||||||
|
manager.addChange(change(2));
|
||||||
|
manager.undo();
|
||||||
|
manager.undo();
|
||||||
|
assertTrue(manager.addChange(change(3)));
|
||||||
|
assertFalse(manager.isRedoAvailable());
|
||||||
|
assertSame(3, wrapper.value);
|
||||||
|
assertSame(1, manager.getChanges().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,14 +148,30 @@ class ChangeManagerTest {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Order(5)
|
@Order(60)
|
||||||
void testMark() {
|
void testMark() {
|
||||||
assertTrue(manager.isAtMarkedIndex());
|
assertTrue(manager.isAtMarkedChange());
|
||||||
manager.addChange(change);
|
manager.addChange(change);
|
||||||
assertFalse(manager.isAtMarkedIndex());
|
assertFalse(manager.isAtMarkedChange());
|
||||||
manager.mark();
|
manager.mark();
|
||||||
assertTrue(manager.isAtMarkedIndex());
|
assertTrue(manager.isAtMarkedChange());
|
||||||
manager.undo();
|
manager.undo();
|
||||||
assertFalse(manager.isAtMarkedIndex());
|
assertFalse(manager.isAtMarkedChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests unmarking a change.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(70)
|
||||||
|
void testUnmark() {
|
||||||
|
manager.addChange(change);
|
||||||
|
manager.mark();
|
||||||
|
manager.unmark();
|
||||||
|
assertFalse(manager.isAtMarkedChange());
|
||||||
|
manager.undo();
|
||||||
|
assertTrue(manager.isAtMarkedChange());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package dev.kske.undoredo.core;
|
package dev.kske.undoredo.core;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
@ -28,4 +30,24 @@ class IntChange implements Change {
|
|||||||
public boolean isIdentity() {
|
public boolean isIdentity() {
|
||||||
return value == 0;
|
return value == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (!(obj instanceof IntChange))
|
||||||
|
return false;
|
||||||
|
IntChange other = (IntChange) obj;
|
||||||
|
return value == other.value && Objects.equals(wrapper, other.wrapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package dev.kske.undoredo.core;
|
package dev.kske.undoredo.core;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
@ -7,4 +9,24 @@ package dev.kske.undoredo.core;
|
|||||||
class IntWrapper {
|
class IntWrapper {
|
||||||
|
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (!(obj instanceof IntWrapper))
|
||||||
|
return false;
|
||||||
|
IntWrapper other = (IntWrapper) obj;
|
||||||
|
return value == other.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
27
javafx/.classpath
Normal file
27
javafx/.classpath
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
23
javafx/.project
Normal file
23
javafx/.project
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>undo-redo-javafx</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
28
javafx/pom.xml
Normal file
28
javafx/pom.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>undo-redo-javafx</artifactId>
|
||||||
|
<name>Undo-Redo JavaFX Integration</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>dev.kske</groupId>
|
||||||
|
<artifactId>undo-redo</artifactId>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.kske</groupId>
|
||||||
|
<artifactId>undo-redo-core</artifactId>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-base</artifactId>
|
||||||
|
<version>11</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,144 @@
|
|||||||
|
package dev.kske.undoredo.javafx;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
|
||||||
|
import dev.kske.undoredo.core.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an ordinary change manager into an observable change manager, providing the required
|
||||||
|
* properties for concrete implementations.
|
||||||
|
* <p>
|
||||||
|
* The properties have the same name as their corresponding {@code -property()} methods and can be
|
||||||
|
* accessed reflectively from JavaFX, e.g. through
|
||||||
|
* {@link javafx.beans.binding.Bindings#select(Object, String...)}. Alternatively, the property
|
||||||
|
* names are available as constants.
|
||||||
|
*
|
||||||
|
* @param <C> the change type to store in this change manager
|
||||||
|
* @param <M> the type of change manager to wrap
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public class ChangeManagerWrapper<C extends Change, M extends ChangeManager<C>>
|
||||||
|
implements ObservableChangeManager<C> {
|
||||||
|
|
||||||
|
public static final String LAST_CHANGE = "lastChange";
|
||||||
|
public static final String AT_MARKED_CHANGE = "atMarkedChange";
|
||||||
|
public static final String UNDO_AVAILABLE = "undoAvailable";
|
||||||
|
public static final String REDO_AVAILABLE = "redoAvailable";
|
||||||
|
|
||||||
|
protected ReadOnlyObjectWrapper<C> lastChange =
|
||||||
|
new ReadOnlyObjectWrapper<>(this, LAST_CHANGE);
|
||||||
|
protected ReadOnlyBooleanWrapper atMarkedChange =
|
||||||
|
new ReadOnlyBooleanWrapper(this, AT_MARKED_CHANGE);
|
||||||
|
protected ReadOnlyBooleanWrapper undoAvailable =
|
||||||
|
new ReadOnlyBooleanWrapper(this, UNDO_AVAILABLE);
|
||||||
|
protected ReadOnlyBooleanWrapper redoAvailable =
|
||||||
|
new ReadOnlyBooleanWrapper(this, REDO_AVAILABLE);
|
||||||
|
|
||||||
|
protected final M manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a change manager wrapper.
|
||||||
|
*
|
||||||
|
* @param manager the change manager to wrap
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public ChangeManagerWrapper(M manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
updateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addChange(C change) {
|
||||||
|
if (manager.addChange(change)) {
|
||||||
|
updateProperties();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean undo() {
|
||||||
|
if (manager.undo()) {
|
||||||
|
updateProperties();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean redo() {
|
||||||
|
if (manager.redo()) {
|
||||||
|
updateProperties();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mark() {
|
||||||
|
manager.mark();
|
||||||
|
setAtMarkedChange(manager.isAtMarkedChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unmark() {
|
||||||
|
manager.unmark();
|
||||||
|
setAtMarkedChange(manager.isAtMarkedChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the values of all properties to those present in the wrapped change manager.
|
||||||
|
*
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
private void updateProperties() {
|
||||||
|
setLastChange(manager.getLastChange().orElse(null));
|
||||||
|
setAtMarkedChange(manager.isAtMarkedChange());
|
||||||
|
setUndoAvailable(manager.isUndoAvailable());
|
||||||
|
setRedoAvailable(manager.isRedoAvailable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ReadOnlyObjectProperty<C> lastChangeProperty() {
|
||||||
|
return lastChange.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void setLastChange(C lastChange) {
|
||||||
|
this.lastChange.set(lastChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ReadOnlyBooleanProperty atMarkedChangeProperty() {
|
||||||
|
return atMarkedChange.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void setAtMarkedChange(boolean atMarkedChange) {
|
||||||
|
this.atMarkedChange.set(atMarkedChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ReadOnlyBooleanProperty undoAvailableProperty() {
|
||||||
|
return undoAvailable.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void setUndoAvailable(boolean undoAvailable) {
|
||||||
|
this.undoAvailable.set(undoAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ReadOnlyBooleanProperty redoAvailableProperty() {
|
||||||
|
return redoAvailable.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void setRedoAvailable(boolean redoAvailable) {
|
||||||
|
this.redoAvailable.set(redoAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<C> getChanges() {
|
||||||
|
return manager.getChanges();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package dev.kske.undoredo.javafx;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
|
||||||
|
import dev.kske.undoredo.core.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A change manager that exposes its state through JavaFX properties, thereby allowing a direct
|
||||||
|
* integration of Undo-Redo with JavaFX listeners and property bindings.
|
||||||
|
*
|
||||||
|
* @param <C> the change type to store in this change manager
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 0.0.1
|
||||||
|
* @see ChangeManagerWrapper
|
||||||
|
*/
|
||||||
|
public interface ObservableChangeManager<C extends Change> extends ChangeManager<C> {
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<C> lastChangeProperty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Optional<C> getLastChange() {
|
||||||
|
return Optional.ofNullable(lastChangeProperty().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyBooleanProperty atMarkedChangeProperty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isAtMarkedChange() {
|
||||||
|
return atMarkedChangeProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyBooleanProperty undoAvailableProperty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isUndoAvailable() {
|
||||||
|
return undoAvailableProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyBooleanProperty redoAvailableProperty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isRedoAvailable() {
|
||||||
|
return redoAvailableProperty().get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Contains JavaFX-based wrapper API for integrating Undo-Redo with JavaFX.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
package dev.kske.undoredo.javafx;
|
15
javafx/src/main/java/module-info.java
Normal file
15
javafx/src/main/java/module-info.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Contains JavaFX-based wrapper API for integrating Undo-Redo with JavaFX.
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
module dev.kske.undoredo.javafx {
|
||||||
|
|
||||||
|
exports dev.kske.undoredo.javafx;
|
||||||
|
|
||||||
|
opens dev.kske.undoredo.javafx to javafx.base;
|
||||||
|
|
||||||
|
requires transitive dev.kske.undoredo.core;
|
||||||
|
requires transitive javafx.base;
|
||||||
|
}
|
37
pom.xml
37
pom.xml
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>dev.kske</groupId>
|
<groupId>dev.kske</groupId>
|
||||||
<artifactId>undo-redo</artifactId>
|
<artifactId>undo-redo</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.1.0</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>Undo-Redo</name>
|
<name>Undo-Redo</name>
|
||||||
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>core</module>
|
<module>core</module>
|
||||||
|
<module>javafx</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
@ -47,6 +48,16 @@
|
|||||||
</roles>
|
</roles>
|
||||||
<timezone>Europe/Berlin</timezone>
|
<timezone>Europe/Berlin</timezone>
|
||||||
</developer>
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<name>Leon Hofmeister</name>
|
||||||
|
<email>leon@kske.dev</email>
|
||||||
|
<url>https://git.kske.dev/delvh</url>
|
||||||
|
<roles>
|
||||||
|
<role>architect</role>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
<timezone>Europe/Berlin</timezone>
|
||||||
|
</developer>
|
||||||
</developers>
|
</developers>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
@ -171,11 +182,31 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.0.0-M5</version>
|
<version>3.0.0-M5</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>--add-opens dev.kske.undoredo.core/dev.kske.undoredo.core=ALL-UNNAMED</argLine>
|
<argLine>${argLine} --add-opens dev.kske.undoredo.core/dev.kske.undoredo.core=ALL-UNNAMED</argLine>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Generate coverage report -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>0.8.7</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>prepare-agent</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>report</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>report</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user