Reputation: 65
I'm trying to limit the number of characters which can be inserted in a JavaFX-8 TextField: when the limit is reached endLineReached() method is called. I would like to set the maximum number of chars basing on size (the visible part on GUI) of the textfield. So the code I'm using is
textfield.textProperty().addListener((ov, oldValue, newValue) -> {
onEndLine(textfield.getText(), textfield.getPrefColumnCount());
});
public void onEndLine(String text, int prefColumnCount) {
if (text.length() > prefColumnCount) {
endLineReached();
}
}
The problem is that textfield.getPrefColumnCount() not work properly. Furthermore I'm not able to set prefColumnCount value programmatically because the textfield width could change every time the program is re-started (not during execution). The textfield is child of a JavaFX HBox.
Upvotes: 2
Views: 3402
Reputation: 209245
If you want to limit the length of the text based solely on the visual width of the text field, that's a little tricky as it depends on many factors (the font, the width of the text field, the padding applied to the text field, in the case of a variable-width font the actual text that has been entered, etc).
Note this seems a strange thing to do (imo), because the user will be able to enter relatively long text if it happens to use narrow characters, but relatively short text if it happens to use wide characters (in this example below I can enter 26 "l"s but only 8 "m"s). Really the text the user is allowed to enter should be based on logical considerations (i.e. business rules), not visual ones. But maybe you have some unusual use-case for this.
You can use a text formatter on the text field to veto the addition of text. To check the width, create a new Text
object with the new text, set the font to the same font as the text field, and then check its width. You need to account for padding in the text field as well. Note this is a little fragile, as you don't really know the implementation of the layout of the text field, but this appears to work ok, at least with the JDK version I have.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TextFieldNoScroll extends Application {
@Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
textField.setTextFormatter(new TextFormatter<String>(change -> {
if (change.isAdded()) {
Insets textFieldInsets = change.getControl().getPadding();
double horizPadding = textFieldInsets.getLeft() + textFieldInsets.getRight() ;
Text newText = new Text(change.getControlNewText());
newText.setFont(((TextInputControl)change.getControl()).getFont());
double newTextWidth = newText.getBoundsInLocal().getWidth();
if (newTextWidth + horizPadding > change.getControl().getWidth()) {
return null ;
}
}
return change ;
}));
VBox root = new VBox(textField);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 120, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 2