package com.wilutions.fx.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.StringTokenizer;
import com.wilutions.itol.db.Suggest;
import javafx.application.Platform;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
public class TextFieldWithSuggestions<T> extends TextField {
private PopupForSuggestions<T> popup;
private boolean suggestionsLocked = false;
public final static String DELIMS = " \t\n\r\f";
public TextFieldWithSuggestions(Suggest<T> suggest) {
popup = new PopupForSuggestions<T>(this,
new SuggestIfNotExist<T>(suggest),
(item) -> onSuggestionSelected(item));
this.textProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
updateSuggestions();
});
});
this.setOnKeyPressed((keyEvent) -> {
KeyCode code = keyEvent.getCode();
switch (code) {
case DOWN:
case UP:
if (popup.isShowing()) {
popup.fireEvent(keyEvent);
}
else {
// show popup
popup.updateSuggestions("");
}
keyEvent.consume();
break;
case TAB:
if (popup.isShowing()) {
popup.fireEvent(keyEvent);
keyEvent.consume();
}
default:
}
});
}
private void updateSuggestions() {
String newValue = getText();
String filter = "";
if (!suggestionsLocked && !newValue.isEmpty()) {
int startIndex = getStartTokenIndex(newValue);
int endIndex = getEndTokenIndex(newValue, startIndex);
filter = newValue.substring(startIndex, endIndex);
}
popup.updateSuggestions(filter);
}
protected int getStartTokenIndex(String text) {
int index = 0;
int caretPos = this.getCaretPosition()-1;
for (int i = caretPos; i >= 0; i--) {
if (i < text.length() && DELIMS.indexOf(text.charAt(i)) >= 0) {
index = i+1;
break;
}
}
return index;
}
protected int getEndTokenIndex(String text, int startIndex) {
int index = text.length();
for (int i = startIndex; i < text.length(); i++) {
if (DELIMS.indexOf(text.charAt(i)) >= 0) {
index = i;
break;
}
}
return index;
}
protected void onSuggestionSelected(T item) {
suggestionsLocked = true;
try {
String text = getText();
int startIndex = getStartTokenIndex(text);
int endIndex = getEndTokenIndex(text, startIndex);
String addText = item.toString();
if (endIndex >= text.length() || DELIMS.indexOf(text.charAt(endIndex)) < 0) {
addText += " ";
}
replaceText(startIndex, endIndex, addText);
}
finally {
suggestionsLocked = false;
}
}
protected class SuggestIfNotExist<S> implements Suggest<S> {
Suggest<S> innerSuggest;
SuggestIfNotExist(Suggest<S> innerSuggest) {
this.innerSuggest = innerSuggest;
}
@Override
public Collection<S> find(String text, int max, Collection<S> ignored) {
HashSet<String> terms = new HashSet<>();
StringTokenizer stok = new StringTokenizer(getText(), DELIMS);
while (stok.hasMoreTokens()) terms.add(stok.nextToken());
Collection<S> innerItems = innerSuggest.find(text, max + terms.size(), null);
ArrayList<S> outerItems = new ArrayList<>(max);
for (S item : innerItems) {
if (terms.contains(item.toString())) continue;
outerItems.add(item);
if (outerItems.size() >= max) break;
}
return outerItems;
}
}
}