private void bindSearchFieldAndFilteredComboBox() {
// Set the items of the filtered ComboBox.
filteredComboBox.setItems( createFilteredList() );
// Keep it up to date, even if the original list changes.
final ComboBox< T > comboBox = getSkinnable();
final FilteredList< T > filteredList = createFilteredList();
comboBox.itemsProperty().addListener( ( observable, oldValue, newValue ) -> filteredComboBox
.setItems( filteredList ) );
// Update the filter, when the text in the search field changes.
searchField.textProperty().addListener( observable -> updateFilter() );
// The search field must only be visible, when the pop-up is showing.
searchField.visibleProperty().bind( filteredComboBox.showingProperty() );
filteredComboBox.showingProperty().addListener( ( observable, oldValue, newValue ) -> {
if ( newValue ) {
// When the filtered ComboBox pop-up is showing, we must also
// set the showing property of the original ComboBox. And here
// we must remember the previous value for the ESCAPE behavior.
// And we must transfer the focus to the search field, because
// otherwise the search field would not allow typing in the
// search text.
comboBox.show();
previousValue = comboBox.getValue();
searchField.requestFocus();
}
else {
// When the filtered ComboBox pop-up is hidden, we must also
// set the showing property of the original ComboBox to false,
// and clear the search field.
comboBox.hide();
searchField.setText( "" );
}
} );
// When the search field is focused, the pop-up must still be shown.
searchField.focusedProperty().addListener( ( observable, oldValue, newValue ) -> {
if ( newValue ) {
filteredComboBox.show();
}
else {
// If focus is lost from the search field, but nothing was
// selected (usually this is due to invalid search criteria
// leading to an empty drop-list), we must restore the previous
// selection in order to avoid a blank selection. Note that
// checking for an empty drop-list doesn't work, as it is
// state-dependent due to when, where, and why it gets cached.
if ( filteredComboBox.getSelectionModel().isEmpty() ) {
comboBox.setValue( previousValue );
}
filteredComboBox.hide();
}
} );
}