Reputation: 1594
I have been trying to make the TextArea grow with its content, for exmaple:
There is one Line in the Text area, now the user keeps writing and reaches the TextArea's right border and the text wraps, now he does have a second line (which is not wrapped by simply using '\n' internally) and I now want the text field to grow for the height of one more line.
What I have tried already:
This
Text text = textArea.lookup(".text").getLocalBounds.getHeight()
always returns the same, no matter how much Lines i do have.
This
textArea.getPrefRowCount()
always returns
1
no matter how much lines I have.
How can I achieve this? If someone wants a working example, Skype has that kind of mechanism in its chat.
Upvotes: 0
Views: 1454
Reputation: 1
From here Binding height of internal Text node to the height of TextArea
I made a working solution. TextArea is being expanded according to the number of text rows. Bug with scroll bar that was mentioned also is fixed by simply adding a small padding which prevents internal ScrollBar in TextArea from spawning.
First of all:
textArea.setWrapText(true);
Then we need to call this scary looking method with listeners:
private void setupTextAreaDimensionsChangeEventListener(TextArea textArea) {
textArea.textProperty().addListener((observable, oldValue, newValue) -> {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.schedule(() -> {
updateTextAreaHeight(textArea);
}, 10, TimeUnit.MILLISECONDS);
});
textArea.widthProperty().addListener((observableValue, oldWidth, newWidth) -> {
updateTextAreaHeight(textArea);
});
}
First listener handles vertical growth caused by typing and reaching end of the row. Second listener is responsible for width TextArea's width changes for example due to resizing a window. ScheduledExecutorService is for making sure that internal Text node used in next method is already reachable (Platform run later wasn't enough).
private void updateTextAreaHeight(TextArea textArea) {
Platform.runLater(() -> {
try {
Text text = (Text) textArea.lookup(".text");
if (text != null) {
double newHeight = calculateRequiredHeight(text);
updateTextAreaDimensions(textArea, newHeight);
} else {
throw new Exception("Failed to locate Text Node inside of TextArea");
}
} catch (Exception e) {
System.err.println("Failed to adjust height of TextArea: " + e);
}
});
}
private double calculateRequiredHeight(Text text) {
double textHeight = text.boundsInParentProperty().get().getMaxY();
return textHeight + PADDING;
}
private void updateTextAreaDimensions(TextArea textArea, double height) {
textArea.setMinHeight(height);
textArea.setPrefHeight(height);
textArea.setMaxHeight(height);
}
All together makes a responsive TextArea.
Upvotes: 0
Reputation: 1594
This
Text text = (Text) textArea.lookup(".text");
textArea.setPrefHeight(text.boundsInParentProperty().get().getMaxY());
works but is not very nice since the scroll bars keep bugging.
Upvotes: 1
Reputation: 275
Should not be too hard to do it. I think you will probably have to @Override
the setPreferredRowCount()
method of textArea
to return the number of rows based on what you need, or on a fixed value.
This is a common "issue" u can encounter on any Swing
component such as JFrame, JPanel, JLabel, JButton
, etc.
Also on these components too it is highly recommended to override the preferred methods in order to correctly resize them, instead of using setBounds
.
Also maybe the setPreferredRowCount()
method won't update herself so you would probably have to call it in a loop (or a Thread but I'd rather not) to make sure your sizes are always updated. Something like:
EDIT:
while(inputNeeded) {
if(textArea1.textChanged) {
if(text1.length > 10) {
textArea1.setPreferredRowCount(3);
} else if(text1.length > 20) {
textArea1.setPreferredRowCount(4);
}
}
}
... and so on. My bad, you don't really have to override anything. Just try to use a loop like this.
Upvotes: -1