mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Refactor incremental search (FindAsYouType) into ItemView class
This commit is contained in:
@@ -17,14 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.gui.deckeditor;
|
package forge.gui.deckeditor;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Point;
|
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.FocusAdapter;
|
|
||||||
import java.awt.event.FocusEvent;
|
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
@@ -33,17 +26,10 @@ import java.util.Map.Entry;
|
|||||||
|
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.Popup;
|
|
||||||
import javax.swing.PopupFactory;
|
|
||||||
import javax.swing.SwingConstants;
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.Timer;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
import javax.swing.event.ListSelectionListener;
|
import javax.swing.event.ListSelectionListener;
|
||||||
|
|
||||||
import org.apache.commons.lang3.CharUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import forge.Command;
|
import forge.Command;
|
||||||
import forge.Singletons;
|
import forge.Singletons;
|
||||||
import forge.deck.DeckBase;
|
import forge.deck.DeckBase;
|
||||||
@@ -59,9 +45,7 @@ import forge.gui.framework.FScreen;
|
|||||||
import forge.gui.framework.ICDoc;
|
import forge.gui.framework.ICDoc;
|
||||||
import forge.gui.match.controllers.CDetail;
|
import forge.gui.match.controllers.CDetail;
|
||||||
import forge.gui.match.controllers.CPicture;
|
import forge.gui.match.controllers.CPicture;
|
||||||
import forge.gui.toolbox.FLabel;
|
|
||||||
import forge.gui.toolbox.FMouseAdapter;
|
import forge.gui.toolbox.FMouseAdapter;
|
||||||
import forge.gui.toolbox.FSkin;
|
|
||||||
import forge.gui.toolbox.itemmanager.ItemManager;
|
import forge.gui.toolbox.itemmanager.ItemManager;
|
||||||
import forge.gui.toolbox.itemmanager.SItemManagerIO;
|
import forge.gui.toolbox.itemmanager.SItemManagerIO;
|
||||||
import forge.gui.toolbox.itemmanager.SItemManagerIO.EditorPreference;
|
import forge.gui.toolbox.itemmanager.SItemManagerIO.EditorPreference;
|
||||||
@@ -83,7 +67,6 @@ public enum CDeckEditorUI implements ICDoc {
|
|||||||
|
|
||||||
private final HashMap<FScreen, ACEditorBase<? extends InventoryItem, ? extends DeckBase>> screenChildControllers;
|
private final HashMap<FScreen, ACEditorBase<? extends InventoryItem, ? extends DeckBase>> screenChildControllers;
|
||||||
private ACEditorBase<? extends InventoryItem, ? extends DeckBase> childController;
|
private ACEditorBase<? extends InventoryItem, ? extends DeckBase> childController;
|
||||||
private boolean isFindingAsYouType = false;
|
|
||||||
|
|
||||||
private CDeckEditorUI() {
|
private CDeckEditorUI() {
|
||||||
screenChildControllers = new HashMap<FScreen, ACEditorBase<? extends InventoryItem, ? extends DeckBase>>();
|
screenChildControllers = new HashMap<FScreen, ACEditorBase<? extends InventoryItem, ? extends DeckBase>>();
|
||||||
@@ -344,7 +327,7 @@ public enum CDeckEditorUI implements ICDoc {
|
|||||||
catTable.getComponent().addKeyListener(new KeyAdapter() {
|
catTable.getComponent().addKeyListener(new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyPressed(KeyEvent e) {
|
public void keyPressed(KeyEvent e) {
|
||||||
if (!isFindingAsYouType && KeyEvent.VK_SPACE == e.getKeyCode()) {
|
if (!catView.isIncrementalSearchActive() && KeyEvent.VK_SPACE == e.getKeyCode()) {
|
||||||
addSelectedCards(e.isControlDown() || e.isMetaDown(), e.isShiftDown() ? 4: 1);
|
addSelectedCards(e.isControlDown() || e.isMetaDown(), e.isShiftDown() ? 4: 1);
|
||||||
}
|
}
|
||||||
else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) {
|
else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) {
|
||||||
@@ -363,7 +346,7 @@ public enum CDeckEditorUI implements ICDoc {
|
|||||||
deckTable.getComponent().addKeyListener(new KeyAdapter() {
|
deckTable.getComponent().addKeyListener(new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyPressed(KeyEvent e) {
|
public void keyPressed(KeyEvent e) {
|
||||||
if (!isFindingAsYouType && KeyEvent.VK_SPACE == e.getKeyCode()) {
|
if (!catView.isIncrementalSearchActive() && KeyEvent.VK_SPACE == e.getKeyCode()) {
|
||||||
removeSelectedCards(e.isControlDown() || e.isMetaDown(), e.isShiftDown() ? 4: 1);
|
removeSelectedCards(e.isControlDown() || e.isMetaDown(), e.isShiftDown() ? 4: 1);
|
||||||
}
|
}
|
||||||
else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) {
|
else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) {
|
||||||
@@ -422,26 +405,6 @@ public enum CDeckEditorUI implements ICDoc {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final _FindAsYouType catFind = new _FindAsYouType(catView);
|
|
||||||
final _FindAsYouType deckFind = new _FindAsYouType(deckView);
|
|
||||||
|
|
||||||
catTable.getComponent().addFocusListener(new FocusAdapter() {
|
|
||||||
@Override
|
|
||||||
public void focusLost(FocusEvent arg0) {
|
|
||||||
catFind.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
deckTable.getComponent().addFocusListener(new FocusAdapter() {
|
|
||||||
@Override
|
|
||||||
public void focusLost(FocusEvent arg0) {
|
|
||||||
deckFind.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// highlight items as the user types a portion of their names
|
|
||||||
catTable.getComponent().addKeyListener(catFind);
|
|
||||||
deckTable.getComponent().addKeyListener(deckFind);
|
|
||||||
|
|
||||||
//set card when selection changes
|
//set card when selection changes
|
||||||
catView.addSelectionListener(new ListSelectionListener() {
|
catView.addSelectionListener(new ListSelectionListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -481,153 +444,6 @@ public enum CDeckEditorUI implements ICDoc {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private class _FindAsYouType extends KeyAdapter {
|
|
||||||
private StringBuilder str = new StringBuilder();
|
|
||||||
private final FLabel popupLabel = new FLabel.Builder().fontAlign(SwingConstants.LEFT).opaque().build();
|
|
||||||
private boolean popupShowing = false;
|
|
||||||
private Popup popup;
|
|
||||||
private Timer popupTimer;
|
|
||||||
private final ItemManager<? extends InventoryItem> tableView;
|
|
||||||
static final int okModifiers = KeyEvent.SHIFT_MASK | KeyEvent.ALT_GRAPH_MASK;
|
|
||||||
|
|
||||||
public _FindAsYouType(ItemManager<? extends InventoryItem> tableView) {
|
|
||||||
this.tableView = tableView;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void _setPopupSize() {
|
|
||||||
// resize popup to size of label (ensure there's room for the next character so the label
|
|
||||||
// doesn't show '...' in the time between when we set the text and when we increase the size
|
|
||||||
Dimension labelDimension = popupLabel.getPreferredSize();
|
|
||||||
Dimension popupDimension = new Dimension(labelDimension.width + 12, labelDimension.height + 4);
|
|
||||||
SwingUtilities.getRoot(popupLabel).setSize(popupDimension);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void _findNextMatch(int startIdx, boolean reverse) {
|
|
||||||
int numItems = tableView.getTable().getCount();
|
|
||||||
if (0 == numItems) {
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the next item that matches the string
|
|
||||||
startIdx %= numItems;
|
|
||||||
final int increment = reverse ? numItems - 1 : 1;
|
|
||||||
int stopIdx = (startIdx + numItems - increment) % numItems;
|
|
||||||
String searchStr = str.toString();
|
|
||||||
boolean found = false;
|
|
||||||
for (int idx = startIdx;; idx = (idx + increment) % numItems) {
|
|
||||||
if (StringUtils.containsIgnoreCase(tableView.getTable().getItemAtIndex(idx).getName(), searchStr)) {
|
|
||||||
tableView.getTable().setSelectedIndex(idx);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == stopIdx) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchStr.isEmpty()) {
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// show a popup with the current search string, highlighted in red if not found
|
|
||||||
popupLabel.setText(searchStr + " (hit Enter for next match, Esc to cancel)");
|
|
||||||
if (found) {
|
|
||||||
FSkin.get(popupLabel).setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
FSkin.get(popupLabel).setForeground(new Color(255, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (popupShowing) {
|
|
||||||
_setPopupSize();
|
|
||||||
popupTimer.restart();
|
|
||||||
} else {
|
|
||||||
PopupFactory factory = PopupFactory.getSharedInstance();
|
|
||||||
Point tableLoc = tableView.getTable().getTable().getTableHeader().getLocationOnScreen();
|
|
||||||
popup = factory.getPopup(null, popupLabel, tableLoc.x + 10, tableLoc.y + 10);
|
|
||||||
FSkin.get(SwingUtilities.getRoot(popupLabel)).setBackground(FSkin.getColor(FSkin.Colors.CLR_INACTIVE));
|
|
||||||
|
|
||||||
popupTimer = new Timer(5000, new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
popupTimer.setRepeats(false);
|
|
||||||
|
|
||||||
popup.show();
|
|
||||||
_setPopupSize();
|
|
||||||
popupTimer.start();
|
|
||||||
isFindingAsYouType = true;
|
|
||||||
popupShowing = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
str = new StringBuilder();
|
|
||||||
popupShowing = false;
|
|
||||||
if (null != popup) {
|
|
||||||
popup.hide();
|
|
||||||
popup = null;
|
|
||||||
}
|
|
||||||
if (null != popupTimer) {
|
|
||||||
popupTimer.stop();
|
|
||||||
popupTimer = null;
|
|
||||||
}
|
|
||||||
isFindingAsYouType = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyPressed(KeyEvent e) {
|
|
||||||
if (KeyEvent.VK_ESCAPE == e.getKeyCode()) {
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyTyped(KeyEvent e) {
|
|
||||||
switch (e.getKeyChar()) {
|
|
||||||
case KeyEvent.CHAR_UNDEFINED:
|
|
||||||
return;
|
|
||||||
|
|
||||||
case KeyEvent.VK_ENTER:
|
|
||||||
case 13: // no KeyEvent constant for this, but this comes up on OSX for shift-enter
|
|
||||||
if (!str.toString().isEmpty()) {
|
|
||||||
// no need to add (or subtract) 1 -- the table selection will already
|
|
||||||
// have been advanced by the (shift+) enter key
|
|
||||||
_findNextMatch(tableView.getTable().getSelectedIndex(), e.isShiftDown());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
case KeyEvent.VK_BACK_SPACE:
|
|
||||||
if (!str.toString().isEmpty()) {
|
|
||||||
str.deleteCharAt(str.toString().length() - 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KeyEvent.VK_SPACE:
|
|
||||||
// don't trigger if the first character is a space
|
|
||||||
if (str.toString().isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// fall through
|
|
||||||
|
|
||||||
default:
|
|
||||||
// shift and/or alt-graph down is ok. anything else is a hotkey (e.g. ctrl-f)
|
|
||||||
if (okModifiers != (e.getModifiers() | okModifiers)
|
|
||||||
|| !CharUtils.isAsciiPrintable(e.getKeyChar())) { // escape sneaks in here on Windows
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
str.append(e.getKeyChar());
|
|
||||||
}
|
|
||||||
|
|
||||||
_findNextMatch(Math.max(0, tableView.getTable().getSelectedIndex()), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ public abstract class ItemManager<T extends InventoryItem> extends JPanel {
|
|||||||
if (this.initialized) { return; } //avoid initializing more than once
|
if (this.initialized) { return; } //avoid initializing more than once
|
||||||
|
|
||||||
//build table view
|
//build table view
|
||||||
|
this.table.initialize();
|
||||||
this.viewScroller.setOpaque(false);
|
this.viewScroller.setOpaque(false);
|
||||||
this.viewScroller.getViewport().setOpaque(false);
|
this.viewScroller.getViewport().setOpaque(false);
|
||||||
this.viewScroller.setBorder(null);
|
this.viewScroller.setBorder(null);
|
||||||
@@ -870,6 +871,16 @@ public abstract class ItemManager<T extends InventoryItem> extends JPanel {
|
|||||||
return this.pnlButtons;
|
return this.pnlButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isIncrementalSearchActive.
|
||||||
|
*
|
||||||
|
* @return true if an incremental search is currently active
|
||||||
|
*/
|
||||||
|
public boolean isIncrementalSearchActive() {
|
||||||
|
return this.table.isIncrementalSearchActive();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* getWantUnique.
|
* getWantUnique.
|
||||||
|
|||||||
@@ -171,6 +171,11 @@ public final class ItemListView<T extends InventoryItem> extends ItemView<T> {
|
|||||||
return this.table;
|
return this.table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Point getLocationOnScreen() {
|
||||||
|
return this.table.getTableHeader().getLocationOnScreen(); //use table header's location since that stays in place
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getCaption() {
|
protected String getCaption() {
|
||||||
return "List View";
|
return "List View";
|
||||||
|
|||||||
@@ -1,27 +1,62 @@
|
|||||||
package forge.gui.toolbox.itemmanager.views;
|
package forge.gui.toolbox.itemmanager.views;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.FocusAdapter;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.Popup;
|
||||||
|
import javax.swing.PopupFactory;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.CharUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.gui.toolbox.FLabel;
|
||||||
|
import forge.gui.toolbox.FSkin;
|
||||||
import forge.gui.toolbox.itemmanager.ItemManager;
|
import forge.gui.toolbox.itemmanager.ItemManager;
|
||||||
import forge.item.InventoryItem;
|
import forge.item.InventoryItem;
|
||||||
|
|
||||||
public abstract class ItemView<T extends InventoryItem> {
|
public abstract class ItemView<T extends InventoryItem> {
|
||||||
private final ItemManager<T> itemManager;
|
private final ItemManager<T> itemManager;
|
||||||
|
private boolean isIncrementalSearchActive = false;
|
||||||
|
|
||||||
protected ItemView(ItemManager<T> itemManager0) {
|
protected ItemView(ItemManager<T> itemManager0) {
|
||||||
this.itemManager = itemManager0;
|
this.itemManager = itemManager0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initialize() {
|
||||||
|
//hook incremental search functionality
|
||||||
|
final IncrementalSearch incrementalSearch = new IncrementalSearch();
|
||||||
|
this.getComponent().addFocusListener(new FocusAdapter() {
|
||||||
|
@Override
|
||||||
|
public void focusLost(FocusEvent arg0) {
|
||||||
|
incrementalSearch.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.getComponent().addKeyListener(incrementalSearch);
|
||||||
|
}
|
||||||
|
|
||||||
public ItemManager<T> getItemManager() {
|
public ItemManager<T> getItemManager() {
|
||||||
return this.itemManager;
|
return this.itemManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isIncrementalSearchActive() {
|
||||||
|
return this.isIncrementalSearchActive;
|
||||||
|
}
|
||||||
|
|
||||||
public final T getSelectedItem() {
|
public final T getSelectedItem() {
|
||||||
int index = getSelectedIndex();
|
int index = getSelectedIndex();
|
||||||
return index >= 0 ? getItemAtIndex(index) : null;
|
return index >= 0 ? getItemAtIndex(index) : null;
|
||||||
@@ -102,6 +137,10 @@ public abstract class ItemView<T extends InventoryItem> {
|
|||||||
return this.getComponent().hasFocus();
|
return this.getComponent().hasFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Point getLocationOnScreen() {
|
||||||
|
return this.getComponent().getParent().getLocationOnScreen(); //use parent scroller's location by default
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.getCaption(); //return caption as string for display in combo box
|
return this.getCaption(); //return caption as string for display in combo box
|
||||||
@@ -121,4 +160,150 @@ public abstract class ItemView<T extends InventoryItem> {
|
|||||||
protected abstract void onSetSelectedIndex(int index);
|
protected abstract void onSetSelectedIndex(int index);
|
||||||
protected abstract void onSetSelectedIndices(Iterable<Integer> indices);
|
protected abstract void onSetSelectedIndices(Iterable<Integer> indices);
|
||||||
protected abstract void onScrollSelectionIntoView(JViewport viewport);
|
protected abstract void onScrollSelectionIntoView(JViewport viewport);
|
||||||
|
|
||||||
|
private class IncrementalSearch extends KeyAdapter {
|
||||||
|
private StringBuilder str = new StringBuilder();
|
||||||
|
private final FLabel popupLabel = new FLabel.Builder().fontAlign(SwingConstants.LEFT).opaque().build();
|
||||||
|
private boolean popupShowing = false;
|
||||||
|
private Popup popup;
|
||||||
|
private Timer popupTimer;
|
||||||
|
private static final int okModifiers = KeyEvent.SHIFT_MASK | KeyEvent.ALT_GRAPH_MASK;
|
||||||
|
|
||||||
|
public IncrementalSearch() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPopupSize() {
|
||||||
|
// resize popup to size of label (ensure there's room for the next character so the label
|
||||||
|
// doesn't show '...' in the time between when we set the text and when we increase the size
|
||||||
|
Dimension labelDimension = popupLabel.getPreferredSize();
|
||||||
|
Dimension popupDimension = new Dimension(labelDimension.width + 12, labelDimension.height + 4);
|
||||||
|
SwingUtilities.getRoot(popupLabel).setSize(popupDimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findNextMatch(int startIdx, boolean reverse) {
|
||||||
|
int numItems = itemManager.getItemCount();
|
||||||
|
if (0 == numItems) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the next item that matches the string
|
||||||
|
startIdx %= numItems;
|
||||||
|
final int increment = reverse ? numItems - 1 : 1;
|
||||||
|
int stopIdx = (startIdx + numItems - increment) % numItems;
|
||||||
|
String searchStr = str.toString();
|
||||||
|
boolean found = false;
|
||||||
|
for (int idx = startIdx;; idx = (idx + increment) % numItems) {
|
||||||
|
if (StringUtils.containsIgnoreCase(ItemView.this.getItemAtIndex(idx).getName(), searchStr)) {
|
||||||
|
ItemView.this.setSelectedIndex(idx);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == stopIdx) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchStr.isEmpty()) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// show a popup with the current search string, highlighted in red if not found
|
||||||
|
popupLabel.setText(searchStr + " (hit Enter for next match, Esc to cancel)");
|
||||||
|
if (found) {
|
||||||
|
FSkin.get(popupLabel).setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FSkin.get(popupLabel).setForeground(new Color(255, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popupShowing) {
|
||||||
|
setPopupSize();
|
||||||
|
popupTimer.restart();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PopupFactory factory = PopupFactory.getSharedInstance();
|
||||||
|
Point tableLoc = ItemView.this.getLocationOnScreen();
|
||||||
|
popup = factory.getPopup(null, popupLabel, tableLoc.x + 10, tableLoc.y + 10);
|
||||||
|
FSkin.get(SwingUtilities.getRoot(popupLabel)).setBackground(FSkin.getColor(FSkin.Colors.CLR_INACTIVE));
|
||||||
|
|
||||||
|
popupTimer = new Timer(5000, new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
popupTimer.setRepeats(false);
|
||||||
|
|
||||||
|
popup.show();
|
||||||
|
setPopupSize();
|
||||||
|
popupTimer.start();
|
||||||
|
isIncrementalSearchActive = true;
|
||||||
|
popupShowing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
str = new StringBuilder();
|
||||||
|
popupShowing = false;
|
||||||
|
if (null != popup) {
|
||||||
|
popup.hide();
|
||||||
|
popup = null;
|
||||||
|
}
|
||||||
|
if (null != popupTimer) {
|
||||||
|
popupTimer.stop();
|
||||||
|
popupTimer = null;
|
||||||
|
}
|
||||||
|
isIncrementalSearchActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (KeyEvent.VK_ESCAPE == e.getKeyCode()) {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
switch (e.getKeyChar()) {
|
||||||
|
case KeyEvent.CHAR_UNDEFINED:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case KeyEvent.VK_ENTER:
|
||||||
|
case 13: // no KeyEvent constant for this, but this comes up on OSX for shift-enter
|
||||||
|
if (!str.toString().isEmpty()) {
|
||||||
|
// no need to add (or subtract) 1 -- the table selection will already
|
||||||
|
// have been advanced by the (shift+) enter key
|
||||||
|
findNextMatch(ItemView.this.getSelectedIndex(), e.isShiftDown());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case KeyEvent.VK_BACK_SPACE:
|
||||||
|
if (!str.toString().isEmpty()) {
|
||||||
|
str.deleteCharAt(str.toString().length() - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyEvent.VK_SPACE:
|
||||||
|
// don't trigger if the first character is a space
|
||||||
|
if (str.toString().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
|
||||||
|
default:
|
||||||
|
// shift and/or alt-graph down is ok. anything else is a hotkey (e.g. ctrl-f)
|
||||||
|
if (okModifiers != (e.getModifiers() | okModifiers)
|
||||||
|
|| !CharUtils.isAsciiPrintable(e.getKeyChar())) { // escape sneaks in here on Windows
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
str.append(e.getKeyChar());
|
||||||
|
}
|
||||||
|
|
||||||
|
findNextMatch(Math.max(0, ItemView.this.getSelectedIndex()), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user