使用下拉列表在Java中创建自动完成文本框
我想创建一个自动建议文本框,在每次密钥释放事件时查询数据库。 这部分很简单,但我想给它一个很好的视觉效果。类似于我们在Facebook搜索等网站上看到的自动建议文本框 如何制作这样的界面 一个天真的想法是将一个JList放在文本框的正下方,并在找到一个JList时将其设置为可见,并在其中显示结果使用下拉列表在Java中创建自动完成文本框,java,swing,autocomplete,autosuggest,Java,Swing,Autocomplete,Autosuggest,我想创建一个自动建议文本框,在每次密钥释放事件时查询数据库。 这部分很简单,但我想给它一个很好的视觉效果。类似于我们在Facebook搜索等网站上看到的自动建议文本框 如何制作这样的界面 一个天真的想法是将一个JList放在文本框的正下方,并在找到一个JList时将其设置为可见,并在其中显示结果 有没有更好的方法或标准方法呢?一个非常简单的方法是使用GlazedList实现自动完成。站起来跑步很容易。你可以找到它 只需一行代码,即可在JComboBox上安装auto complete,如下所示:
有没有更好的方法或标准方法呢?一个非常简单的方法是使用
GlazedList
实现自动完成。站起来跑步很容易。你可以找到它
只需一行代码,即可在JComboBox上安装auto complete,如下所示:
JComboBox comboBox = new JComboBox();
Object[] elements = new Object[] {"Cat", "Dog", "Lion", "Mouse"};
AutoCompleteSupport.install(comboBox, GlazedLists.eventListOf(elements));
而且
SwingX
支持自动完成,可能比GlazedList
更易于使用。使用SwingX
编写的所有代码都是AutoCompleteDecorator.decoration(组合框)代码>@syb0rg的答案更简单,因为它使用了第三方库
但是,我使用了另一种方法:
它使用一个名为autosuggester
的自定义类,该类接受JTextField
、它的窗口
和一个数组列表
,以检查键入的单词、背景颜色和文本颜色、建议焦点颜色以及不透明度值。通过传递JTextField
reference,添加了一个DocumentListener
,它将检查键入的单词,是否显示建议,如果是,显示什么建议。当键入一个单词时,DocumentListener
将在wordTyped(..)中使用当前键入的单词或(至少键入了多少单词)激发wordTyped(String wordTyped)
方法
将对照AutoSuggester
s classes字典中的单词进行检查,该字典是字符串的基本数组列表
,可以动态设置,如下例所示:
(目前,您必须使用鼠标单击要自动完成的单词,或者使用向下键遍历时使用向下键和文本字段并输入以选择建议。我尚未实现):
导入java.awt.Color;
导入java.awt.Dimension;
导入java.awt.GridLayout;
导入java.awt.Window;
导入java.awt.event.ActionEvent;
导入java.awt.event.KeyEvent;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入java.util.ArrayList;
导入javax.swing.AbstractAction;
导入javax.swing.JComponent;
导入javax.swing.JFrame;
导入javax.swing.JLabel;
导入javax.swing.JPanel;
导入javax.swing.JTextField;
导入javax.swing.JWindow;
导入javax.swing.KeyStroke;
导入javax.swing.SwingUtilities;
导入javax.swing.border.LineBorder;
导入javax.swing.event.DocumentEvent;
导入javax.swing.event.DocumentListener;
/**
*@作者大卫
*/
公开课考试{
公开考试(){
JFrame=新JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextField=新的JTextField(10);
AutoSuggester AutoSuggester=新的AutoSuggester(f,帧,null,Color.WHITE.brighter(),Color.BLUE,Color.RED,0.75f){
@凌驾
布尔字类型(字符串类型DWORD){
//为字典创建列表在您的情况下,可以通过调用查询db并以arraylist形式返回结果的方法来完成此操作
ArrayList words=新的ArrayList();
添加(“你好”);
文字。添加(“遗产”);
加上(“幸福”);
添加(“再见”);
词语。添加(“残忍”);
词语。添加(“car”);
词语。添加(“战争”);
文字。添加(“遗嘱”);
字。加上(“世界”);
文字。添加(“墙”);
词典(单词);
//addToDictionary(“bye”);//添加单个单词
return super.wordTyped(typedWord);//现在调用super检查是否与最新字典匹配
}
};
JPanel p=新的JPanel();
p、 添加(f);
框架。添加(p);
frame.pack();
frame.setVisible(true);
}
公共静态void main(字符串[]args){
SwingUtilities.invokeLater(新的Runnable(){
@凌驾
公开募捐{
新测试();
}
});
}
}
类自动建议器{
私有最终JTextField textField;
专用最终窗口容器;
私人JPanel建议小组;
私有JWindow自动建议弹出窗口;
私有字符串类型字;
私有最终ArrayList字典=新ArrayList();
私人int currentIndexOfSpace,tW,tH;
私有DocumentListener DocumentListener=新DocumentListener(){
@凌驾
公共作废插入更新(DocumentEvent de){
检查并显示建议();
}
@凌驾
公共作废移除更新(DocumentEvent de){
检查并显示建议();
}
@凌驾
公共作废更改日期(DocumentEvent de){
检查并显示建议();
}
};
私人最终颜色建议文本颜色;
私人最终颜色建议聚焦颜色;
公共自动建议器(JTextField textField,Window Main Window,ArrayList单词,彩色popUpBackground,彩色textColor,彩色建议FocusedColor,浮点不透明度){
this.textField=textField;
this.suggestionTextColor=textColor;
this.container=main窗口;
this.suggestionFocusedColor=suggestionFocusedColor;
this.textField.getDocument().addDocumentListener(documentListener);
词典(单词);
typedWord=“”;
currentIndexOfSpace=0;
tW=0;
tH=0;
自动提示弹出窗口
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* @author David
*/
public class Test {
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextField f = new JTextField(10);
AutoSuggestor autoSuggestor = new AutoSuggestor(f, frame, null, Color.WHITE.brighter(), Color.BLUE, Color.RED, 0.75f) {
@Override
boolean wordTyped(String typedWord) {
//create list for dictionary this in your case might be done via calling a method which queries db and returns results as arraylist
ArrayList<String> words = new ArrayList<>();
words.add("hello");
words.add("heritage");
words.add("happiness");
words.add("goodbye");
words.add("cruel");
words.add("car");
words.add("war");
words.add("will");
words.add("world");
words.add("wall");
setDictionary(words);
//addToDictionary("bye");//adds a single word
return super.wordTyped(typedWord);//now call super to check for any matches against newest dictionary
}
};
JPanel p = new JPanel();
p.add(f);
frame.add(p);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Test();
}
});
}
}
class AutoSuggestor {
private final JTextField textField;
private final Window container;
private JPanel suggestionsPanel;
private JWindow autoSuggestionPopUpWindow;
private String typedWord;
private final ArrayList<String> dictionary = new ArrayList<>();
private int currentIndexOfSpace, tW, tH;
private DocumentListener documentListener = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent de) {
checkForAndShowSuggestions();
}
@Override
public void removeUpdate(DocumentEvent de) {
checkForAndShowSuggestions();
}
@Override
public void changedUpdate(DocumentEvent de) {
checkForAndShowSuggestions();
}
};
private final Color suggestionsTextColor;
private final Color suggestionFocusedColor;
public AutoSuggestor(JTextField textField, Window mainWindow, ArrayList<String> words, Color popUpBackground, Color textColor, Color suggestionFocusedColor, float opacity) {
this.textField = textField;
this.suggestionsTextColor = textColor;
this.container = mainWindow;
this.suggestionFocusedColor = suggestionFocusedColor;
this.textField.getDocument().addDocumentListener(documentListener);
setDictionary(words);
typedWord = "";
currentIndexOfSpace = 0;
tW = 0;
tH = 0;
autoSuggestionPopUpWindow = new JWindow(mainWindow);
autoSuggestionPopUpWindow.setOpacity(opacity);
suggestionsPanel = new JPanel();
suggestionsPanel.setLayout(new GridLayout(0, 1));
suggestionsPanel.setBackground(popUpBackground);
addKeyBindingToRequestFocusInPopUpWindow();
}
private void addKeyBindingToRequestFocusInPopUpWindow() {
textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released");
textField.getActionMap().put("Down released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ae) {//focuses the first label on popwindow
for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) {
if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) {
((SuggestionLabel) suggestionsPanel.getComponent(i)).setFocused(true);
autoSuggestionPopUpWindow.toFront();
autoSuggestionPopUpWindow.requestFocusInWindow();
suggestionsPanel.requestFocusInWindow();
suggestionsPanel.getComponent(i).requestFocusInWindow();
break;
}
}
}
});
suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released");
suggestionsPanel.getActionMap().put("Down released", new AbstractAction() {
int lastFocusableIndex = 0;
@Override
public void actionPerformed(ActionEvent ae) {//allows scrolling of labels in pop window (I know very hacky for now :))
ArrayList<SuggestionLabel> sls = getAddedSuggestionLabels();
int max = sls.size();
if (max > 1) {//more than 1 suggestion
for (int i = 0; i < max; i++) {
SuggestionLabel sl = sls.get(i);
if (sl.isFocused()) {
if (lastFocusableIndex == max - 1) {
lastFocusableIndex = 0;
sl.setFocused(false);
autoSuggestionPopUpWindow.setVisible(false);
setFocusToTextField();
checkForAndShowSuggestions();//fire method as if document listener change occured and fired it
} else {
sl.setFocused(false);
lastFocusableIndex = i;
}
} else if (lastFocusableIndex <= i) {
if (i < max) {
sl.setFocused(true);
autoSuggestionPopUpWindow.toFront();
autoSuggestionPopUpWindow.requestFocusInWindow();
suggestionsPanel.requestFocusInWindow();
suggestionsPanel.getComponent(i).requestFocusInWindow();
lastFocusableIndex = i;
break;
}
}
}
} else {//only a single suggestion was given
autoSuggestionPopUpWindow.setVisible(false);
setFocusToTextField();
checkForAndShowSuggestions();//fire method as if document listener change occured and fired it
}
}
});
}
private void setFocusToTextField() {
container.toFront();
container.requestFocusInWindow();
textField.requestFocusInWindow();
}
public ArrayList<SuggestionLabel> getAddedSuggestionLabels() {
ArrayList<SuggestionLabel> sls = new ArrayList<>();
for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) {
if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) {
SuggestionLabel sl = (SuggestionLabel) suggestionsPanel.getComponent(i);
sls.add(sl);
}
}
return sls;
}
private void checkForAndShowSuggestions() {
typedWord = getCurrentlyTypedWord();
suggestionsPanel.removeAll();//remove previos words/jlabels that were added
//used to calcualte size of JWindow as new Jlabels are added
tW = 0;
tH = 0;
boolean added = wordTyped(typedWord);
if (!added) {
if (autoSuggestionPopUpWindow.isVisible()) {
autoSuggestionPopUpWindow.setVisible(false);
}
} else {
showPopUpWindow();
setFocusToTextField();
}
}
protected void addWordToSuggestions(String word) {
SuggestionLabel suggestionLabel = new SuggestionLabel(word, suggestionFocusedColor, suggestionsTextColor, this);
calculatePopUpWindowSize(suggestionLabel);
suggestionsPanel.add(suggestionLabel);
}
public String getCurrentlyTypedWord() {//get newest word after last white spaceif any or the first word if no white spaces
String text = textField.getText();
String wordBeingTyped = "";
if (text.contains(" ")) {
int tmp = text.lastIndexOf(" ");
if (tmp >= currentIndexOfSpace) {
currentIndexOfSpace = tmp;
wordBeingTyped = text.substring(text.lastIndexOf(" "));
}
} else {
wordBeingTyped = text;
}
return wordBeingTyped.trim();
}
private void calculatePopUpWindowSize(JLabel label) {
//so we can size the JWindow correctly
if (tW < label.getPreferredSize().width) {
tW = label.getPreferredSize().width;
}
tH += label.getPreferredSize().height;
}
private void showPopUpWindow() {
autoSuggestionPopUpWindow.getContentPane().add(suggestionsPanel);
autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textField.getWidth(), 30));
autoSuggestionPopUpWindow.setSize(tW, tH);
autoSuggestionPopUpWindow.setVisible(true);
int windowX = 0;
int windowY = 0;
windowX = container.getX() + textField.getX() + 5;
if (suggestionsPanel.getHeight() > autoSuggestionPopUpWindow.getMinimumSize().height) {
windowY = container.getY() + textField.getY() + textField.getHeight() + autoSuggestionPopUpWindow.getMinimumSize().height;
} else {
windowY = container.getY() + textField.getY() + textField.getHeight() + autoSuggestionPopUpWindow.getHeight();
}
autoSuggestionPopUpWindow.setLocation(windowX, windowY);
autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textField.getWidth(), 30));
autoSuggestionPopUpWindow.revalidate();
autoSuggestionPopUpWindow.repaint();
}
public void setDictionary(ArrayList<String> words) {
dictionary.clear();
if (words == null) {
return;//so we can call constructor with null value for dictionary without exception thrown
}
for (String word : words) {
dictionary.add(word);
}
}
public JWindow getAutoSuggestionPopUpWindow() {
return autoSuggestionPopUpWindow;
}
public Window getContainer() {
return container;
}
public JTextField getTextField() {
return textField;
}
public void addToDictionary(String word) {
dictionary.add(word);
}
boolean wordTyped(String typedWord) {
if (typedWord.isEmpty()) {
return false;
}
//System.out.println("Typed word: " + typedWord);
boolean suggestionAdded = false;
for (String word : dictionary) {//get words in the dictionary which we added
boolean fullymatches = true;
for (int i = 0; i < typedWord.length(); i++) {//each string in the word
if (!typedWord.toLowerCase().startsWith(String.valueOf(word.toLowerCase().charAt(i)), i)) {//check for match
fullymatches = false;
break;
}
}
if (fullymatches) {
addWordToSuggestions(word);
suggestionAdded = true;
}
}
return suggestionAdded;
}
}
class SuggestionLabel extends JLabel {
private boolean focused = false;
private final JWindow autoSuggestionsPopUpWindow;
private final JTextField textField;
private final AutoSuggestor autoSuggestor;
private Color suggestionsTextColor, suggestionBorderColor;
public SuggestionLabel(String string, final Color borderColor, Color suggestionsTextColor, AutoSuggestor autoSuggestor) {
super(string);
this.suggestionsTextColor = suggestionsTextColor;
this.autoSuggestor = autoSuggestor;
this.textField = autoSuggestor.getTextField();
this.suggestionBorderColor = borderColor;
this.autoSuggestionsPopUpWindow = autoSuggestor.getAutoSuggestionPopUpWindow();
initComponent();
}
private void initComponent() {
setFocusable(true);
setForeground(suggestionsTextColor);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
replaceWithSuggestedText();
autoSuggestionsPopUpWindow.setVisible(false);
}
});
getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), "Enter released");
getActionMap().put("Enter released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ae) {
replaceWithSuggestedText();
autoSuggestionsPopUpWindow.setVisible(false);
}
});
}
public void setFocused(boolean focused) {
if (focused) {
setBorder(new LineBorder(suggestionBorderColor));
} else {
setBorder(null);
}
repaint();
this.focused = focused;
}
public boolean isFocused() {
return focused;
}
private void replaceWithSuggestedText() {
String suggestedWord = getText();
String text = textField.getText();
String typedWord = autoSuggestor.getCurrentlyTypedWord();
String t = text.substring(0, text.lastIndexOf(typedWord));
String tmp = t + text.substring(text.lastIndexOf(typedWord)).replace(typedWord, suggestedWord);
textField.setText(tmp + " ");
}
}
package autocomplete;
import com.mxrck.autocompleter.TextAutoCompleter;
import java.sql.SQLException;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class AutoComplete {
JFrame f=new JFrame();
JTextField t1;
AutoComplete() throws ClassNotFoundException, SQLException{
f.setSize(500,500);
f.setLocation(500,100);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(null);
f.setVisible(true);
t1=new JTextField();
t1.setBounds(50,80,200,20);
f.add(t1);
TextAutoCompleter complete=new TextAutoCompleter(t1);
DBConn conn=new DBConn();
conn.connection();
conn.retrieve();
while(conn.rs.next()){
complete.addItem(conn.rs.getString("number"));
}
}
public static void main(String[] args) throws ClassNotFoundException,
SQLException{
new AutoComplete();
}
}
//Create seperate class for database connection and write the following code
package autocomplete;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBConn {
Connection con; ResultSet rs;PreparedStatement stat;
public void connection() throws ClassNotFoundException, SQLException{
String url="jdbc:mysql://localhost:3306/";
String driver="com.mysql.jdbc.Driver";
String db="demo";
String username="root";
String password="root";
stat =null;
Class.forName(driver);
con=(Connection)DriverManager.getConnection
(url+db,username,password);
System.out.println("Connecttion SuccessFul");
}
public void retrieve() throws SQLException{
Statement stmt=con.createStatement();
String query="select number from phone";
rs = stmt.executeQuery(query);
System.out.println("retrieve succesfully");
}
//here I have to do my code for up key
//---------------------------------------------------------------------
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released");
textField.getActionMap().put("Up released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ae) {//focuses the first label on popwindow
for (int i = suggestionsPanel.getComponentCount()-1; i >=0; i--) {
if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) {
((SuggestionLabel) suggestionsPanel.getComponent(i)).setFocused(true);
autoSuggestionPopUpWindow.toFront();
autoSuggestionPopUpWindow.requestFocusInWindow();
suggestionsPanel.requestFocusInWindow();
suggestionsPanel.getComponent(i).requestFocusInWindow();
break;
}
}
}
});
suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released");
suggestionsPanel.getActionMap().put("Up released", new AbstractAction() {
//######int lastFocusableIndex = 0;
int lastFocusableIndex = 0;
//lastFocusableIndex=lastFocusableIndex___;
@Override
public void actionPerformed(ActionEvent ae) {//allows scrolling of labels in pop window (I know very hacky for now :))
ArrayList<SuggestionLabel> sls = getAddedSuggestionLabels();
int max = sls.size();
lastFocusableIndex=lastFocusableIndex___;
System.out.println("UP UP UP UP");//***//
System.out.println("max = "+String.valueOf(max));//***//
System.out.println("lastFocusableIndex = "+String.valueOf(lastFocusableIndex));//***//
System.out.println("UP UP UP UP");//***//
if (max > 1) {//more than 1 suggestion
for (int i = max-1; i >=0; i--) {
SuggestionLabel sl = sls.get(i);
if (sl.isFocused()) {
if (lastFocusableIndex == 0) {
lastFocusableIndex = max - 1;
lastFocusableIndex___=lastFocusableIndex;
sl.setFocused(false);
autoSuggestionPopUpWindow.setVisible(false);
setFocusToTextField();
checkForAndShowSuggestions();//fire method as if document listener change occured and fired it
} else {
sl.setFocused(false);
lastFocusableIndex = i;
lastFocusableIndex___=lastFocusableIndex;
}
} else if (lastFocusableIndex > i) {
if (i < max ) {
sl.setFocused(true);
autoSuggestionPopUpWindow.toFront();
autoSuggestionPopUpWindow.requestFocusInWindow();
suggestionsPanel.requestFocusInWindow();
suggestionsPanel.getComponent(i).requestFocusInWindow();
lastFocusableIndex = i;
lastFocusableIndex___=lastFocusableIndex;
break;
}
}
}
} else {//only a single suggestion was given
autoSuggestionPopUpWindow.setVisible(false);
setFocusToTextField();
checkForAndShowSuggestions();//fire method as if document listener change occured and fired it
}
}
});
import javax.swing.border.LineBorder
import java.util.ArrayList
import javax.swing.event.DocumentListener
import java.awt.*
import java.awt.event.*
import javax.swing.*
import javax.swing.event.DocumentEvent
/**
* Author of the original version: David @ https://stackoverflow.com/users/1133011/david-kroukamp
*/
class Test {
init {
val frame = JFrame()
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
val f = JTextField(10)
val autoSuggestor = object : AutoSuggestor(f, frame, ArrayList(), Color.WHITE.brighter(), Color.BLUE, Color.RED, 0.75f) {
override fun wordTyped(typedWord: String?): Boolean {
//create list for dictionary this in your case might be done via calling a method which queries db and returns results as arraylist
val words = ArrayList<String>()
words.add("hello")
words.add("heritage")
words.add("happiness")
words.add("goodbye")
words.add("cruel")
words.add("car")
words.add("war")
words.add("will")
words.add("world")
words.add("wall")
setDictionary(words)
//addToDictionary("bye");//adds a single word
return super.wordTyped(typedWord)//now call super to check for any matches against newest dictionary
}
}
val p = JPanel()
p.add(f)
frame.add(p)
frame.pack()
frame.isVisible = true
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
SwingUtilities.invokeLater { Test() }
}
}
}
internal open class AutoSuggestor(val textField: JTextField, val container: Window, words: ArrayList<String>, popUpBackground: Color, private val suggestionsTextColor: Color, private val suggestionFocusedColor: Color, opacity: Float, private val callback: (String) -> Unit = {}) {
private val suggestionsPanel: JPanel
val autoSuggestionPopUpWindow: JWindow
private var typedWord: String? = null
private val dictionary = ArrayList<String>()
private var currentIndexOfSpace: Int = 0
private var tW: Int = 0
private var tH: Int = 0
private val documentListener = object : DocumentListener {
override fun insertUpdate(de: DocumentEvent) {
checkForAndShowSuggestions()
}
override fun removeUpdate(de: DocumentEvent) {
checkForAndShowSuggestions()
}
override fun changedUpdate(de: DocumentEvent) {
checkForAndShowSuggestions()
}
}
val addedSuggestionLabels: ArrayList<SuggestionLabel>
get() {
val sls = ArrayList<SuggestionLabel>()
for (i in 0 until suggestionsPanel.componentCount) {
if (suggestionsPanel.getComponent(i) is SuggestionLabel) {
val sl = suggestionsPanel.getComponent(i) as SuggestionLabel
sls.add(sl)
}
}
return sls
}
//get newest word after last white space if any or the first word if no white spaces
val currentlyTypedWord: String
get() {
val text = textField.text
var wordBeingTyped = ""
if (text.contains(" ")) {
val tmp = text.lastIndexOf(" ")
if (tmp >= currentIndexOfSpace) {
currentIndexOfSpace = tmp
wordBeingTyped = text.substring(text.lastIndexOf(" "))
}
} else {
wordBeingTyped = text
}
return wordBeingTyped.trim { it <= ' ' }
}
init {
this.textField.document.addDocumentListener(documentListener)
setDictionary(words)
typedWord = ""
currentIndexOfSpace = 0
tW = 0
tH = 0
autoSuggestionPopUpWindow = JWindow(container)
autoSuggestionPopUpWindow.opacity = opacity
suggestionsPanel = JPanel()
suggestionsPanel.layout = GridLayout(0, 1)
suggestionsPanel.background = popUpBackground
addFocusListenersToHandleVisibilityOfPopUpWindow()
addKeyBindingToRequestFocusInPopUpWindow()
}
private fun addFocusListenersToHandleVisibilityOfPopUpWindow() {
textField.addFocusListener(object:FocusListener {
override fun focusLost(e: FocusEvent?) {
var focusOnPopUp = false
for (i in 0 until suggestionsPanel.componentCount) {
if (suggestionsPanel.getComponent(i) is SuggestionLabel) {
val label = suggestionsPanel.getComponent(i) as SuggestionLabel
if (label.isFocused)
focusOnPopUp = true
}
}
if (!focusOnPopUp && !shouldShowPopUpWindow) {
autoSuggestionPopUpWindow.isVisible = false
}
}
override fun focusGained(e: FocusEvent?) {
shouldShowPopUpWindow = false
}
})
}
private fun addKeyBindingToRequestFocusInPopUpWindow() {
textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), "Escape released")
textField.actionMap.put("Escape released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {// Hide the popwindow
shouldShowPopUpWindow = false
autoSuggestionPopUpWindow.isVisible = false
}
})
textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released")
textField.actionMap.put("Down released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {//focuses the first label on popwindow
for (i in 0 until suggestionsPanel.componentCount) {
if (suggestionsPanel.getComponent(i) is SuggestionLabel) {
(suggestionsPanel.getComponent(i) as SuggestionLabel).isFocused = true
autoSuggestionPopUpWindow.toFront()
autoSuggestionPopUpWindow.requestFocusInWindow()
suggestionsPanel.requestFocusInWindow()
suggestionsPanel.getComponent(i).requestFocusInWindow()
break
}
}
}
})
textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released")
textField.actionMap.put("Up released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {//focuses the last label on popwindow
for (i in 0 until suggestionsPanel.componentCount) {
val reverseIndex = suggestionsPanel.componentCount-1 - i
if (suggestionsPanel.getComponent(reverseIndex) is SuggestionLabel) {
(suggestionsPanel.getComponent(reverseIndex) as SuggestionLabel).isFocused = true
autoSuggestionPopUpWindow.toFront()
autoSuggestionPopUpWindow.requestFocusInWindow()
suggestionsPanel.requestFocusInWindow()
suggestionsPanel.getComponent(reverseIndex).requestFocusInWindow()
break
}
}
}
})
suggestionsPanel.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), "Escape released")
suggestionsPanel.actionMap.put("Escape released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {// Hide the popwindow
shouldShowPopUpWindow = false
autoSuggestionPopUpWindow.isVisible = false
}
})
suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released")
suggestionsPanel.actionMap.put("Up released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {//allows scrolling of labels in pop window (I know very hacky for now :))
val sls = addedSuggestionLabels
val max = sls.size
var indexOfFocusedSuggestion = -1
for (i in 0 until max) {
val sl = sls[i]
if ( sl.isFocused )
indexOfFocusedSuggestion = i
}
if (indexOfFocusedSuggestion - 1 < 0) {
sls[indexOfFocusedSuggestion].isFocused = false
autoSuggestionPopUpWindow.isVisible = false
setFocusToTextField()
checkForAndShowSuggestions()//fire method as if document listener change occured and fired it
}
else {
sls[indexOfFocusedSuggestion].isFocused = false
sls[indexOfFocusedSuggestion-1].isFocused = true
autoSuggestionPopUpWindow.toFront()
autoSuggestionPopUpWindow.requestFocusInWindow()
suggestionsPanel.requestFocusInWindow()
suggestionsPanel.getComponent(indexOfFocusedSuggestion-1).requestFocusInWindow()
}
}
})
suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released")
suggestionsPanel.actionMap.put("Down released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {//allows scrolling of labels in pop window (I know very hacky for now :))
val sls = addedSuggestionLabels
val max = sls.size
var indexOfFocusedSuggestion = -1
for (i in 0 until max) {
val sl = sls[i]
if ( sl.isFocused )
indexOfFocusedSuggestion = i
}
if (indexOfFocusedSuggestion + 1 >= max) {
sls[indexOfFocusedSuggestion].isFocused = false
autoSuggestionPopUpWindow.isVisible = false
setFocusToTextField()
checkForAndShowSuggestions()//fire method as if document listener change occured and fired it
}
else {
sls[indexOfFocusedSuggestion].isFocused = false
sls[indexOfFocusedSuggestion+1].isFocused = true
autoSuggestionPopUpWindow.toFront()
autoSuggestionPopUpWindow.requestFocusInWindow()
suggestionsPanel.requestFocusInWindow()
suggestionsPanel.getComponent(indexOfFocusedSuggestion+1).requestFocusInWindow()
}
}
})
}
private fun setFocusToTextField() {
container.toFront()
container.requestFocusInWindow()
textField.requestFocusInWindow()
}
var shouldShowPopUpWindow = false
private fun checkForAndShowSuggestions() {
typedWord = currentlyTypedWord
suggestionsPanel.removeAll()//remove previos words/jlabels that were added
//used to calcualte size of JWindow as new Jlabels are added
tW = 0
tH = 0
val added = wordTyped(typedWord)
if (!added) {
if (autoSuggestionPopUpWindow.isVisible) {
autoSuggestionPopUpWindow.isVisible = false
}
} else {
shouldShowPopUpWindow = true
showPopUpWindow()
setFocusToTextField()
}
}
protected fun addWordToSuggestions(word: String) {
val suggestionLabel = SuggestionLabel(word, suggestionFocusedColor, suggestionsTextColor, this, callback)
calculatePopUpWindowSize(suggestionLabel)
suggestionsPanel.add(suggestionLabel)
}
private fun calculatePopUpWindowSize(label: JLabel) {
//so we can size the JWindow correctly
if (tW < label.preferredSize.width) {
tW = label.preferredSize.width
}
tH += label.preferredSize.height
}
private fun showPopUpWindow() {
autoSuggestionPopUpWindow.contentPane.add(suggestionsPanel)
autoSuggestionPopUpWindow.minimumSize = Dimension(textField.width, 30)
autoSuggestionPopUpWindow.setSize(tW, tH)
autoSuggestionPopUpWindow.isVisible = true
var windowX = 0
var windowY = 0
windowX = container.getX() + textField.x + 5
if (suggestionsPanel.height > autoSuggestionPopUpWindow.minimumSize.height) {
windowY = container.getY() + textField.y + textField.height + autoSuggestionPopUpWindow.minimumSize.height
} else {
windowY = container.getY() + textField.y + textField.height + autoSuggestionPopUpWindow.height
}
autoSuggestionPopUpWindow.setLocation(windowX, windowY)
autoSuggestionPopUpWindow.minimumSize = Dimension(textField.width, 30)
autoSuggestionPopUpWindow.revalidate()
autoSuggestionPopUpWindow.repaint()
}
fun setDictionary(words: ArrayList<String>?) {
dictionary.clear()
if (words == null) {
return //so we can call constructor with null value for dictionary without exception thrown
}
for (word in words) {
dictionary.add(word)
}
}
fun addToDictionary(word: String) {
dictionary.add(word)
}
open fun wordTyped(typedWord: String?): Boolean {
if (typedWord!!.isEmpty()) {
return false
}
var suggestionAdded = false
for (word in dictionary) {//get words in the dictionary which we added
var fullyMatches = word.length >= typedWord.length
for (i in 0 until typedWord.length) {//each string in the word
if (word.length > i && !typedWord.toLowerCase().startsWith(word.toLowerCase()[i].toString(), i)) {//check for match
fullyMatches = false
break
}
}
if (fullyMatches) {
addWordToSuggestions(word)
suggestionAdded = true
}
}
return suggestionAdded
}
}
internal class SuggestionLabel(string: String, private val suggestionBorderColor: Color, private val suggestionsTextColor: Color, private val autoSuggestor: AutoSuggestor, private val callback: (String) -> Unit) : JLabel(string) {
var isFocused = false
set(focused) {
if (focused) {
border = LineBorder(suggestionBorderColor)
} else {
border = null
}
repaint()
field = focused
}
private val autoSuggestionsPopUpWindow: JWindow
private val textField: JTextField
init {
this.textField = autoSuggestor.textField
this.autoSuggestionsPopUpWindow = autoSuggestor.autoSuggestionPopUpWindow
initComponent()
}
private fun initComponent() {
isFocusable = true
foreground = suggestionsTextColor
addMouseListener(object : MouseAdapter() {
override fun mouseClicked(me: MouseEvent) {
super.mouseClicked(me)
replaceWithSuggestedText()
autoSuggestionsPopUpWindow.isVisible = false
}
})
getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), "Enter released")
actionMap.put("Enter released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {
replaceWithSuggestedText()
autoSuggestionsPopUpWindow.isVisible = false
}
})
getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), "Escape released")
actionMap.put("Escape released", object : AbstractAction() {
override fun actionPerformed(ae: ActionEvent) {// Hide the popwindow
autoSuggestionsPopUpWindow.isVisible = false
}
})
}
private fun replaceWithSuggestedText() {
val suggestedWord = text
val text = textField.text
val typedWord = autoSuggestor.currentlyTypedWord
val t = text.substring(0, text.lastIndexOf(typedWord))
val tmp = t + text.substring(text.lastIndexOf(typedWord)).replace(typedWord, suggestedWord)
textField.text = tmp
callback(tmp)
}
}