Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract caret and selection to separate classes #526

Merged
merged 6 commits into from
Jun 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.testfx.util.WaitForAsyncUtils;

import java.util.Arrays;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

Expand Down Expand Up @@ -248,6 +249,18 @@ private void moveCaretTo(int position) {
area.moveTo(position);
}

private void waitForMultiLineRegistration() throws TimeoutException {
// When the stage's width changes, TextFlow does not properly handle API calls to a
// multi-line paragraph immediately. So, wait until it correctly responds
// to the stage width change
Future<Void> textFlowIsReady = WaitForAsyncUtils.asyncFx(() -> {
while (area.getParagraphLinesCount(0) != lines.length) {
sleep(10);
}
});
WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS, textFlowIsReady);
}

@Override
public void start(Stage stage) throws Exception {
super.start(stage);
Expand All @@ -266,12 +279,7 @@ public class NoModifiers {

@Before
public void setup() throws TimeoutException {
// When the stage's width changes, TextFlow does not properly handle API calls to a
// multi-line paragraph immediately. So, wait until it correctly responds
// to the stage width change
WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS,
() -> area.getParagraphLinesCount(0) == lines.length
);
waitForMultiLineRegistration();
}

@Test
Expand Down Expand Up @@ -324,12 +332,7 @@ public class ShortcutDown {

@Before
public void setup() throws TimeoutException {
// When the stage's width changes, TextFlow does not properly handle API calls to a
// multi-line paragraph immediately. So, wait until it correctly responds
// to the stage width change
WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS,
() -> area.getParagraphLinesCount(0) == lines.length
);
waitForMultiLineRegistration();

press(SHORTCUT);
}
Expand Down Expand Up @@ -385,12 +388,7 @@ public class ShiftDown {

@Before
public void setup() throws TimeoutException {
// When the stage's width changes, TextFlow does not properly handle API calls to a
// multi-line paragraph immediately. So, wait until it correctly responds
// to the stage width change
WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS,
() -> area.getParagraphLinesCount(0) == lines.length
);
waitForMultiLineRegistration();

press(SHIFT);
}
Expand Down Expand Up @@ -445,12 +443,7 @@ public class ShortcutShiftDown {

@Before
public void setup() throws TimeoutException {
// When the stage's width changes, TextFlow does not properly handle API calls to a
// multi-line paragraph immediately. So, wait until it correctly responds
// to the stage width change
WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS,
() -> area.getParagraphLinesCount(0) == lines.length
);
waitForMultiLineRegistration();

press(SHORTCUT, SHIFT);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import org.fxmisc.richtext.Caret;
import org.fxmisc.richtext.InlineCssTextAreaAppTest;
import org.fxmisc.richtext.ViewActions.CaretVisibility;
import org.fxmisc.richtext.model.NavigationActions;
import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -83,7 +83,7 @@ public void start(Stage stage) throws Exception {
super.start(stage);

// insure caret is always visible
area.setShowCaret(CaretVisibility.ON);
area.setShowCaret(Caret.CaretVisibility.ON);

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 50; i++) {
Expand Down
90 changes: 90 additions & 0 deletions richtextfx/src/main/java/org/fxmisc/richtext/BoundedSelection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.fxmisc.richtext;

import javafx.beans.value.ObservableValue;
import org.fxmisc.richtext.model.StyledDocument;

/**
* An object for encapsulating a selection in a given area that is bound to an underlying caret. In other words,
* {@link #selectRange(int, int) selecting some range in the area} will move a caret in the same call.
*
* <p>
* <b>"Position"</b> refers to the place in-between characters. In other words, every {@code "|"} in
* {@code "|t|e|x|t|"} is a valid position. There are two kinds of positions used here:</p>
* <ol>
* <li>
* {@link #getStartPosition()}/{@link #getEndPosition()}, which refers to a position somewhere in the
* entire area's content. It's bounds are {@code 0 <= x < area.getLength()}.
* </li>
* <li>
* {@link #getStartColumnPosition()}/{@link #getEndColumnPosition()}, which refers to a position
* somewhere in the current paragraph. It's bounds are {@code 0 <= x < area.getParagraphLength(index)}.
* </li>
* </ol>
*
* Note: when parameter names are "position" without the "column" prefix, they refer to the position in the entire area.
*
* <p>
* The selection is typically made using the {@link #getAnchorPosition() anchor's position} and
* the underlying {@link Caret#getPosition() caret's position}. Hence, {@link #selectRange(int, int)}
* is the typical method to use, although {@link #selectRange0(int, int)} can also be used.
* </p>
* <p>
* Be careful about calling the underlying {@link Caret#moveTo(int)} method. This will displace the caret
* from the selection bounds and may lead to undesirable/unexpected behavior. If this is done, a
* {@link #selectRange(int, int)} call will reposition the caret, so that it is either the start or end
* bound of this selection.
* </p>
*
* <p>
* For type safety, {@link #getSelectedDocument()} requires the same generic types from {@link StyledDocument}.
* This means that one must write a lot of boilerplate for the generics:
* {@code BoundedSelection<Collection<String>, StyledText<Collection<String>>, Collection<String>> selection}.
* However, this is only necessary if one is using {@link #getSelectedDocument()} or
* {@link #selectedDocumentProperty()}. <b>If you are not going to use the "selectedDocument" getter or property,
* then just write the much simpler {@code BoundedSelection<?, ?, ?> selection}</b>.
* </p>
*
* @param <PS> type for {@link StyledDocument}'s paragraph style; only necessary when using the "selectedDocument"
* getter or property
* @param <SEG> type for {@link StyledDocument}'s segment type; only necessary when using the "selectedDocument"
* getter or property
* @param <S> type for {@link StyledDocument}'s segment style; only necessary when using the "selectedDocument"
* getter or property
*/
public interface BoundedSelection<PS, SEG, S> extends UnboundedSelection<PS, SEG, S> {

@Override
default boolean isBoundToCaret() {
return true;
}

@Override
default BoundedSelection asBoundedSelection() {
return this;
}

int getAnchorPosition();
ObservableValue<Integer> anchorPositionProperty();

int getAnchorParIndex();
ObservableValue<Integer> anchorParIndexProperty();

int getAnchorColPosition();
ObservableValue<Integer> anchorColPositionProperty();

/**
* Positions the anchor and caretPosition explicitly,
* effectively creating a selection.
*
* <p><b>Caution:</b> see {@link org.fxmisc.richtext.model.TextEditingArea#getAbsolutePosition(int, int)}
* to know how the column index argument can affect the returned position.</p>
*/
void selectRange(int anchorParagraph, int anchorColumn, int caretParagraph, int caretColumn);

/**
* Positions the anchor and caretPosition explicitly,
* effectively creating a selection.
*/
void selectRange(int anchorPosition, int caretPosition);

}
Loading