Wrap text in CodeArea does not refresh properly
I am trying to enable and disable text wrapping in CodeArea dynamically. Unfortunately, if horizontal scrollbar is already showing, enabling text wrapping does not refresh the CodeArea.
Also, if I change font size dynamically, and wrap text is enabled, the CodeArea does not refresh properly either, and there are line gaps in the text if width becomes less than viewport width when font size is decreased.
I am enabling and disabling text wrap using codeArea.setWrapText(wrapFlag);
I am changing font size using codeArea.setStyle("-fx-font-size: " + fontSize + "px");
I must note that when I scroll vertically after either of this for some time, the CodeArea is refreshed. Can you please help me with this.
You could try calling codeArea.requestLayout() after either setWrapText or setStyle.
I have already tried calling codeArea.requestLayout() but to no avail.
I have built a demo to explain the scenario.
You can copy a large text which is wider than the width of the CodeArea from anywhere into the CodeArea in the example below and then click on Wrap Text button to reproduce the issue.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.CodeArea;
public class WrapTest extends Application {
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Wrap Text Test");
// creating the layout
// main vbox holding everything together
VBox vBox = new VBox();
vBox.setPadding(new Insets(10));
vBox.setSpacing(10);
// anchor pane holding the scrollable CodeArea
AnchorPane anchorPane = new AnchorPane();
VBox.setVgrow(anchorPane, Priority.ALWAYS);
// the scrollable code area
CodeArea codeArea = new CodeArea();
VirtualizedScrollPane scrollPane = new VirtualizedScrollPane<>(codeArea);
AnchorPane.setTopAnchor(scrollPane, 0.0);
AnchorPane.setRightAnchor(scrollPane, 0.0);
AnchorPane.setBottomAnchor(scrollPane, 0.0);
AnchorPane.setLeftAnchor(scrollPane, 0.0);
codeArea.setStyle("-fx-font-size: 20px;");
codeArea.setStyle("-fx-border-color: lightgray;");
anchorPane.getChildren().add(scrollPane);
vBox.getChildren().add(anchorPane);
// the hbox button panel
HBox hBox = new HBox();
// the wrap text toggle button
ToggleButton wrapTextButton = new ToggleButton("Wrap Text");
wrapTextButton.selectedProperty().addListener((obs, oldVal, newVal) -> {
// toggle text wrap on the code area
codeArea.setWrapText(newVal);
// attempt to refresh layout
codeArea.requestLayout();
});
hBox.getChildren().add(wrapTextButton);
vBox.getChildren().add(hBox);
primaryStage.setScene(new Scene(vBox, 300, 275));
primaryStage.setMaximized(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thanks for the demo :-) So .... RichTextFX uses another library called Flowless which governs the width of the control.
Flowless doesn't process it's entire contents to determine the max width but only the visible items, so when setWrapText(true) is invoked Flowless doesn't recalculate all the items but only what is visible. But since all of the visible items are now shorter than the max width it doesn't reset (at least that's my understanding of what's happening - the code is complicated).
Unfortunately the original author of Flowless isn't maintaining the code any more and because I'm not familiar with the code it's awfully difficult and time consuming to figure out how it works or how to change it. Besides it's not even clear which side is misbehaving RichTextFX or Flowless.
That's the bad news, the good news is that there's a horrible hack - behold:
// toggle text wrap on the code area
codeArea.setWrapText( wrap );
if ( wrap ) // brute force refresh :(
{
final int c = codeArea.getCaretPosition();
final int p = codeArea.firstVisibleParToAllParIndex();
final StyledDocument doc = codeArea.subDocument( 0, codeArea.getLength() );
codeArea.clear();
codeArea.insert( 0, doc );
codeArea.showParagraphAtTop( p );
codeArea.moveTo( c );
}
I'm not sure about including / baking this into RichTextFX ? Anyway hope that it works okay for you .... ?
Sorry for the late reply.
I tested your solution. It works great! Thanks! However, it does not play well some parts of my application so I need to figure those out.
But seriously, the solution/hack is a life saver. Thanks again!