1

In my question How do I draw on a JPanel from multiple outside classes? Frakcool gave me the advice to use key bindings, and one thing didn't work: When the JButton put another JPanel infront of the frame, the Keybinding didn't respond. This is the code:

private JFrame frame;
private JPanel[] statePanels;
private CardLayout layout;
private JPanel mainPanel;
private JButton button;
private String status;

void initAndShow()
{
    //Init stuff
    mainPanel = new JPanel(layout);
    statePanels = new JPanel[2];
    button = new JButton("Exit");
    status = "Menu";

    button.addActionListener(e -> {
        status = status.equals("Menu") ? "World" : "Menu";
        layout.show(mainPanel, status);
    });

    statePanels[0] = new OutWorldHandler();
    statePanels[1] = new InWorldHandler();

    mainPanel.add(statePanels[0], "Menu");
    mainPanel.add(statePanels[1], "World");

    mainPanel.getInputMap().put(KeyStroke.getKeyStroke('f'), "close");
    mainPanel.getActionMap().put("close", this);

    frame.add(mainPanel);
    frame.add(button, BorderLayout.SOUTH);
}

@Override
public void actionPerformed(ActionEvent e)
{
    System.out.println("hi");
}

The expected output was that when I allways pressed f the console would print out "hi", but it only did as long as I didn't press the button

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
Keheck
  • 131
  • 1
  • 10
  • The problem is, by default, getInputMap will return a “When has focus” mapping, thus means that mainPanel will need to have focus in order to trigger the bindings. Use the [getInputMap(int)](https://docs.oracle.com/javase/10/docs/api/javax/swing/JComponent.html#getInputMap(int)) variant and pass WHEN_IN_FOCUSED_WINDOW, then it will trigger when ever the window is focused – MadProgrammer Jan 23 '19 at 19:07
  • @MadProgrammer And I thought I had the answer... – Keheck Jan 23 '19 at 19:12

2 Answers2

1

Key bindings aren't particularly complex, but they can take a little bit of getting use to. To this end, it's useful to have How to Use Key Bindings and the JavaDocs for JComponent on hand, for reference.

One of the goals of the key bindings API is configurability, allowing us more control over determining when a key stroke should trigger an event.

The JComponent#getInputMap method returns a mapping which is bound to the "when has focused" context. This means that the component will need to have focus (and be focusable obviously) before a binding will be triggered.

If you want the binding to be triggered at a different level, then you need to use JComponent#getInputMap(int) and pass it one of the three available contexts:

Which you use will depend on your needs, but I'm generally lazy and go for JComponent.WHEN_IN_FOCUSED_WINDOW when I want a "global" state

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
-1

The solution that worked for me: I simply added a statement to the button.addActionListener lambda that put the focus on mainPanel: mainPanel.grabFocus();.

The full code then looked something like this:

button.addActionListener(e -> {
    status = status.equals("Menu") ? "World" : "Menu";

    layout.show(mainPanel, status);
    button.setText("Exit " + status);
    mainPanel.grabFocus();
});
Keheck
  • 131
  • 1
  • 10
  • Don't use grabFocus(). Read the API for the grabFocus()N method. It will tell your the better method to use. – camickr Jan 24 '19 at 03:16