Skip to content

Commit 25662f1

Browse files
Merge pull request #526 from JordanMartinez/extractCaret3
Extract caret and selection to separate classes
2 parents 9db6f92 + 492c35a commit 25662f1

13 files changed

Lines changed: 1332 additions & 321 deletions

File tree

richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/NavigationTests.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.testfx.util.WaitForAsyncUtils;
1313

1414
import java.util.Arrays;
15+
import java.util.concurrent.Future;
1516
import java.util.concurrent.TimeUnit;
1617
import java.util.concurrent.TimeoutException;
1718

@@ -248,6 +249,18 @@ private void moveCaretTo(int position) {
248249
area.moveTo(position);
249250
}
250251

252+
private void waitForMultiLineRegistration() throws TimeoutException {
253+
// When the stage's width changes, TextFlow does not properly handle API calls to a
254+
// multi-line paragraph immediately. So, wait until it correctly responds
255+
// to the stage width change
256+
Future<Void> textFlowIsReady = WaitForAsyncUtils.asyncFx(() -> {
257+
while (area.getParagraphLinesCount(0) != lines.length) {
258+
sleep(10);
259+
}
260+
});
261+
WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS, textFlowIsReady);
262+
}
263+
251264
@Override
252265
public void start(Stage stage) throws Exception {
253266
super.start(stage);
@@ -266,12 +279,7 @@ public class NoModifiers {
266279

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

277285
@Test
@@ -324,12 +332,7 @@ public class ShortcutDown {
324332

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

334337
press(SHORTCUT);
335338
}
@@ -385,12 +388,7 @@ public class ShiftDown {
385388

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

395393
press(SHIFT);
396394
}
@@ -445,12 +443,7 @@ public class ShortcutShiftDown {
445443

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

455448
press(SHORTCUT, SHIFT);
456449
}

richtextfx/src/integrationTest/java/org/fxmisc/richtext/view/MiscellaneousAPITests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import javafx.geometry.Point2D;
77
import javafx.geometry.Pos;
88
import javafx.stage.Stage;
9+
import org.fxmisc.richtext.Caret;
910
import org.fxmisc.richtext.InlineCssTextAreaAppTest;
10-
import org.fxmisc.richtext.ViewActions.CaretVisibility;
1111
import org.fxmisc.richtext.model.NavigationActions;
1212
import org.junit.Before;
1313
import org.junit.Test;
@@ -83,7 +83,7 @@ public void start(Stage stage) throws Exception {
8383
super.start(stage);
8484

8585
// insure caret is always visible
86-
area.setShowCaret(CaretVisibility.ON);
86+
area.setShowCaret(Caret.CaretVisibility.ON);
8787

8888
StringBuilder sb = new StringBuilder();
8989
for (int i = 0; i < 50; i++) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package org.fxmisc.richtext;
2+
3+
import javafx.beans.value.ObservableValue;
4+
import org.fxmisc.richtext.model.StyledDocument;
5+
6+
/**
7+
* An object for encapsulating a selection in a given area that is bound to an underlying caret. In other words,
8+
* {@link #selectRange(int, int) selecting some range in the area} will move a caret in the same call.
9+
*
10+
* <p>
11+
* <b>"Position"</b> refers to the place in-between characters. In other words, every {@code "|"} in
12+
* {@code "|t|e|x|t|"} is a valid position. There are two kinds of positions used here:</p>
13+
* <ol>
14+
* <li>
15+
* {@link #getStartPosition()}/{@link #getEndPosition()}, which refers to a position somewhere in the
16+
* entire area's content. It's bounds are {@code 0 <= x < area.getLength()}.
17+
* </li>
18+
* <li>
19+
* {@link #getStartColumnPosition()}/{@link #getEndColumnPosition()}, which refers to a position
20+
* somewhere in the current paragraph. It's bounds are {@code 0 <= x < area.getParagraphLength(index)}.
21+
* </li>
22+
* </ol>
23+
*
24+
* Note: when parameter names are "position" without the "column" prefix, they refer to the position in the entire area.
25+
*
26+
* <p>
27+
* The selection is typically made using the {@link #getAnchorPosition() anchor's position} and
28+
* the underlying {@link Caret#getPosition() caret's position}. Hence, {@link #selectRange(int, int)}
29+
* is the typical method to use, although {@link #selectRange0(int, int)} can also be used.
30+
* </p>
31+
* <p>
32+
* Be careful about calling the underlying {@link Caret#moveTo(int)} method. This will displace the caret
33+
* from the selection bounds and may lead to undesirable/unexpected behavior. If this is done, a
34+
* {@link #selectRange(int, int)} call will reposition the caret, so that it is either the start or end
35+
* bound of this selection.
36+
* </p>
37+
*
38+
* <p>
39+
* For type safety, {@link #getSelectedDocument()} requires the same generic types from {@link StyledDocument}.
40+
* This means that one must write a lot of boilerplate for the generics:
41+
* {@code BoundedSelection<Collection<String>, StyledText<Collection<String>>, Collection<String>> selection}.
42+
* However, this is only necessary if one is using {@link #getSelectedDocument()} or
43+
* {@link #selectedDocumentProperty()}. <b>If you are not going to use the "selectedDocument" getter or property,
44+
* then just write the much simpler {@code BoundedSelection<?, ?, ?> selection}</b>.
45+
* </p>
46+
*
47+
* @param <PS> type for {@link StyledDocument}'s paragraph style; only necessary when using the "selectedDocument"
48+
* getter or property
49+
* @param <SEG> type for {@link StyledDocument}'s segment type; only necessary when using the "selectedDocument"
50+
* getter or property
51+
* @param <S> type for {@link StyledDocument}'s segment style; only necessary when using the "selectedDocument"
52+
* getter or property
53+
*/
54+
public interface BoundedSelection<PS, SEG, S> extends UnboundedSelection<PS, SEG, S> {
55+
56+
@Override
57+
default boolean isBoundToCaret() {
58+
return true;
59+
}
60+
61+
@Override
62+
default BoundedSelection asBoundedSelection() {
63+
return this;
64+
}
65+
66+
int getAnchorPosition();
67+
ObservableValue<Integer> anchorPositionProperty();
68+
69+
int getAnchorParIndex();
70+
ObservableValue<Integer> anchorParIndexProperty();
71+
72+
int getAnchorColPosition();
73+
ObservableValue<Integer> anchorColPositionProperty();
74+
75+
/**
76+
* Positions the anchor and caretPosition explicitly,
77+
* effectively creating a selection.
78+
*
79+
* <p><b>Caution:</b> see {@link org.fxmisc.richtext.model.TextEditingArea#getAbsolutePosition(int, int)}
80+
* to know how the column index argument can affect the returned position.</p>
81+
*/
82+
void selectRange(int anchorParagraph, int anchorColumn, int caretParagraph, int caretColumn);
83+
84+
/**
85+
* Positions the anchor and caretPosition explicitly,
86+
* effectively creating a selection.
87+
*/
88+
void selectRange(int anchorPosition, int caretPosition);
89+
90+
}

0 commit comments

Comments
 (0)