Skip to content

Commit

Permalink
Provide suspendable undo manager and test
Browse files Browse the repository at this point in the history
  • Loading branch information
Jurgen committed Mar 16, 2020
1 parent c9a002b commit ebcce70
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import org.fxmisc.richtext.RichTextFXTestBase;
import org.fxmisc.richtext.StyledTextArea;
import org.fxmisc.richtext.model.SimpleEditableStyledDocument;
import org.fxmisc.richtext.model.RichTextChange;
import org.fxmisc.richtext.model.TextChange;
import org.fxmisc.richtext.util.UndoUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.reactfx.SuspendableYes;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
Expand Down Expand Up @@ -174,6 +176,20 @@ public void testForBug904() {
interact( area::undo ); // should not throw Unexpected change received exception
}

@Test
public void suspendable_UndoManager_skips_style_check() {

SuspendableYes suspendUndo = new SuspendableYes();
area.setUndoManager( UndoUtils.richTextSuspendableUndoManager( area, suspendUndo ) );
write( "some text\n" );
interact( () -> suspendUndo.suspendWhile( () -> area.setStyle( 5, 9, "-fx-font-weight: bold;" ) ) );
write( "new line" );
interact( area::undo ); // should not throw Unexpected change received exception

area.setUndoManager( UndoUtils.defaultUndoManager( area ) );
RichTextChange.skipStyleComparison( false );
}

}

public class UsingStyledTextArea extends RichTextFXTestBase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,31 @@ public final PlainTextChange toPlainTextChange() {
public final boolean isPlainTextIdentity() {
return removed.getText().equals(inserted.getText());
}

private static boolean skipStyleComparison = false;

public static void skipStyleComparison( boolean value )
{
skipStyleComparison = value;
}

/*
* This gets used, by the default UndoManagers supplied by UndoUtils,
* to check that a submitted undo/redo matches the change reported.
*/
public boolean equals( Object other )
{
if( skipStyleComparison && other instanceof RichTextChange )
{
PlainTextChange otherChange = ((RichTextChange) other).toPlainTextChange();
boolean matches = toPlainTextChange().equals( otherChange );
if ( ! matches ) System.err.println(
"Plain text comparison mismatch caused by text change"
+" during undo manager suspension (styling ignored)."
);
return matches;
}

return super.equals( other );
}
}
35 changes: 35 additions & 0 deletions richtextfx/src/main/java/org/fxmisc/richtext/util/UndoUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import org.fxmisc.richtext.model.TextChange;
import org.fxmisc.undo.UndoManager;
import org.fxmisc.undo.UndoManagerFactory;
import org.fxmisc.undo.impl.MultiChangeUndoManagerImpl;
import org.fxmisc.undo.impl.UnlimitedChangeQueue;
import org.reactfx.SuspendableYes;
import org.reactfx.value.Val;

import javafx.beans.value.ObservableBooleanValue;
Expand Down Expand Up @@ -121,6 +124,38 @@ public static <PS, SEG, S> UndoManager<List<RichTextChange<PS, SEG, S>>> richTex
preventMergeDelay);
};

/**
* Returns an UndoManager with an unlimited history that can undo/redo {@link RichTextChange}s. New changes
* emitted from the stream will not be merged with the previous change after {@link #DEFAULT_PREVENT_MERGE_DELAY}
* <p><b>Note</b>: that <u>only styling changes</u> may occur <u>during suspension</u> of the undo manager.
*/
public static <PS, SEG, S> UndoManager<List<RichTextChange<PS, SEG, S>>> richTextSuspendableUndoManager(
GenericStyledArea<PS, SEG, S> area, SuspendableYes suspendUndo) {
return richTextSuspendableUndoManager(area, DEFAULT_PREVENT_MERGE_DELAY, suspendUndo);
}

/**
* Returns an UndoManager with an unlimited history that can undo/redo {@link RichTextChange}s. New changes
* emitted from the stream will not be merged with the previous change after {@code preventMergeDelay}.
* <p><b>Note</b>: that <u>only styling changes</u> may occur <u>during suspension</u> of the undo manager.
*/
public static <PS, SEG, S> UndoManager<List<RichTextChange<PS, SEG, S>>> richTextSuspendableUndoManager(
GenericStyledArea<PS, SEG, S> area, Duration preventMergeDelay, SuspendableYes suspendUndo) {

RichTextChange.skipStyleComparison( true );

return new MultiChangeUndoManagerImpl<>
(
new UnlimitedChangeQueue<>(),
TextChange::invert,
applyMultiRichTextChange(area),
TextChange::mergeWith,
TextChange::isIdentity,
area.multiRichChanges().conditionOn(suspendUndo),
preventMergeDelay
);
};

/**
* Returns an UndoManager with an unlimited history that can undo/redo {@link PlainTextChange}s. New changes
* emitted from the stream will not be merged with the previous change
Expand Down

0 comments on commit ebcce70

Please sign in to comment.