package net.thirdy.blackmarket.fxcontrols;
import java.util.Collection;
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;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.PopupControl;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Popup;
import javafx.stage.PopupWindow.AnchorLocation;
import javafx.util.Callback;
/**
* Modified version of: https://gist.github.com/floralvikings/10290131
*
* This class is a TextField which implements an "autocomplete" functionality,
* based on a supplied list of entries.
*
* @author Caleb Brinkman
* @author Vicente de Rivera III
*/
public class AutoCompleteTextField<T> extends TextField {
/** The existing autocomplete entries. */
private final SortedSet<T> entries;
/** The popup used to select an entry. */
private PopupControl entriesPopup;
/**
* Use for checking if current text value has actually changed, for
* efficiency.
*/
private String currentTextValue;
private ObjectProperty<T> selectedItemProperty;
public ObjectProperty<T> selectedItemProperty() {
return selectedItemProperty;
}
private ObservableList<T> listViewItems;
private ListView<T> listView;
/** Construct a new AutoCompleteTextField. */
public AutoCompleteTextField(Collection<T> items, double prefWidth) {
super();
entries = new TreeSet<>();
entriesPopup = new PopupControl();
entriesPopup.setPrefWidth(USE_PREF_SIZE);
entriesPopup.setAutoFix(true);
entriesPopup.setAutoHide(true);
setupListView(prefWidth);
((Pane) entriesPopup.getScene().getRoot()).getChildren().setAll(listView);
entriesPopup.setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT);
setItems(items);
textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observableValue, String oldVal, String newVal) {
System.out.println(" textProperty old: " + oldVal + " new: " + newVal);
updatePopup();
}
});
setOnMouseClicked(e -> {
showPopupAllItems();
});
focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue,
Boolean newValue) {
entriesPopup.hide();
currentTextValue = null;
}
});
}
private void setupListView(double prefWidth) {
listView = new ListView<>();
listView.setPrefWidth(prefWidth);
// Focusing on this node makes it blurry for some reason
// http://www.jensd.de/wordpress/?p=1245
listView.setStyle(
"-fx-background-color: -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: 0, 1, 2; -fx-background-radius: 5, 4, 3;");
setupListViewCellFactory();
listView.getSelectionModel().selectedItemProperty().addListener((observe, oldVal, newVal) -> {
// System.out.println("old: " + oldVal + " new: " + newVal);
T selectedItem = listView.getSelectionModel().getSelectedItem();
if (selectedItem != null) {
System.out.println("CLICK: " + selectedItem);
// AutoCompleteTextField.this.setText(selectedItem.toString());
// selectedItemProperty().setValue(selectedItem);
// entriesPopup.hide();
}
});
}
private void setupListViewCellFactory() {
listView.setCellFactory(new Callback<ListView<T>, ListCell<T>>(){
@Override
public ListCell<T> call(ListView<T> p) {
return new ListCell<T>() {
@Override
protected void updateItem(T t, boolean empty) {
super.updateItem(t, empty);
if (!empty) {
Button button = new Button(t.toString());
// button.setStyle("-fx-background-color: transparent;");
button.setOnAction(e -> System.out.println("CLICKED: " + t.toString()));
button.setMaxWidth(Double.MAX_VALUE);
// setGraphic(new TextFlow(button));
setGraphic(button);
} else {
setGraphic(null);
}
}
@Override
public void startEdit() {
super.startEdit();
System.out.println("START EDIT");
}
};
}
});
}
public void setItems(Collection<T> items) {
initializeSelectedItemProperty(items.iterator().next());
initializeListViewItems(items);
entries.clear();
entries.addAll(items);
}
private void initializeListViewItems(Collection<T> items) {
if (listViewItems == null) {
listViewItems = FXCollections.observableArrayList(items);
}
listView.setItems(listViewItems);
}
private void initializeSelectedItemProperty(T t) {
if (selectedItemProperty == null) {
selectedItemProperty = new SimpleObjectProperty<>();
}
selectedItemProperty.set(t);
}
private void updatePopup() {
System.out.println("updatePopup()");
T selectedItem = listView.getSelectionModel().getSelectedItem();
if (selectedItem != null && getText().equals(selectedItem.toString())) {
System.out.println("listview selected is equal to getText()");
// listView.getSelectionModel().clearSelection();
return;
}
if (StringUtils.equals(currentTextValue, getText())) {
return;
} else {
currentTextValue = getText();
}
LinkedList<T> searchResult = new LinkedList<>();
searchResult.addAll(entries.stream().filter(x -> StringUtils.containsIgnoreCase(x.toString(), getText()))
.collect(Collectors.toList()));
if (entries.size() > 0) {
listViewItems.setAll(searchResult);
if (!entriesPopup.isShowing()) {
Point2D p = this.localToScene(0.0, 0.0);
double x = p.getX() + this.getScene().getX() + this.getScene().getWindow().getX();
double y = p.getY() + this.getScene().getY() + this.getScene().getWindow().getY() + this.getHeight();
entriesPopup.show(AutoCompleteTextField.this, x, y);
}
} else {
entriesPopup.hide();
currentTextValue = null;
}
}
private void showPopupAllItems() {
System.out.println("showPopupAllItems()");
if (!entriesPopup.isShowing()) {
listViewItems.setAll(entries);
Point2D p = this.localToScene(0.0, 0.0);
double x = p.getX() + this.getScene().getX() + this.getScene().getWindow().getX();
double y = p.getY() + this.getScene().getY() + this.getScene().getWindow().getY() + this.getHeight();
entriesPopup.show(AutoCompleteTextField.this, x, y);
}
}
/**
* Get the existing set of autocomplete entries.
*
* @return The existing autocomplete entries.
*/
public SortedSet<T> getEntries() {
return entries;
}
}