Skip to content

Commit 494b398

Browse files
committed
update to accommodate multiple selections
1 parent de63b02 commit 494b398

File tree

1 file changed

+75
-34
lines changed

1 file changed

+75
-34
lines changed

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

+75-34
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ public ObjectProperty<Paint> highlightTextFillProperty() {
104104
p.layoutYProperty().unbind();
105105

106106
getChildren().remove(p);
107+
resetTextSelection(p);
107108
}
108109
if (change.wasAdded()) {
109110
SelectionPath p = change.getValueAdded();
@@ -139,22 +140,15 @@ public ObjectProperty<Paint> highlightTextFillProperty() {
139140
};
140141
carets.addListener( caretNodeListener );
141142

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-
// });
143+
highlightTextFill.addListener((ob,oldFill,newFill) -> getChildren().stream()
144+
.filter( n -> n instanceof TextExt).map( n -> (TextExt) n )
145+
.forEach( t -> t.selectionFillProperty().set(newFill) )
146+
);
151147

152148
// populate with text nodes
153149
par.getStyledSegments().stream().map(nodeFactory).forEach(n -> {
154150
if (n instanceof TextExt) {
155151
TextExt t = (TextExt) n;
156-
// XXX: binding selectionFill to textFill,
157-
// see the note at highlightTextFill
158152
t.selectionFillProperty().bind(highlightTextFill);
159153
}
160154
getChildren().add(n);
@@ -231,7 +225,7 @@ void dispose() {
231225
carets.removeListener( caretNodeListener );
232226

233227
getChildren().stream().filter( n -> n instanceof TextExt ).map( n -> (TextExt) n )
234-
.forEach( t -> t.selectionFillProperty().unbind() );
228+
.forEach( t -> t.selectionFillProperty().unbind() );
235229

236230
try { getChildren().clear(); }
237231
catch ( Exception EX ) {}
@@ -336,6 +330,7 @@ private void updateAllSelectionShapes() {
336330

337331
private void updateSingleSelection(SelectionPath path) {
338332
path.getElements().setAll(getRangeShapeSafely(path.rangeProperty().getValue()));
333+
updateTextSelection(path);
339334
}
340335

341336
private PathElement[] getRangeShapeSafely(IndexRange range) {
@@ -435,32 +430,79 @@ private PathElement[] createRectangle(double topLeftX, double topLeftY, double b
435430
};
436431
}
437432

433+
438434
// XXX: Because of JDK bug https://bugs.openjdk.java.net/browse/JDK-8149134
439435
// this does not work correctly if a paragraph contains more than one segment
440436
// and the selection is (also) in the second or later segments.
441437
// 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();
438+
private void updateTextSelection(SelectionPath selection)
439+
{
440+
IndexRange range = selection.rangeProperty().getValue();
441+
if (range.getLength() == 0) {
442+
resetTextSelection(selection);
443+
return;
444+
}
445445

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();
446+
int selStart = range.getStart();
447+
int selEnd = range.getEnd();
448+
int charSoFar = 0;
451449

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;
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());
461+
462+
if (selStart <= charSoFar) text.setSelectionStart(0);
463+
else text.setSelectionStart(selStart-charSoFar);
464+
465+
if (selEnd > end) text.setSelectionEnd(len);
466+
else
467+
{
468+
text.setSelectionEnd(selEnd-charSoFar);
469+
break;
470+
}
471+
}
472+
charSoFar = end;
473+
}
474+
else if (node.isManaged()) // custom user nodes
475+
{
476+
charSoFar++;
459477
}
460-
text.setImpl_selectionStart(textSelStart);
461-
text.setImpl_selectionEnd(textSelEnd);
478+
}
479+
}
462480

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

@@ -511,9 +553,8 @@ public String toString() {
511553
@Override
512554
protected void layoutChildren() {
513555
super.layoutChildren();
514-
updateCaretShape();
515-
updateSelectionShape();
516-
updateTextSelection();
556+
updateAllCaretShapes();
557+
updateAllSelectionShapes();
517558
updateBackgroundShapes();
518559
}
519560

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

545586
if (ranges.isEmpty()) {
546-
addNewValueRange.run();;
587+
addNewValueRange.run();
547588
} else {
548589
int lastIndex = ranges.size() - 1;
549590
Tuple2<T, IndexRange> lastShapeValueRange = ranges.get(lastIndex);

0 commit comments

Comments
 (0)