package net.thirdy.blackmarket.samples; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Side; import javafx.scene.Scene; import javafx.scene.control.ComboBox; import javafx.scene.control.ContextMenu; import javafx.scene.control.CustomMenuItem; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import java.awt.Color; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; /** * This class is a TextField which implements an "autocomplete" functionality, based on a supplied list of entries. * @author Caleb Brinkman */ public class AutoCompleteComboBox extends ComboBox<String> { /** The existing autocomplete entries. */ private final SortedSet<String> entries; /** The popup used to select an entry. */ private ContextMenu entriesPopup; /** Construct a new AutoCompleteTextField. */ public AutoCompleteComboBox() { super(); setEditable(true); entries = new TreeSet<>(); entries.addAll(Arrays.asList("Test", "Pad", "Shiver", "Loooooooooooooooooooooooooooooooooooog")); entriesPopup = new ContextMenu(); getItems().addAll(entries); getEditor().textProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> observableValue, String s, String s2) { if (getEditor().getText().length() == 0) { entriesPopup.hide(); } else { LinkedList<String> searchResult = new LinkedList<>(); // searchResult.addAll(entries.subSet(getText(), getText() + Character.MAX_VALUE)); searchResult.addAll(entries.stream() .filter( x -> StringUtils.containsIgnoreCase(x, getEditor().getText()) ) .collect(Collectors.toList()) ); if (entries.size() > 0) { populatePopup(searchResult); if (!entriesPopup.isShowing()) { entriesPopup.show(AutoCompleteComboBox.this, Side.BOTTOM, 0, 0); } } else { entriesPopup.hide(); } } } }); focusedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean aBoolean2) { entriesPopup.hide(); } }); } /** * Get the existing set of autocomplete entries. * @return The existing autocomplete entries. */ public SortedSet<String> getEntries() { return entries; } /** * Populate the entry set with the given search results. Display is limited to 10 entries, for performance. * @param searchResult The set of matching strings. */ private void populatePopup(List<String> searchResult) { List<CustomMenuItem> menuItems = new LinkedList<>(); // If you'd like more entries, modify this line. int maxEntries = 10; int count = Math.min(searchResult.size(), maxEntries); for (int i = 0; i < count; i++) { final String result = searchResult.get(i); Label entryLabel = new Label(result); CustomMenuItem item = new CustomMenuItem(entryLabel, true); item.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent actionEvent) { getEditor().setText(result); entriesPopup.hide(); } }); menuItems.add(item); } entriesPopup.getItems().clear(); entriesPopup.getItems().addAll(menuItems); } }