Skip to content

Commit 43a987f

Browse files
committed
update to accommodate multiple selections
1 parent de63b02 commit 43a987f

File tree

1 file changed

+75
-35
lines changed

1 file changed

+75
-35
lines changed

richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java

+75-35
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,18 @@ public ObjectProperty<Paint> highlightTextFillProperty() {
9595
Val<Double> leftInset = Val.map(insetsProperty(), Insets::getLeft);
9696
Val<Double> topInset = Val.map(insetsProperty(), Insets::getTop);
9797

98-
ChangeListener<IndexRange> selectionRangeListener = (obs, ov, nv) -> requestLayout();
98+
ChangeListener<IndexRange> selectionRangeListener = (obs, prevRange, nv) -> {
99+
resetTextSelection(prevRange);
100+
requestLayout();
101+
};
99102
selectionPathListener = change -> {
100103
if (change.wasRemoved()) {
101104
SelectionPath p = change.getValueRemoved();
102105
p.rangeProperty().removeListener(selectionRangeListener);
103106
p.layoutXProperty().unbind();
104107
p.layoutYProperty().unbind();
105108

109+
resetTextSelection(p.rangeProperty().getValue());
106110
getChildren().remove(p);
107111
}
108112
if (change.wasAdded()) {
@@ -139,22 +143,15 @@ public ObjectProperty<Paint> highlightTextFillProperty() {
139143
};
140144
carets.addListener( caretNodeListener );
141145

142-
// XXX: see the note at highlightTextFill
143-
// highlightTextFill.addListener(new ChangeListener<Paint>() {
144-
// @Override
145-
// public void changed(ObservableValue<? extends Paint> observable,
146-
// Paint oldFill, Paint newFill) {
147-
// for(PumpedUpText text: textNodes())
148-
// text.selectionFillProperty().set(newFill);
149-
// }
150-
// });
146+
highlightTextFill.addListener((ob,oldFill,newFill) -> getChildren().stream()
147+
.filter( n -> n instanceof TextExt).map( n -> (TextExt) n )
148+
.forEach( t -> t.selectionFillProperty().set(newFill) )
149+
);
151150

152151
// populate with text nodes
153152
par.getStyledSegments().stream().map(nodeFactory).forEach(n -> {
154153
if (n instanceof TextExt) {
155154
TextExt t = (TextExt) n;
156-
// XXX: binding selectionFill to textFill,
157-
// see the note at highlightTextFill
158155
t.selectionFillProperty().bind(highlightTextFill);
159156
}
160157
getChildren().add(n);
@@ -231,7 +228,7 @@ void dispose() {
231228
carets.removeListener( caretNodeListener );
232229

233230
getChildren().stream().filter( n -> n instanceof TextExt ).map( n -> (TextExt) n )
234-
.forEach( t -> t.selectionFillProperty().unbind() );
231+
.forEach( t -> t.selectionFillProperty().unbind() );
235232

236233
try { getChildren().clear(); }
237234
catch ( Exception EX ) {}
@@ -336,6 +333,7 @@ private void updateAllSelectionShapes() {
336333

337334
private void updateSingleSelection(SelectionPath path) {
338335
path.getElements().setAll(getRangeShapeSafely(path.rangeProperty().getValue()));
336+
updateTextSelection(path);
339337
}
340338

341339
private PathElement[] getRangeShapeSafely(IndexRange range) {
@@ -435,32 +433,75 @@ private PathElement[] createRectangle(double topLeftX, double topLeftY, double b
435433
};
436434
}
437435

436+
438437
// XXX: Because of JDK bug https://bugs.openjdk.java.net/browse/JDK-8149134
439438
// this does not work correctly if a paragraph contains more than one segment
440439
// and the selection is (also) in the second or later segments.
441440
// Visually the text color of the selection may be mix black & white.
442-
private void updateTextSelection() {
443-
int selStart = selection.get().getStart();
444-
int selEnd = selection.get().getEnd();
441+
private void updateTextSelection(SelectionPath selection)
442+
{
443+
IndexRange range = selection.rangeProperty().getValue();
444+
if (range.getLength() == 0) return;
445+
446+
final int selStart = range.getStart();
447+
final int selEnd = range.getEnd();
448+
int charSoFar = 0;
449+
450+
for (Node node : getChildren())
451+
{
452+
if (node instanceof TextExt)
453+
{
454+
TextExt text = (TextExt) node;
455+
int len = text.getText().length();
456+
int end = charSoFar + len;
457+
458+
if (end > selStart)
459+
{
460+
// TODO text.setSelectionFill(selection.getTextFill());
445461

446-
int start = 0;
447-
FilteredList<Node> nodeList = getChildren().filtered(node -> node instanceof TextExt);
448-
for (Node node : nodeList) {
449-
TextExt text = (TextExt) node;
450-
int end = start + text.getText().length();
462+
if (selStart <= charSoFar) text.setSelectionStart(0);
463+
else text.setSelectionStart(selStart-charSoFar);
451464

452-
int textSelStart = Math.max(start, selStart);
453-
int textSelEnd = Math.min(end, selEnd);
454-
if (textSelEnd > textSelStart) {
455-
textSelStart -= start;
456-
textSelEnd -= start;
457-
} else {
458-
textSelStart = textSelEnd = -1;
465+
if (selEnd > end) text.setSelectionEnd(len);
466+
else
467+
{
468+
text.setSelectionEnd(selEnd-charSoFar);
469+
break;
470+
}
471+
}
472+
charSoFar = end;
459473
}
460-
text.setImpl_selectionStart(textSelStart);
461-
text.setImpl_selectionEnd(textSelEnd);
474+
else if (node.isManaged()) // custom user nodes
475+
{
476+
charSoFar++;
477+
}
478+
}
479+
}
462480

463-
start = end;
481+
private void resetTextSelection(IndexRange range)
482+
{
483+
final int selStart = range.getStart();
484+
final int selEnd = range.getEnd();
485+
int charSoFar = 0;
486+
487+
for (Node node : getChildren())
488+
{
489+
if (node instanceof TextExt)
490+
{
491+
TextExt text = (TextExt) node;
492+
charSoFar += text.getText().length();
493+
494+
if (charSoFar >= selStart)
495+
{
496+
text.setSelectionStart(-1);
497+
text.setSelectionEnd(-1);
498+
if (charSoFar >= selEnd) break;
499+
}
500+
}
501+
else if (node.isManaged()) // custom user nodes
502+
{
503+
charSoFar++;
504+
}
464505
}
465506
}
466507

@@ -511,9 +552,8 @@ public String toString() {
511552
@Override
512553
protected void layoutChildren() {
513554
super.layoutChildren();
514-
updateCaretShape();
515-
updateSelectionShape();
516-
updateTextSelection();
555+
updateAllCaretShapes();
556+
updateAllSelectionShapes();
517557
updateBackgroundShapes();
518558
}
519559

@@ -543,7 +583,7 @@ private void updateSharedShapeRange(T value, int start, int end, BiFunction<T, T
543583
Runnable addNewValueRange = () -> ranges.add(Tuples.t(value, new IndexRange(start, end)));
544584

545585
if (ranges.isEmpty()) {
546-
addNewValueRange.run();;
586+
addNewValueRange.run();
547587
} else {
548588
int lastIndex = ranges.size() - 1;
549589
Tuple2<T, IndexRange> lastShapeValueRange = ranges.get(lastIndex);

0 commit comments

Comments
 (0)