IconTextField: add combobox-style suggestion dropdown
This commit is contained in:
@@ -31,25 +31,36 @@ import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListDataEvent;
|
||||
import javax.swing.event.ListDataListener;
|
||||
import javax.swing.text.Document;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.pushingpixels.substance.internal.ui.SubstanceListUI;
|
||||
|
||||
/**
|
||||
* This component is a FlatTextField with an icon on its left side, and a clear button (×) on its right side.
|
||||
@@ -58,10 +69,13 @@ public class IconTextField extends JPanel
|
||||
{
|
||||
// To support gifs, the icon needs to be wrapped in a JLabel
|
||||
private final JLabel iconWrapperLabel;
|
||||
|
||||
private final FlatTextField textField;
|
||||
|
||||
private final JButton clearButton;
|
||||
private final JButton suggestionButton;
|
||||
|
||||
@Getter
|
||||
private final DefaultListModel<String> suggestionListModel;
|
||||
|
||||
public IconTextField()
|
||||
{
|
||||
@@ -108,66 +122,146 @@ public class IconTextField extends JPanel
|
||||
textField.addMouseListener(hoverEffect);
|
||||
innerTxt.addMouseListener(hoverEffect);
|
||||
|
||||
clearButton = new JButton("×");
|
||||
clearButton.setPreferredSize(new Dimension(30, 0));
|
||||
clearButton.setFont(FontManager.getRunescapeBoldFont());
|
||||
clearButton.setForeground(ColorScheme.PROGRESS_ERROR_COLOR);
|
||||
clearButton.setBorder(null);
|
||||
clearButton.setBorderPainted(false);
|
||||
clearButton.setContentAreaFilled(false);
|
||||
clearButton.setVisible(false);
|
||||
|
||||
// ActionListener for keyboard use (via Tab -> Space)
|
||||
clearButton = createRHSButton(ColorScheme.PROGRESS_ERROR_COLOR, Color.PINK);
|
||||
clearButton.setText("×");
|
||||
clearButton.addActionListener(evt -> setText(null));
|
||||
|
||||
// MouseListener for hover and click events
|
||||
clearButton.addMouseListener(new MouseAdapter()
|
||||
suggestionListModel = new DefaultListModel<>();
|
||||
suggestionListModel.addListDataListener(new ListDataListener()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
public void intervalAdded(ListDataEvent e)
|
||||
{
|
||||
setText(null);
|
||||
updateContextButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
public void intervalRemoved(ListDataEvent e)
|
||||
{
|
||||
clearButton.setForeground(Color.PINK);
|
||||
textField.dispatchEvent(mouseEvent);
|
||||
updateContextButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
public void contentsChanged(ListDataEvent e)
|
||||
{
|
||||
clearButton.setForeground(ColorScheme.PROGRESS_ERROR_COLOR);
|
||||
textField.dispatchEvent(mouseEvent);
|
||||
updateContextButton();
|
||||
}
|
||||
});
|
||||
|
||||
JList<String> suggestionList = new JList<>();
|
||||
suggestionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
suggestionList.setModel(suggestionListModel);
|
||||
suggestionList.addListSelectionListener(e ->
|
||||
{
|
||||
String val = suggestionList.getSelectedValue();
|
||||
if (val == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
textField.setText(val);
|
||||
textField.getTextField().selectAll();
|
||||
textField.getTextField().requestFocusInWindow();
|
||||
});
|
||||
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
popup.setLightWeightPopupEnabled(true);
|
||||
popup.setLayout(new BorderLayout());
|
||||
popup.add(suggestionList, BorderLayout.CENTER);
|
||||
popup.addFocusListener(new FocusAdapter()
|
||||
{
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
popup.setVisible(false);
|
||||
suggestionList.clearSelection();
|
||||
|
||||
SubstanceListUI ui = (SubstanceListUI) suggestionList.getUI();
|
||||
ui.resetRolloverIndex();
|
||||
}
|
||||
});
|
||||
|
||||
suggestionButton = createRHSButton(ColorScheme.LIGHT_GRAY_COLOR, ColorScheme.MEDIUM_GRAY_COLOR);
|
||||
suggestionButton.setText("▾");
|
||||
suggestionButton.addActionListener(e ->
|
||||
{
|
||||
popup.setPopupSize(getWidth(), suggestionList.getPreferredSize().height);
|
||||
popup.show(IconTextField.this, 0, suggestionButton.getHeight());
|
||||
popup.revalidate();
|
||||
popup.requestFocusInWindow();
|
||||
});
|
||||
|
||||
// Show the clear button when text is present, and hide again when empty
|
||||
textField.getTextField().getDocument().addDocumentListener(new DocumentListener()
|
||||
{
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
SwingUtilities.invokeLater(() -> clearButton.setVisible(true));
|
||||
updateContextButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
SwingUtilities.invokeLater(() -> clearButton.setVisible(!getText().isEmpty()));
|
||||
updateContextButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
updateContextButton();
|
||||
}
|
||||
});
|
||||
|
||||
JPanel rhsButtons = new JPanel();
|
||||
rhsButtons.setBackground(new Color(0, 0, 0, 0));
|
||||
rhsButtons.setOpaque(false);
|
||||
rhsButtons.setLayout(new BorderLayout());
|
||||
rhsButtons.add(clearButton, BorderLayout.EAST);
|
||||
rhsButtons.add(suggestionButton, BorderLayout.WEST);
|
||||
updateContextButton();
|
||||
|
||||
add(iconWrapperLabel, BorderLayout.WEST);
|
||||
add(textField, BorderLayout.CENTER);
|
||||
add(clearButton, BorderLayout.EAST);
|
||||
add(rhsButtons, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
private JButton createRHSButton(Color fg, Color rollover)
|
||||
{
|
||||
JButton b = new JButton();
|
||||
b.setPreferredSize(new Dimension(30, 0));
|
||||
b.setFont(FontManager.getRunescapeBoldFont());
|
||||
b.setBorder(null);
|
||||
b.setRolloverEnabled(true);
|
||||
SwingUtil.removeButtonDecorations(b);
|
||||
b.setForeground(fg);
|
||||
|
||||
b.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
b.setForeground(rollover);
|
||||
textField.dispatchEvent(mouseEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
b.setForeground(fg);
|
||||
textField.dispatchEvent(mouseEvent);
|
||||
}
|
||||
});
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private void updateContextButton()
|
||||
{
|
||||
boolean empty = StringUtils.isBlank(textField.getText());
|
||||
|
||||
clearButton.setVisible(!empty);
|
||||
suggestionButton.setVisible(!suggestionListModel.isEmpty() && empty);
|
||||
}
|
||||
|
||||
public void addActionListener(ActionListener actionListener)
|
||||
@@ -188,6 +282,7 @@ public class IconTextField extends JPanel
|
||||
|
||||
public void setText(String text)
|
||||
{
|
||||
assert SwingUtilities.isEventDispatchThread();
|
||||
textField.setText(text);
|
||||
}
|
||||
|
||||
@@ -290,5 +385,4 @@ public class IconTextField extends JPanel
|
||||
|
||||
private final String file;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user