javajavafxautocompletecontrolsfx

JavaFX - Autocompletion TextField that suggests only matching item starting with input (SuggestionProvider not accessible)


I would like the Autocompletion to suggests only matching item that START with the user's input, instead of showing all the item that CONTAIN the user's input. One way I found is to use SuggestionProvider, but I can't get it imported on my Class.


I'm trying to use an autocompleted TextField with controlfx ( TextFields.bindAutoCompletion(...) ), which kinda works but doesn't act as I would like.

I have a TextField where you can start typing a number of an account and the autocompleted TF suggests a list of all account which contains the user's input in his number. For example, if I start typing "4", the suggested list shows "4710....", "4712...", ..., but also "624...", "74....". I guess the last accounts are shown because they CONTAINS my input.

It's not what I want so I found a way to make it acts like I want : by passing a list with only accounts that start with the user's input :

TextFields.bindAutoCompletion(myTextField,
UIUtils.comptesListToStrings(db.getComptesStartingBy(myTextField.getText())));

But I need to update it whenever the input text changes. So I found a way by using a SuggestionProvider like this (Update autoComplete JavaFx?):

Set<String> autoCompletions = new HashSet<>(Arrays.asList("A", "B", "C"));
SuggestionProvider<String> provider = SuggestionProvider.create(autoCompletions);
new AutoCompletionTextFieldBinding<>(textField, provider);

// and after some times, possible autoCompletions values has changed and now we have:
Set<String> filteredAutoCompletions = new HashSet<>(Arrays.asList("A", "B"));

provider.clearSuggestions();
provider.addPossibleSuggestions(filteredAutoCompletions);

But I can't make it work when I try to import SuggestionProvider. Eclipse suggests me "Import 'SuggestionProvider' (impl.org.controlfx.autocompletion)" but when I do, I get an error like :

import org.controlsfx.control.textfield.TextFields; // <-- this works perfectly
import impl.org.controlsfx.autocompletion.SuggestionProvider; // <-- "The type impl.org.controlsfx.autocompletion.SuggestionProvider is not accessible"

Import suggested by Eclipse (img)

After importing (img)

I believe this package is in the same jar as the first line (org.controlsfx.control.textfield.TextFields --> controlfx-11-1-2.jar): dependencies tree (img)

I'm using Maven for dependencies, on Eclipse.

I tried to found a solution but couldn't make it. So here I am, asking for help to solve this. If there is a way to make the default bindAutoCompletion only showing accounts that START WITH the users's input instead of showing all accounts that CONTAIN the user's input, it would also be great.

I'm a beginner in Java and this is my first post on stackoverflow, so please excuse my low-knowledge.


Solution

  • The impl.* packages are, by design, not part of the public API and not accessible.

    Instead, use the version of bindAutoCompletion that takes a Callback.

    The Callback is a function from a suggestion request to a collection of suggestions. It will be invoked when the text in the text field changes, and you can retrieve the text from the request using the getUserText() method. This way you can use arbitrary logic to map the text field text to a list of suggestions.

    Here is a quick and dirty example that ensures the suggested text starts with the text in the text field:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    import org.controlsfx.control.textfield.TextFields;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class HelloApplication extends Application {
        @Override
        public void start(Stage stage) throws IOException {
            TextField textField = new TextField();
            List<String> allItems = new ArrayList<>();
            for (int i = 1 ; i <= 100; i++) allItems.add(String.valueOf(i));
    
            TextFields.bindAutoCompletion(
                textField,
                suggestionRequest -> matchingItems(allItems, suggestionRequest.getUserText())
            );
            BorderPane root = new BorderPane(textField);
            Scene scene = new Scene(root, 800, 500);
            stage.setScene(scene);
            stage.show();
        }
    
        private List<String> matchingItems(List<String> allItems, String prefix) {
            List<String> matches = new ArrayList<>();
            for (String s : allItems) {
                if (s.startsWith(prefix)) {
                    matches.add(s);
                }
            }
            return matches ;
        }
    
        public static void main(String[] args) {
            launch();
        }
    }