implemented JSearchBar

master
Niclas Thobaben 2018-06-20 17:09:48 +02:00
parent a49cef4e00
commit a3205a1cce
7 changed files with 526 additions and 1 deletions

Binary file not shown.

View File

@ -1,5 +1,116 @@
package nt.UI.control;
public class JSearchBar {
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.TextField;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import nt.UI.control.interfaces.SearchBarFinder;
public class JSearchBar extends JSearchTextField{
private static final long serialVersionUID = 1L;
protected static final File SYMBOLS_FILE = null;
protected SearchBarFinder finder;
protected SearchDropDown dropDown;
protected boolean completionActive = false;
protected String lastEntered = "";
private final JSearchBarController controller;
public JSearchBar(SearchBarFinder finder) {
this.finder = finder;
this.dropDown = new SearchDropDown();
setFont(new Font("LigatureSymbols", Font.PLAIN, 13));
this.controller = new JSearchBarController(this);
controller.setDefaultState();
}
@Override
public void setVisible(boolean b) {
super.setVisible(b);
txtSearch.setText("\uE116 search");
txtSearch.setHorizontalAlignment(JTextField.CENTER);
}
protected void doCompletion() {
System.out.println("completion");
String[] tokens = this.finder.getSearchResults(txtSearch.getText());
if(tokens.length > 1) {
this.dropDown.putItems(tokens);
if(!this.dropDown.isVisible()) {
this.dropDown.setVisible(true);
}
}else {
if(this.dropDown.isVisible()) {
this.dropDown.setVisible(false);
}
}
}
protected void startCompletion() {
this.completionActive = true;
this.dropDown.setVisible(true);
}
protected void stopCompletion() {
this.completionActive = false;
this.dropDown.setVisible(false);
}
protected class SearchDropDown extends JWindow{
private static final long serialVersionUID = 1L;
private static final int ROW_COUNT = 5;
protected DefaultListModel<String> data = new DefaultListModel<>();
protected JList<String> list = new JList<>(data);
protected JScrollPane scrollPane = new JScrollPane(list);
public SearchDropDown() {
this.list.setVisibleRowCount(ROW_COUNT);
setAlwaysOnTop(true);
add(this.scrollPane);
}
public void putItems(String[] tokens) {
this.data.clear();
for(String token : tokens) {
this.data.addElement(token);
}
positionMenu();
}
public void positionMenu() {
JSearchBar parent = JSearchBar.this;
if(parent.isShowing()) {
if (this.list.getFixedCellHeight() < 0) {
this.list.setFixedCellHeight(this.getFontMetrics(this.getFont()).getHeight() + 2);
}
int x, y, width, height;
Dimension dim = parent.getSize();
x = parent.getLocationOnScreen().x;
y = parent.getLocationOnScreen().y + dim.height;
width = dim.width;
height = this.list.getFixedCellHeight() * ROW_COUNT;
setSize(new Dimension(width, height));
setLocation(x, y);
}
}
}
}

View File

@ -0,0 +1,202 @@
package nt.UI.control;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import nt.UI.util.DocumentAdapter;
public class JSearchBarController extends KeyAdapter implements DocumentListener, FocusListener, ComponentListener, HierarchyBoundsListener, ListSelectionListener{
private static final String COMPLETION_CANCEL_KEY = "comp.cancel";
private static final String SELECT_KEY = "comp.select";
private static final KeyStroke COMPLETION_CANCEL_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
private static final KeyStroke SELECT_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
private JSearchBar searchBar;
protected JSearchBarController(JSearchBar searchBar) {
this.searchBar = searchBar;
searchBar.txtSearch.addFocusListener(this);
searchBar.txtSearch.addKeyListener(this);
searchBar.txtSearch.getDocument().addDocumentListener(this);
InputMap inputMap = searchBar.txtSearch.getInputMap();
inputMap.put(COMPLETION_CANCEL_STROKE, COMPLETION_CANCEL_KEY);
ActionMap actionMap = searchBar.txtSearch.getActionMap();
actionMap.put(COMPLETION_CANCEL_KEY, new AbstractAction() {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
setDefaultState();
}
});
searchBar.txtSearch.addComponentListener(this);
searchBar.txtSearch.addHierarchyBoundsListener(this);
searchBar.addActionListener(e -> setDefaultState());
searchBar.dropDown.list.addListSelectionListener(this);
}
protected void setDefaultState() {
JTextField txt = this.searchBar.txtSearch;
this.searchBar.showCancelButton(false);
txt.setText("search");
txt.setFont(txt.getFont().deriveFont(Font.ITALIC));
this.searchBar.repaint();
this.searchBar.stopCompletion();
Window win = SwingUtilities.getWindowAncestor(searchBar);
if(win != null) {
win.requestFocus();
}
}
protected void setEnteredState() {
JTextField txt = this.searchBar.txtSearch;
this.searchBar.showCancelButton(true);
txt.setFont(txt.getFont().deriveFont(Font.PLAIN));
}
@Override
public void focusGained(FocusEvent e) {
JTextField bar = this.searchBar.txtSearch;
if(bar.getText().toLowerCase().equals("search")) {
bar.setText("");
}
setEnteredState();
}
@Override
public void focusLost(FocusEvent e) {
JTextField bar = this.searchBar.txtSearch;
if(bar.getText().isEmpty() || bar.getText().toLowerCase().equals("search")) {
setDefaultState();
}else {
setEnteredState();
}
this.searchBar.stopCompletion();
}
@Override
public void keyPressed(KeyEvent e) {
if(this.searchBar.completionActive) {
JList<?> list = searchBar.dropDown.list;
int index = list.getSelectedIndex();
if(e.getKeyCode() == KeyEvent.VK_DOWN) {
index = index >= list.getModel().getSize()? list.getModel().getSize()-1:++index;
}else if(e.getKeyCode() == KeyEvent.VK_UP) {
index = index <= -1? -1 : --index;
}
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
}
@Override
public void componentResized(ComponentEvent e) {
if(this.searchBar.completionActive) {
this.searchBar.dropDown.positionMenu();
}
}
@Override
public void componentMoved(ComponentEvent e) {
if(this.searchBar.completionActive) {
this.searchBar.dropDown.positionMenu();
}
}
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentHidden(ComponentEvent e) {
// TODO Auto-generated method stub
}
@Override
public void ancestorMoved(HierarchyEvent e) {
if(this.searchBar.completionActive) {
this.searchBar.stopCompletion();
}
}
@Override
public void ancestorResized(HierarchyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void valueChanged(ListSelectionEvent e) {
String val = searchBar.dropDown.list.getSelectedValue();
if(val != null) {
JTextField field = searchBar.txtSearch;
field.setText(val);
field.setCaretPosition(field.getText().length());
}
}
@Override
public void insertUpdate(DocumentEvent e) {
if(searchBar.txtSearch.getDocument().getLength() > 0) {
if(!searchBar.completionActive) {
searchBar.startCompletion();
}
searchBar.doCompletion();
}else {
searchBar.stopCompletion();
}
}
@Override
public void removeUpdate(DocumentEvent e) {
searchBar.doCompletion();
}
@Override
public void changedUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,105 @@
package nt.UI.control;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.text.JTextComponent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class JSearchTextField extends JPanel{
private static final long serialVersionUID = 1L;
private JLabel lblLens;
protected JTextField txtSearch;
protected JButton btnCancel;
private Component horizontalStrut;
private Component horizontalStrut_1;
public JSearchTextField() {
super();
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
Font symbols = new Font("LigatureSymbols", Font.PLAIN, 18);
horizontalStrut_1 = Box.createHorizontalStrut(20);
add(horizontalStrut_1);
lblLens = new JLabel("\uE116");
lblLens.setFont(symbols);
add(lblLens);
horizontalStrut = Box.createHorizontalStrut(10);
add(horizontalStrut);
txtSearch = new JTextField() {
private static final long serialVersionUID = 1L;
@Override
public void setText(String txt) {
}
};
txtSearch.setText("search");
txtSearch.setBorder(new EmptyBorder(0, 0, 0, 0));
txtSearch.setBackground(getBackground());
add(txtSearch);
txtSearch.setColumns(10);
btnCancel = new JButton("\uE10f");
btnCancel.setHorizontalAlignment(SwingConstants.RIGHT);
btnCancel.setBorderPainted(false);
btnCancel.setFont(symbols);
btnCancel.setOpaque(false);
btnCancel.setVisible(false);
add(btnCancel);
setBorder(new LineBorder(Color.LIGHT_GRAY));
setPreferredSize(btnCancel.getPreferredSize());
setMinimumSize(btnCancel.getMinimumSize());
}
public void addActionListener(ActionListener l) {
this.btnCancel.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
this.btnCancel.removeActionListener(l);
}
public void showCancelButton(boolean b) {
this.btnCancel.setVisible(b);
System.out.println(b);
revalidate();
}
@Override
public void setBackground(Color bg) {
super.setBackground(bg);
for(Component c : getComponents()) {
c.setBackground(bg);
}
}
@Override
public void setForeground(Color fg) {
super.setForeground(fg);
for(Component c : getComponents()) {
c.setForeground(fg);
}
}
}

View File

@ -0,0 +1,7 @@
package nt.UI.control.interfaces;
public interface SearchBarFinder {
public String[] getSearchResults(String token);
}

View File

@ -0,0 +1,71 @@
package nt.UI.tests;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.metal.MetalLookAndFeel;
import nt.UI.control.JSearchBar;
import nt.UI.control.JSearchTextField;
import nt.UI.control.interfaces.SearchBarFinder;
import javax.swing.BoxLayout;
import java.awt.Component;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JTextField;
public class UITestMain extends JFrame{
private static final boolean DEBUG_LAF = false;
public static void main(String[] args) {
if(DEBUG_LAF) {
try {
UIManager.setLookAndFeel(new MetalLookAndFeel());
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
UITestMain frame = new UITestMain();
frame.setVisible(true);
}
public UITestMain() {
setSize(400,400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
getContentPane().add(new JSearchBar(new SearchBarFinder() {
@Override
public String[] getSearchResults(String token) {
List<String> list = Arrays.asList("a aa aaa aaaa b bb bbb bbbb bbbbb ab aabb bbaa baba".split(" "));
LinkedList<String> results = new LinkedList<>();
for(String tok : list) {
if(tok.startsWith(token)) {
System.out.println(tok + " " + token);
results.add(tok);
}
}
return results.toArray(new String[0]);
}
}));
Component verticalStrut = Box.createVerticalStrut(300);
getContentPane().add(verticalStrut);
JButton btnBtn = new JButton("btn");
getContentPane().add(btnBtn);
JSearchTextField searchTextField = new JSearchTextField();
getContentPane().add(searchTextField);
searchTextField.requestFocus();
}
}

View File

@ -0,0 +1,29 @@
package nt.UI.util;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public abstract class DocumentAdapter implements DocumentListener{
public enum DEType {
INSERT, REMOVE, CHANGE
};
@Override
public void insertUpdate(DocumentEvent e) {
documentChanged(e, DEType.INSERT);
}
@Override
public void removeUpdate(DocumentEvent e) {
documentChanged(e, DEType.REMOVE);
}
@Override
public void changedUpdate(DocumentEvent e) {
documentChanged(e, DEType.CHANGE);
}
public abstract void documentChanged(DocumentEvent e, DEType type);
}