Skip to content

Commit

Permalink
update to accommodate multiple selections
Browse files Browse the repository at this point in the history
  • Loading branch information
Jugen committed Sep 4, 2024
1 parent de63b02 commit 43a987f
Showing 1 changed file with 75 additions and 35 deletions.
110 changes: 75 additions & 35 deletions richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,18 @@ public ObjectProperty<Paint> highlightTextFillProperty() {
Val<Double> leftInset = Val.map(insetsProperty(), Insets::getLeft);
Val<Double> topInset = Val.map(insetsProperty(), Insets::getTop);

ChangeListener<IndexRange> selectionRangeListener = (obs, ov, nv) -> requestLayout();
ChangeListener<IndexRange> selectionRangeListener = (obs, prevRange, nv) -> {
resetTextSelection(prevRange);
requestLayout();
};
selectionPathListener = change -> {
if (change.wasRemoved()) {
SelectionPath p = change.getValueRemoved();
p.rangeProperty().removeListener(selectionRangeListener);
p.layoutXProperty().unbind();
p.layoutYProperty().unbind();

resetTextSelection(p.rangeProperty().getValue());
getChildren().remove(p);
}
if (change.wasAdded()) {
Expand Down Expand Up @@ -139,22 +143,15 @@ public ObjectProperty<Paint> highlightTextFillProperty() {
};
carets.addListener( caretNodeListener );

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

// populate with text nodes
par.getStyledSegments().stream().map(nodeFactory).forEach(n -> {
if (n instanceof TextExt) {
TextExt t = (TextExt) n;
// XXX: binding selectionFill to textFill,
// see the note at highlightTextFill
t.selectionFillProperty().bind(highlightTextFill);
}
getChildren().add(n);
Expand Down Expand Up @@ -231,7 +228,7 @@ void dispose() {
carets.removeListener( caretNodeListener );

getChildren().stream().filter( n -> n instanceof TextExt ).map( n -> (TextExt) n )
.forEach( t -> t.selectionFillProperty().unbind() );
.forEach( t -> t.selectionFillProperty().unbind() );

try { getChildren().clear(); }
catch ( Exception EX ) {}
Expand Down Expand Up @@ -336,6 +333,7 @@ private void updateAllSelectionShapes() {

private void updateSingleSelection(SelectionPath path) {
path.getElements().setAll(getRangeShapeSafely(path.rangeProperty().getValue()));
updateTextSelection(path);
}

private PathElement[] getRangeShapeSafely(IndexRange range) {
Expand Down Expand Up @@ -435,32 +433,75 @@ private PathElement[] createRectangle(double topLeftX, double topLeftY, double b
};
}


// XXX: Because of JDK bug https://bugs.openjdk.java.net/browse/JDK-8149134
// this does not work correctly if a paragraph contains more than one segment
// and the selection is (also) in the second or later segments.
// Visually the text color of the selection may be mix black & white.
private void updateTextSelection() {
int selStart = selection.get().getStart();
int selEnd = selection.get().getEnd();
private void updateTextSelection(SelectionPath selection)
{
IndexRange range = selection.rangeProperty().getValue();
if (range.getLength() == 0) return;

final int selStart = range.getStart();
final int selEnd = range.getEnd();
int charSoFar = 0;

for (Node node : getChildren())
{
if (node instanceof TextExt)
{
TextExt text = (TextExt) node;
int len = text.getText().length();
int end = charSoFar + len;

if (end > selStart)
{
// TODO text.setSelectionFill(selection.getTextFill());

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

int textSelStart = Math.max(start, selStart);
int textSelEnd = Math.min(end, selEnd);
if (textSelEnd > textSelStart) {
textSelStart -= start;
textSelEnd -= start;
} else {
textSelStart = textSelEnd = -1;
if (selEnd > end) text.setSelectionEnd(len);
else
{
text.setSelectionEnd(selEnd-charSoFar);
break;
}
}
charSoFar = end;
}
text.setImpl_selectionStart(textSelStart);
text.setImpl_selectionEnd(textSelEnd);
else if (node.isManaged()) // custom user nodes
{
charSoFar++;
}
}
}

start = end;
private void resetTextSelection(IndexRange range)
{
final int selStart = range.getStart();
final int selEnd = range.getEnd();
int charSoFar = 0;

for (Node node : getChildren())
{
if (node instanceof TextExt)
{
TextExt text = (TextExt) node;
charSoFar += text.getText().length();

if (charSoFar >= selStart)
{
text.setSelectionStart(-1);
text.setSelectionEnd(-1);
if (charSoFar >= selEnd) break;
}
}
else if (node.isManaged()) // custom user nodes
{
charSoFar++;
}
}
}

Expand Down Expand Up @@ -511,9 +552,8 @@ public String toString() {
@Override
protected void layoutChildren() {
super.layoutChildren();
updateCaretShape();
updateSelectionShape();
updateTextSelection();
updateAllCaretShapes();
updateAllSelectionShapes();
updateBackgroundShapes();
}

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

if (ranges.isEmpty()) {
addNewValueRange.run();;
addNewValueRange.run();
} else {
int lastIndex = ranges.size() - 1;
Tuple2<T, IndexRange> lastShapeValueRange = ranges.get(lastIndex);
Expand Down

0 comments on commit 43a987f

Please sign in to comment.