I find that the TextFieldListCell stays in editing mode too long so I have been experimenting with my own custom ListCell.
I would like to cancel/commit the edit whenever the user leaves the field to focus a different control. Currently the TextFieldListCell only flips out of edit mode when the user selects a different cell or they press Enter or Esc. I thought that implementing my own TextFieldListCell would allow me to also stop editing when the field lost focus, but it appears to temporarily lose focus quite often.
Run the provided test case and watch the console for messages. Start editing one of the cells and notice that every time you click with the mouse in the field (possibly to select part of the text) the field temporarily loses focus to a VirtualFlow and then gets focus back.
I was hoping to be able to cancel/commit the edit on focus lost so that when the user clicks on my text field above the list (say to filter the list) the editor would go away. Am I missing a better way to accomplish this or are these spurious focus changes something I have to deal with?
**************************************************************************
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewTest extends Application {
@Override public void start(Stage primaryStage) throws Exception {
primaryStage.centerOnScreen();
primaryStage.setHeight(200);
primaryStage.setWidth(550);
final ListView<String> list = new ListView<>();
list.setEditable(true);
list.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override public ListCell<String> call(ListView<String> param) {
return new ListCell<String>() {
private TextField field = new TextField();
private ChangeListener<Boolean> focusListener;
@Override public void updateItem(String item, boolean empty) {
if ( (getItem() == null) ? item == null : getItem().equals(item) ) {
// working around bugRT-31200
return;
}
super.updateItem(item, empty);
setText(item);
}
@Override public void startEdit() {
super.startEdit();
field.setText(getItem());
setGraphic(field);
setText(null);
if ( focusListener == null ) {
focusListener = new ChangeListener<Boolean>() {
@Override public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
System.out.println("field focussed: " + newValue);
if ( !newValue ) {
System.out.println(" - lost focus to: " +
getScene().getFocusOwner());
}
}
};
}
field.focusedProperty().addListener( focusListener );
}
@Override public void cancelEdit() {
field.focusedProperty().removeListener(focusListener);
setGraphic(null);
setText(getItem());
super.cancelEdit();
}
};
}
});
list.getItems().setAll("one", "two", "three", "four", "five");
TextField field = new TextField();
field.setMinHeight(Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene(new VBox(field, list)));
primaryStage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
I would like to cancel/commit the edit whenever the user leaves the field to focus a different control. Currently the TextFieldListCell only flips out of edit mode when the user selects a different cell or they press Enter or Esc. I thought that implementing my own TextFieldListCell would allow me to also stop editing when the field lost focus, but it appears to temporarily lose focus quite often.
Run the provided test case and watch the console for messages. Start editing one of the cells and notice that every time you click with the mouse in the field (possibly to select part of the text) the field temporarily loses focus to a VirtualFlow and then gets focus back.
I was hoping to be able to cancel/commit the edit on focus lost so that when the user clicks on my text field above the list (say to filter the list) the editor would go away. Am I missing a better way to accomplish this or are these spurious focus changes something I have to deal with?
**************************************************************************
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewTest extends Application {
@Override public void start(Stage primaryStage) throws Exception {
primaryStage.centerOnScreen();
primaryStage.setHeight(200);
primaryStage.setWidth(550);
final ListView<String> list = new ListView<>();
list.setEditable(true);
list.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override public ListCell<String> call(ListView<String> param) {
return new ListCell<String>() {
private TextField field = new TextField();
private ChangeListener<Boolean> focusListener;
@Override public void updateItem(String item, boolean empty) {
if ( (getItem() == null) ? item == null : getItem().equals(item) ) {
// working around bug
return;
}
super.updateItem(item, empty);
setText(item);
}
@Override public void startEdit() {
super.startEdit();
field.setText(getItem());
setGraphic(field);
setText(null);
if ( focusListener == null ) {
focusListener = new ChangeListener<Boolean>() {
@Override public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
System.out.println("field focussed: " + newValue);
if ( !newValue ) {
System.out.println(" - lost focus to: " +
getScene().getFocusOwner());
}
}
};
}
field.focusedProperty().addListener( focusListener );
}
@Override public void cancelEdit() {
field.focusedProperty().removeListener(focusListener);
setGraphic(null);
setText(getItem());
super.cancelEdit();
}
};
}
});
list.getItems().setAll("one", "two", "three", "four", "five");
TextField field = new TextField();
field.setMinHeight(Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene(new VBox(field, list)));
primaryStage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}