Reputation: 537
Can I force the TextArea control to automatic expanding the height?
In the following case, I would like to see the scrollbar at ScrollPane control, not at TextArea control.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<ScrollPane fitToHeight="true" fitToWidth="true" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.91" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.Controller">
<VBox style="-fx-background-color: bisque">
<TextField/>
<TextArea VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets top="20.0"/>
</VBox.margin>
</TextArea>
</VBox>
</ScrollPane>
Upvotes: 0
Views: 4865
Reputation: 21
I'm not saying that I like the solution. It's hacky as hell but actually works a lot better then the one with textHolder
- is more stable. It's written in Kotlin with TornadoFX. For Java it should work the same, but probably with a lot more lines of code ;)
The principle is basically the same, but instead of dedicated textHolder
object we are using the actual Text
node inside the TextArea
.
class ExpandableTextArea : TextArea() {
init {
addClass("expandable")
isWrapText = true
children.onChange { a ->
val scrollPane = a.list.first() as ScrollPane
val contentView = scrollPane.content as Region
contentView.childrenUnmodifiable.onChange { b ->
b.next()
if (b.list.size == 2) {
val group = b.list[1] as Group
group.children.onChange { c ->
val text = c.list.first() as Text
text.layoutBoundsProperty().onChange {
if (it != null) {
val targetHeight = it.height + font.size
prefHeight = targetHeight
minHeight = targetHeight
}
}
}
}
}
}
}
}
Of course you still need the same CSS to turn the scrollbars off:
.text-area > .scroll-pane{
-fx-hbar-policy:never;
-fx-vbar-policy:never;
}
Please note that here I'm also setting the minHeight
because I want this node to expand "brutally" so it never has to scroll. You can remove that and leave setting the prefHeight
but if there is not enough space the node will stop growing and will be scrollable by mouse but without the scrollbars which can be confusing.
BE WARNED: if for some forsaken reason the TextArea children tree structure changes this will blow up.
Upvotes: 0
Reputation: 1776
For now the only solution that is close to your problem is given by @Uluk Biy, that i found here, what i did is just fit his logic, and hide the ScrollBars
. The only problem is the size of the TextArea
which is binded to that of the Text
and so it starts with a minimum height
at the beginning of the edition, here is the complete code :
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Launcher extends Application{
private Pane root = new Pane();
private Scene scene;
private ScrollPane scroller;
private Pane content = new Pane();
private TextField textF = new TextField();
private TextArea textA = new TextArea();
private Text textHolder = new Text();
private double oldHeight = 0;
@Override
public void start(Stage stage) throws Exception {
root.getChildren().addAll(yourSP());
scene = new Scene(root,300,316);
stage.setScene(scene);
stage.show();
}
private ScrollPane yourSP(){
content.setMinSize(300, 300);
textF.setPrefSize(260, 40);
textF.setLayoutX(20);
textF.setLayoutY(20);
textA.setPrefSize(260, 200);
textA.setLayoutX(20);
textA.setLayoutY(80);
textA.getStylesheets().add(getClass().getResource("texta.css").toExternalForm());
content.getChildren().addAll(textA,textF);
/*************************@Uluk Biy Code**************************/
textA.setWrapText(true);
textHolder.textProperty().bind(textA.textProperty());
textHolder.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
@Override
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
if (oldHeight != newValue.getHeight()) {
oldHeight = newValue.getHeight();
textA.setPrefHeight(textHolder.getLayoutBounds().getHeight() + 20);
System.out.println(textHolder.getLayoutBounds().getHeight());
}
}
});
/****************************************************************/
scroller = new ScrollPane(content);
scroller.setHbarPolicy(ScrollBarPolicy.NEVER);
scroller.setPrefSize(300, 316);
return scroller;
}
public static void main(String[] args) {
launch(args);
}
}
Of course the code can be adapted to fxml format, I just have not had enough time to do it, and here is the style of the TextArea
:
.text-area > .scroll-pane{
-fx-hbar-policy:never;
-fx-vbar-policy:never;
}
good luck for the continuation !
Upvotes: 2