From c44ceec05df02445647a869c1c4837239d329ba8 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Sat, 9 Sep 2017 18:02:26 -0700 Subject: [PATCH 1/2] Add test: UI-initiated all-text deletion should not throw exception --- .../richtext/api/MiscellaneousTests.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java new file mode 100644 index 000000000..2d4a4e106 --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java @@ -0,0 +1,38 @@ +package org.fxmisc.richtext.api; + +import com.nitorcreations.junit.runners.NestedRunner; +import javafx.stage.Stage; +import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static javafx.scene.input.KeyCode.DELETE; + +@RunWith(NestedRunner.class) +public class MiscellaneousTests { + + public class WhenAreaEndsWithEmptyLine extends InlineCssTextAreaAppTest { + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + area.replaceText(0, 0, "abc\n"); + } + + public class AndAllTextIsSelected { + + @Before + public void selectAllText() { + interact(() -> area.selectAll()); + } + + + @Test + public void pressingDeleteShouldNotThrowException() { + push(DELETE); + } + + } + } +} From e1fbe4a5f12dbce922a3968b851008e11777dc2d Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Sat, 9 Sep 2017 20:50:40 -0700 Subject: [PATCH 2/2] Revert to using original approach to update selection's 2D positions For some reason, updating these positions using `Val.create(supplier, internalRange.invalidations)` (alternative current/new approach) does not work. It seems that `Val` handles invalidation slightly differently --- .../org/fxmisc/richtext/SelectionImpl.java | 62 +++++++++---------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java index 55baf1f25..000f1d168 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionImpl.java @@ -46,7 +46,7 @@ final class SelectionImpl implements Selection { @Override public final int getLength() { return length.getValue(); } @Override public final ObservableValue lengthProperty() { return length; } - private final SuspendableVal paragraphSpan; + private final Val paragraphSpan; @Override public final int getParagraphSpan() { return paragraphSpan.getValue(); } @Override public final ObservableValue paragraphSpanProperty() { return paragraphSpan; } @@ -63,11 +63,11 @@ final class SelectionImpl implements Selection { @Override public final int getStartPosition() { return startPosition.getValue(); } @Override public final ObservableValue startPositionProperty() { return startPosition; } - private final SuspendableVal startParagraphIndex; + private final Val startParagraphIndex; @Override public final int getStartParagraphIndex() { return startParagraphIndex.getValue(); } @Override public final ObservableValue startParagraphIndexProperty() { return startParagraphIndex; } - private final SuspendableVal startColumnPosition; + private final Val startColumnPosition; @Override public final int getStartColumnPosition() { return startColumnPosition.getValue(); } @Override public final ObservableValue startColumnPositionProperty() { return startColumnPosition; } @@ -76,11 +76,11 @@ final class SelectionImpl implements Selection { @Override public final int getEndPosition() { return endPosition.getValue(); } @Override public final ObservableValue endPositionProperty() { return endPosition; } - private final SuspendableVal endPararagraphIndex; - @Override public final int getEndParagraphIndex() { return endPararagraphIndex.getValue(); } - @Override public final ObservableValue endParagraphIndexProperty() { return endPararagraphIndex; } + private final Val endParagraphIndex; + @Override public final int getEndParagraphIndex() { return endParagraphIndex.getValue(); } + @Override public final ObservableValue endParagraphIndexProperty() { return endParagraphIndex; } - private final SuspendableVal endColumnPosition; + private final Val endColumnPosition; @Override public final int getEndColumnPosition() { return endColumnPosition.getValue(); } @Override public final ObservableValue endColumnPositionProperty() { return endColumnPosition; } @@ -98,6 +98,9 @@ final class SelectionImpl implements Selection { private final Var internalRange; private final EventStream dirty; + private final Var start2DPosition; + private final Val end2DPosition; + private Subscription subscription = () -> {}; public SelectionImpl(GenericStyledArea area) { @@ -126,32 +129,30 @@ public SelectionImpl(GenericStyledArea area, SuspendableNo dependent selectedDocument = documentVal.suspendable(); selectedText = documentVal.map(StyledDocument::getText).suspendable(); - Val> positions = internalRange.map(sel -> { - Position start2D = area.offsetToPosition(sel.getStart(), Forward); - Position end2D = sel.getLength() == 0 - ? start2D - : start2D.offsetBy(sel.getLength(), Backward); - return Tuples.t(start2D, end2D); + start2DPosition = Var.newSimpleVar(position(0, 0)); + end2DPosition = start2DPosition.map(startPos2D -> + getLength() == 0 + ? startPos2D + : startPos2D.offsetBy(getLength(), Backward) + ); + + internalRange.addListener(obs -> { + IndexRange sel = internalRange.getValue(); + start2DPosition.setValue(area.offsetToPosition(sel.getStart(), Forward)); }); startPosition = internalRange.map(IndexRange::getStart).suspendable(); - - Val start2D = positions.map(Tuple2::get1); - Val startPar = start2D.map(Position::getMajor); - startParagraphIndex = startPar.suspendable(); - startColumnPosition = start2D.map(Position::getMinor).suspendable(); + startParagraphIndex = start2DPosition.map(Position::getMajor); + startColumnPosition = start2DPosition.map(Position::getMinor); endPosition = internalRange.map(IndexRange::getEnd).suspendable(); + endParagraphIndex = end2DPosition.map(Position::getMajor); + endColumnPosition = end2DPosition.map(Position::getMinor); - Val end2D = positions.map(Tuple2::get2); - Val endPar = end2D.map(Position::getMajor); - endPararagraphIndex = endPar.suspendable(); - endColumnPosition = end2D.map(Position::getMinor).suspendable(); - - paragraphSpan = Val.create( - () -> getEndParagraphIndex() - getStartParagraphIndex() + 1, - startPar, endPar - ).suspendable(); + paragraphSpan = Val.combine( + startParagraphIndex, endParagraphIndex, + (startP, endP) -> endP - startP + 1 + ); dirty = merge( invalidationsOf(rangeProperty()), @@ -205,14 +206,7 @@ public SelectionImpl(GenericStyledArea area, SuspendableNo dependent // first, so it's released last beingUpdated, - paragraphSpan, - - endColumnPosition, - endPararagraphIndex, endPosition, - - startColumnPosition, - startParagraphIndex, startPosition, selectedText,