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

Fix -fx-highlight-text-fill (selected text color) (issue #303) #428

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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 @@ -325,12 +325,6 @@ public class GenericStyledArea<PS, SEG, S> extends Region
* *
* ********************************************************************** */

/**
* Text color for highlighted text.
*/
private final StyleableObjectProperty<Paint> highlightTextFill
= new CustomStyleableProperty<>(Color.WHITE, "highlightTextFill", this, HIGHLIGHT_TEXT_FILL);

// editable property
private final BooleanProperty editable = new SimpleBooleanProperty(this, "editable", true) {
@Override
Expand Down Expand Up @@ -1908,7 +1902,6 @@ private Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>> createCell(

ParagraphBox<PS, SEG, S> box = new ParagraphBox<>(paragraph, applyParagraphStyle, nodeFactory);

box.highlightTextFillProperty().bind(highlightTextFill);
box.wrapTextProperty().bind(wrapTextProperty());
box.graphicFactoryProperty().bind(paragraphGraphicFactoryProperty());
box.graphicOffset.bind(virtualFlow.breadthOffsetProperty());
Expand Down Expand Up @@ -2001,7 +1994,6 @@ public void updateIndex(int index) {

@Override
public void dispose() {
box.highlightTextFillProperty().unbind();
box.wrapTextProperty().unbind();
box.graphicFactoryProperty().unbind();
box.graphicOffset.unbind();
Expand Down Expand Up @@ -2125,33 +2117,4 @@ private void suspendVisibleParsWhile(Runnable runnable) {
Suspendable.combine(beingUpdated, visibleParagraphs).suspendWhile(runnable);
}

/* ********************************************************************** *
* *
* CSS *
* *
* ********************************************************************** */

private static final CssMetaData<GenericStyledArea<?, ?, ?>, Paint> HIGHLIGHT_TEXT_FILL = new CustomCssMetaData<>(
"-fx-highlight-text-fill", StyleConverter.getPaintConverter(), Color.WHITE, s -> s.highlightTextFill
);

private static final List<CssMetaData<? extends Styleable, ?>> CSS_META_DATA_LIST;

static {
List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Region.getClassCssMetaData());

styleables.add(HIGHLIGHT_TEXT_FILL);

CSS_META_DATA_LIST = Collections.unmodifiableList(styleables);
}

@Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return CSS_META_DATA_LIST;
}

public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return CSS_META_DATA_LIST;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,6 @@ public String toString() {
);
}

public Property<Paint> highlightTextFillProperty() { return text.highlightTextFillProperty(); }

Paragraph<PS, SEG, S> getParagraph() {
return text.getParagraph();
}
Expand Down
113 changes: 82 additions & 31 deletions richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.reactfx.value.Val;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
Expand All @@ -33,7 +32,6 @@
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.IndexRange;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
Expand Down Expand Up @@ -62,14 +60,6 @@ class ParagraphText<PS, SEG, S> extends TextFlowExt {
private final MapChangeListener<? super Selection<PS, SEG, S>, ? super SelectionPath> selectionPathListener;
private final SetChangeListener<? super CaretNode> caretNodeListener;

// FIXME: changing it currently has not effect, because
// Text.selectionFillProperty().set(newFill) doesn't work
// properly for Text node inside a TextFlow (as of JDK8-b100).
private final ObjectProperty<Paint> highlightTextFill = new SimpleObjectProperty<>(Color.WHITE);
public ObjectProperty<Paint> highlightTextFillProperty() {
return highlightTextFill;
}

private Paragraph<PS, SEG, S> paragraph;

private final CustomCssShapeHelper<Paint> backgroundShapeHelper;
Expand All @@ -95,18 +85,25 @@ 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<Paint> selectionFillListener = (obs, ov, nv) -> requestLayout();
ChangeListener<IndexRange> selectionRangeListener = (obs, prevRange, nv) -> {
resetTextSelection(prevRange);
requestLayout();
};
selectionPathListener = change -> {
if (change.wasRemoved()) {
SelectionPath p = change.getValueRemoved();
p.textFillProperty().removeListener(selectionFillListener);
p.rangeProperty().removeListener(selectionRangeListener);
p.layoutXProperty().unbind();
p.layoutYProperty().unbind();

resetTextSelection(p.rangeProperty().getValue());
getChildren().remove(p);
}
if (change.wasAdded()) {
SelectionPath p = change.getValueAdded();
p.textFillProperty().addListener(selectionFillListener);
p.rangeProperty().addListener(selectionRangeListener);
p.layoutXProperty().bind(leftInset);
p.layoutYProperty().bind(topInset);
Expand Down Expand Up @@ -139,24 +136,8 @@ 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);
// }
// });

// 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(t.fillProperty());
}
getChildren().add(n);
});

Expand Down Expand Up @@ -230,9 +211,6 @@ void dispose() {
selections.removeListener( selectionPathListener );
carets.removeListener( caretNodeListener );

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

try { getChildren().clear(); }
catch ( Exception EX ) {}
}
Expand Down Expand Up @@ -336,6 +314,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,6 +414,78 @@ 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(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)
{
text.setSelectionFill(selection.getTextFill());

if (selStart <= charSoFar) text.setSelectionStart(0);
else text.setSelectionStart(selStart-charSoFar);

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

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++;
}
}
}

private void updateBackgroundShapes() {
int start = 0;

Expand Down Expand Up @@ -513,7 +564,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
15 changes: 15 additions & 0 deletions richtextfx/src/main/java/org/fxmisc/richtext/SelectionPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ public class SelectionPath extends Path {
private final StyleableObjectProperty<Paint> highlightFill
= new CustomStyleableProperty<>(Color.DODGERBLUE, "highlightFill", this, HIGHLIGHT_FILL);

private final StyleableObjectProperty<Paint> textFill
= new CustomStyleableProperty<>(Color.WHITE, "textFill", this, TEXT_FILL);

/**
* Background fill for highlighted/selected text. Can be styled using "-fx-highlight-fill".
*/
public final ObjectProperty<Paint> highlightFillProperty() { return highlightFill; }
public final Paint getHighlightFill() { return highlightFill.get(); }
public final void setHighlightFill(Paint paint) { highlightFill.set(paint); }

/**
* Text color for highlighted/selected text. Can be styled using "-fx-highlight-text-fill".
*/
public final ObjectProperty<Paint> textFillProperty() { return textFill; }
public final Paint getTextFill() { return textFill.get(); }
public final void setTextFill(Paint paint) { textFill.set(paint); }

private final Val<IndexRange> range;
final Val<IndexRange> rangeProperty() { return range; }

Expand All @@ -53,12 +63,17 @@ public String toString() {
"-fx-highlight-fill", StyleConverter.getPaintConverter(), Color.DODGERBLUE, s -> s.highlightFill
);

private static final CssMetaData<SelectionPath, Paint> TEXT_FILL = new CustomCssMetaData<>(
"-fx-highlight-text-fill", StyleConverter.getPaintConverter(), Color.WHITE, s -> s.textFill
);

private static final List<CssMetaData<? extends Styleable, ?>> CSS_META_DATA_LIST;

static {
List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Path.getClassCssMetaData());

styleables.add(HIGHLIGHT_FILL);
styleables.add(TEXT_FILL);

CSS_META_DATA_LIST = Collections.unmodifiableList(styleables);
}
Expand Down