Reputation: 159
I want to create a text field that expands horizontally as more text is entered.
After binding the prefColumnCount
property to the length of the text in the TextField, its size increases as text gets longer. However, the caret's position is not updated and it gets stuck at the same place, no matter the text length. The user has to manually move the caret to the front in order for it to be displayed properly.
Images:
What it looks like after inputting some text, the text doesn't occupy the whole text field:
After moving the caret at the beginning of the text:
Here is my current code:
TextField textField = new TextField();
textField.prefColumnCountProperty().bind(textField.textProperty().length());
setHgrow(textField, Priority.ALWAYS);
How do I fix this problem? Any help is appreciated!
Upvotes: 7
Views: 941
Reputation: 1990
The problem comes from the caret, which graphically updates its position as if he is in a static area; once you define TextField's prefWidth
property, the caret cannot disappear from the text field.
When I write down content until it reaches the right side, the caret should go outside the field, which is impossible; it seems to be in front of a virtual wall. As a result, all the text content is "translated" to the left to let the caret on the same position. What's puzzling is that your binding works well, so TextField
's width continues to grow as you add text...
In your case, I suggest you to refresh the caret position each time you type something into your text field. Add this EventHandler
to your TextField
instantiation:
textField.addEventHandler(KeyEvent.KEY_PRESSED, ke -> {
if (ke.getCode() != KeyCode.DELETE) {
textField.positionCaret(0);
textField.positionCaret(textField.getText().length());
}
});
ADDITIONAL NOTE
Your binding only works for monospaced fonts like Consolas or Courier New; prefWidth
won't be calculated properly if you use fonts like Arial or Calibri. You would have to import com.sun.javafx.tk
and use FontMetrics
and ToolKit
classes.
Instead of your binding, you would have something like:
FontMetrics fm = Toolkit.getToolkit().getFontLoader().getFontMetrics(textField.getFont());
textField.textProperty().addListener((bean_p, old_p, new_p) -> {
Insets in = textField.getInsets();
textField.setPrefWidth(fm.computeStringWidth(new_p) + in.getLeft() + in.getRight());
});
Hope it will be useful for you :)
Upvotes: 2
Reputation: 159291
This example uses a separate measuring text variable in its own css styled scene to calculate updates to the preferred size of the TextField so that it's preferred size expands and shrinks as required.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MagicExpander extends Application {
@Override
public void start(Stage stage) throws Exception {
TextField textField = new ExpandingTextField();
Pane layout = new VBox(textField);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout, 600, 40));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
static class ExpandingTextField extends TextField {
private static int MAGIC_PADDING = 20;
private Text measuringText = new Text();
// To allow potential css formatting to work for the text,
// it is required that text be placed in a Scene,
// even though we never explicitly use the scene.
private Scene measuringScene = new Scene(new Group(measuringText));
ExpandingTextField() {
super();
setPrefWidth(MAGIC_PADDING);
setMinWidth(MAGIC_PADDING);
setMaxWidth(USE_PREF_SIZE);
// note if the text in your text field is styled, then you should also apply the
// a similar style to the measuring text here if you want to get an accurate measurement.
textProperty().addListener(observable -> {
measuringText.setText(getText());
setPrefWidth(measureTextWidth(measuringText) + MAGIC_PADDING);
});
}
private double measureTextWidth(Text text) {
text.applyCss();
return text.getLayoutBounds().getWidth();
}
}
}
Upvotes: 2