I have a JavaFX TextFlow
wrapped in a ScrollPane
, and I am trying to get it to automatically scroll to the bottom whenever a new Text
is added to the TextFlow
.
I have tried attaching listeners maxing the ScrollPane
's vvalue
to:
ScrollPane
's vvalue
property itself.
ScrollPane
to the bottom, which is not desired. ScrollPane
's viewportBounds
property.
TextFlow
's children list.
Text
was added, strangely. I have tried explicitly requesting layout before scrolling, but this had no effectFor an extra big thank you, I'd like to scroll such that if the new addition is too large to display at once, the ScrollPane
scrolls such that the new addition is at the top, and the user should scroll manually to see the 'overflow'. As I said, this would be an added bonus, just scrolling to the bottom would be just fine as I don't expect such large additions (regularly).
And no, I'm not switching to a TextArea
, in which this would be relatively simple. I want to be able to easily add regular, bold and italic text to the TextFlow
, and TextArea
doesn't support this. I also tried Thomas Mikula's RichTextFX, but
StackOverflowError
s on internal code without explanation.So any solution that will work with TextFlow
would be greatly appreciated.
EDIT: Solution, as requested:
private ScrollPane textContainer;
private TextFlow text;
public BaseGui() {
//....
text.getChildren().addListener(
(ListChangeListener<Node>) ((change) -> {
text.layout();
textContainer.layout();
textContainer.setVvalue(1.0f);
}));
textContainer.setContent(text);
//....
}
public void appendBold(String msg) { //similar for italic and regular
append(msg, "-fx-font-weight: bold");
}
private synchronized void append(String msg, String style) {
Platform.runLater(() -> {
Text t = new Text(msg);
t.setFont(segoe(13));
if (!style.equals("")) {
t.setStyle(style);
}
text.getChildren().add(t);
});
}
It won't win any awards for code style, but as this is a personal project I don't really care.
Modify your 3rd approach to use layout()
instead of requestLayout()
.
requestLayout()
marks the layout as dirty and causes a re-layout on the next pulse. In the code
requestLayout();
doSomethingThatDependsOnLayout();
doSomethingThatDependsOnLayout()
will see the old layout.
layout()
performs the layout immediately (synchronously), but only if the layout is dirty. (In your case, change of TextFlow's text marks its layout as dirty.) In the code
layout();
doSomethingThatDependsOnLayout();
doSomethingThatDependsOnLayout()
will see the new layout.