I'm implementing the AutoComplete logic in Javafx. A TextField
is for user input and every time it got updated, the listener will be triggered. And then a dropdown menu which is a listView
will display the matched results.
So the problem is that how can I get the updated caret position in TextField
when the textProperty
listener is in process?
I'm currently using textfield.getCaretPosition()
, but it will only return the previous position.
Any inputs will be appreciated!
commandTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldText, String newText) {
// Following line will return previous caret position
commandTextField.getCaretPosition();
...//autocomplete logic omitted
}
});
EDIT: The reason that I need to get the caret position is that I need to know the position where the input is changed and show suggestions correspondingly.
textField.caretPositionProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
caretPosition = newValue.intValue();
});
textField.textProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
...//autocomplete Logic
}
In the above case, the textProperty
listener will be invoked first and followed by caretPositionProperty
, therefore there is no way to get updated caret position in my case.
It's not the intended use of the API, but you could use a TextFormatter
constructed with an UnaryOperator<TextFormatter.Change>
as parameter. This is usually used to prevent/modify undesired changes to the text, but in this case we'll just use the Change
object to get you the data you want.
The following example "suggests" possibilities each one replacing the selected section with some string:
@Override
public void start(Stage primaryStage) throws Exception {
ListView<String> listView = new ListView<>();
String[] replacements = { "foo", "bar", "42", "zap" };
listView.getItems().setAll(replacements);
TextField textField = new TextField();
TextFormatter<?> formatter = new TextFormatter<>((UnaryOperator<TextFormatter.Change>) change -> {
int a = change.getAnchor();
int b = change.getCaretPosition();
String newText = change.getControlNewText();
String prefix = newText.substring(0, Math.min(a, b));
String suffix = newText.substring(Math.max(a, b));
listView.getItems().clear();
for (String mid : replacements) {
listView.getItems().add(prefix + mid + suffix);
}
// do not change anything about the modification
return change;
});
textField.setTextFormatter(formatter);
Scene scene = new Scene(new VBox(listView, textField));
primaryStage.setScene(scene);
primaryStage.show();
}
There are plenty more properies available including the text and carret and anchor positions before the change, see the javadoc.