Skip to content

Commit

Permalink
Add keyboard shortcut to the Quick Search feature
Browse files Browse the repository at this point in the history
Shortcut: Ctrl-/ (Divide key on the number block)
Implemented workaround to support shortcuts even in focused text areas.
  • Loading branch information
Argent77 committed Oct 27, 2024
1 parent 651816d commit 08506cf
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 4 deletions.
24 changes: 22 additions & 2 deletions src/org/infinity/NearInfinity.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
Expand All @@ -47,9 +48,11 @@
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
Expand All @@ -60,6 +63,7 @@
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.LookAndFeel;
import javax.swing.ProgressMonitor;
import javax.swing.SwingConstants;
Expand Down Expand Up @@ -162,6 +166,9 @@ public final class NearInfinity extends JFrame implements ActionListener, Viewab

private static final String STATUSBAR_TEXT_FMT = "Welcome to Near Infinity! - %s @ %s - %d files available";

// Input map key for triggering the quick search feature
private static final String ACTIONMAP_KEY_QUICK_SEARCH = "SHORTCUT_OPEN_QUICK_SEARCH";

private static final List<Class<? extends LookAndFeel>> CUSTOM_LOOK_AND_FEELS = new ArrayList<>();

static {
Expand Down Expand Up @@ -529,12 +536,14 @@ public void windowClosing(WindowEvent event) {
b.setMargin(new Insets(0, 0, 0, 0));
toolBar.add(b);
toolBar.addSeparator(new Dimension(8, 24));

final String shortcutModifier =
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() == KeyEvent.CTRL_MASK ? "Ctrl" : "⌘";
bpwQuickSearch = new ButtonPopupWindow(Icons.ICON_MAGNIFY_16.getIcon());
bpwQuickSearch.setToolTipText("Find resource");
bpwQuickSearch.setToolTipText("Find resource (Shortcut: " + shortcutModifier + "-/)");
bpwQuickSearch.setMargin(new Insets(4, 4, 4, 4));
toolBar.add(bpwQuickSearch);
bpwQuickSearch.addPopupWindowListener(new PopupWindowListener() {

@Override
public void popupWindowWillBecomeVisible(PopupWindowEvent event) {
// XXX: Working around a visual glitch in QuickSearch's JComboBox popup list
Expand All @@ -558,6 +567,17 @@ public void popupWindowWillBecomeInvisible(PopupWindowEvent event) {
}
});

// registering window-global shortcut for the quick search option
final int ctrl = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, ctrl), ACTIONMAP_KEY_QUICK_SEARCH);
getRootPane().getActionMap().put(ACTIONMAP_KEY_QUICK_SEARCH, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
bpwQuickSearch.showPopupWindow();
}
});

toolBar.add(Box.createHorizontalGlue());
btnLaunchGame = new JButton(Icons.ICON_LAUNCH_24.getIcon());
btnLaunchGame.setFocusable(false);
Expand Down
82 changes: 80 additions & 2 deletions src/org/infinity/gui/InfinityTextArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
Expand All @@ -17,8 +22,15 @@
import java.util.TreeMap;
import java.util.function.Supplier;

import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
Expand Down Expand Up @@ -49,7 +61,7 @@
/**
* Extends {@link RSyntaxTextArea} by NearInfinity-specific features.
*/
public class InfinityTextArea extends RSyntaxTextArea implements ChangeListener {
public class InfinityTextArea extends RSyntaxTextArea implements ChangeListener, KeyListener, HierarchyListener {
/** Available languages for syntax highlighting. */
public enum Language {
/** Disables syntax highlighting */
Expand Down Expand Up @@ -175,6 +187,7 @@ public String getScheme() {

private final SortedMap<Integer, GutterIcon> gutterIcons = new TreeMap<>();
private final Map<Integer, GutterIconInfo> gutterIconsActive = new HashMap<>();
private final HashMap<KeyStroke, Action> inputActionMap = new HashMap<>();

private RTextScrollPane scrollPane;

Expand Down Expand Up @@ -296,8 +309,12 @@ public InfinityTextArea(RSyntaxDocument doc, String text, int rows, int cols, bo
*
* @param resetUndo Specifies whether the undo history will be discarded.
*/
public static void applySettings(RSyntaxTextArea edit, boolean resetUndo) {
public static void applySettings(InfinityTextArea edit, boolean resetUndo) {
if (edit != null) {
// Allows key strokes defined by a parent components to be processed when this component has focus
edit.addKeyListener(edit);
edit.addHierarchyListener(edit);

edit.setCurrentLineHighlightColor(DEFAULT_LINE_HIGHLIGHT_COLOR);
if (BrowserMenuBar.isInstantiated()) {
edit.setTabsEmulated(BrowserMenuBar.getInstance().getOptions().isTextTabEmulated());
Expand Down Expand Up @@ -484,6 +501,67 @@ public void stateChanged(ChangeEvent e) {

// --------------------- End Interface ChangeListener ---------------------

// --------------------- Begin Interface KeyListener ---------------------

@Override
public void keyTyped(KeyEvent e) {
}

@Override
public void keyPressed(KeyEvent e) {
// Processing key strokes defined in parent components.
// Registration of key strokes is handled by the HierarchyListener.
final KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers());
final Action action = inputActionMap.get(keyStroke);
if (action != null) {
action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""));
e.consume();
}
}

@Override
public void keyReleased(KeyEvent e) {
}

// --------------------- End Interface KeyListener ---------------------

// --------------------- Begin Interface HierarchyListener ---------------------

@Override
public void hierarchyChanged(HierarchyEvent e) {
// InfinityTextArea appears to discard or override certain global key stroke definitions.
// This method registers key strokes from parent components, so that they can still be processed.
// Registration process is placed into a HierarchyListener to register key strokes even if
// parent components are assigned later.
if (!(e.getChanged() instanceof JComponent && getTopLevelAncestor() instanceof JFrame)) {
return;
}

final JRootPane rootPane = ((JFrame) getTopLevelAncestor()).getRootPane();
if (rootPane == null) {
return;
}

final InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
final KeyStroke[] keyStrokes = inputMap.allKeys();
final ActionMap actionMap = rootPane.getActionMap();
if (inputMap == null || keyStrokes == null || keyStrokes.length == 0 || actionMap == null) {
return;
}

for (final KeyStroke keyStroke : keyStrokes) {
final Object binding = inputMap.get(keyStroke);
if (binding != null) {
final Action action = actionMap.get(binding);
if (action != null) {
inputActionMap.put(keyStroke, action);
}
}
}
}

// --------------------- End Interface HierarchyListener ---------------------

/**
* Returns the underlying ScrollPane if available. Returns {@code null} otherwise.
*/
Expand Down

0 comments on commit 08506cf

Please sign in to comment.