Reputation: 75
I'm adding a Help Menu to my program and I'm trying to figure out if there's a way to intercept keyboard input without having to use the setFocusable method on the JFrame. It works no problem if I set the focus to the frame and press F1. The problem comes in when they press on any other component. The Frame is no longer in focus, so the F1 key no longer displays the menu. Here is how I did it
package helpmenu;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class HelpMenu extends JFrame
{
public HelpMenu()
{
setSize(300, 300);
setTitle("Help Menu");
}
/**
* Sets a KeyListener to
* the passed in JFrame.
* @param mainFrame
*/
public void callMenu( JFrame mainFrame )
{
mainFrame.addKeyListener( new functionListener() ); //Set keylistener to the main frame.
mainFrame.setFocusable(true); //Put the main frame into focus.
}
/**
* Inner KeyListener
* class for the mainFrame
* @author Shaun
*/
class functionListener implements KeyListener
{
@Override
public void keyPressed(KeyEvent e)
{
//Check if the F1 key was pressed
if ( e.getKeyCode() == KeyEvent.VK_F1 ) {
setVisible(true);
}
}
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyTyped(KeyEvent e) {}
}
}
Upvotes: 0
Views: 908
Reputation: 37855
Don't use KeyListener for this type of thing. As you've noticed, one reason is it requires the component to be in focus. Instead, use a Key Binding with the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
input map.
Also, somewhat related, see "The Use of Multiple JFrames, Good/Bad Practice?" Your Help Menu should probably be a JDialog.
Here's an example of using a Key Binding:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class HelpDialogEx {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new HelpDialogEx();
}
});
}
final JFrame frame = new JFrame("Press F1 for help");
final JPanel cpane = new JPanel(new GridLayout(4, 5, 10, 10));
final HelpDialog help = new HelpDialog(frame);
final AbstractAction helpToggle = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ae) {
help.setVisible(!help.isVisible());
}
};
HelpDialogEx() {
for(int i = 0; i < 20; i++) {
cpane.add(new JButton("Nil"));
}
cpane.setPreferredSize(new Dimension(640, 480));
frame.setContentPane(cpane);
addHelpToggle(frame.getRootPane());
addHelpToggle(help.getRootPane());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void addHelpToggle(JComponent comp) {
KeyStroke f1Key = KeyStroke.getKeyStroke(
KeyEvent.VK_F1, 0, true
);
String cmd = "helpToggle";
for(InputMap im : new InputMap[] {
comp.getInputMap(JComponent.WHEN_FOCUSED),
comp.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
}) {
im.put(f1Key, cmd);
}
comp.getActionMap().put(cmd, helpToggle);
}
static class HelpDialog
extends JDialog {
HelpDialog(Window parent) {
super(parent, "\"Help\"", Dialog.ModalityType.MODELESS);
JTextArea doc = new JTextArea(getUnhelpfulText());
doc.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
doc.setEditable(false);
doc.setLineWrap(true);
JScrollPane scroll = new JScrollPane(doc);
scroll.getViewport().setPreferredSize(new Dimension(480, 360));
setContentPane(scroll);
pack();
setLocationRelativeTo(null);
}
static String getUnhelpfulText() {
StringBuilder sb = new StringBuilder();
addRandomParagraph(sb);
for(int i = 0; i < 3; i++) {
sb.append("\n\n");
addRandomParagraph(sb);
}
return sb.toString();
}
static void addRandomParagraph(StringBuilder sb) {
sb.append(" ");
addRandomSentence(sb);
for(int i = 0; i < 10; i++) {
sb.append(' ');
addRandomSentence(sb);
}
}
static void addRandomSentence(StringBuilder sb) {
sb.append((char)(Math.random() * 26 + 'A'));
for(int i = (int)(Math.random() * 10 + 1); i > 0; i--) {
for(int len = (int)(Math.random() * 10 + 1); len > 0; len--) {
sb.append((char)(Math.random() * 26 + 'a'));
}
sb.append(' ');
}
sb.setCharAt(sb.length() - 1, '.');
}
}
}
I've found that although the tutorial says that WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
should cover when the component itself is focused, it doesn't.
Upvotes: 3