Search code examples
javaswingjdialogkey-bindingsspaces

registerKeyboardAction with VK_SPACE not working


Background: I have a table cell editor that when invoked pops up a dialog with a list of checkboxes, and 2 buttons (OK and Cancel). All works well. I later registered keyboard actions so that the ENTER and ESCAPE keys would perform the ok/cancel actions. Still no problems.

Using the same technique, I tried to register the SPACE key to do a third action. This didn't fire. Sample code:

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class DialogTest implements Runnable
{
  JDialog dialog;
  JList jlist;

  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new DialogTest());
  }

  public void run()
  {    
    ActionListener okListener = new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        close(true);
      }
    };

    ActionListener cancelListener = new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        close(false);
      }
    };

    ActionListener otherListener = new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        doOther();
      }
    };

    JButton okButton = new JButton("OK");
    okButton.addActionListener(okListener);

    JButton cancelButton = new JButton("Cancel");
    cancelButton.addActionListener(cancelListener);

    jlist = new JList(new String[]{"A", "B", "C", "D", "E", "F", "G"});
    jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    jlist.setVisibleRowCount(5);

    JScrollPane scroll = new JScrollPane(jlist);
    scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    JPanel buttonsPanel = new JPanel(new FlowLayout());
    buttonsPanel.add(okButton);
    buttonsPanel.add(cancelButton);

    JPanel content = new JPanel(new BorderLayout());
    content.add(scroll, BorderLayout.CENTER);
    content.add(buttonsPanel, BorderLayout.SOUTH);

    dialog = new JDialog((Frame) null, true);
    dialog.setContentPane(content);
    dialog.pack();
    dialog.setLocationRelativeTo(null);

    dialog.getRootPane().registerKeyboardAction(okListener,
        KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    dialog.getRootPane().registerKeyboardAction(cancelListener,
        KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    // This doesn't work
    dialog.getRootPane().registerKeyboardAction(otherListener,
        KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    dialog.setVisible(true);
  }

  private void close(boolean commit)
  {
    if (commit)
    {
      System.out.println("Now saving...");
    }
    else
    {
      System.out.println("Now closing...");
      System.exit(0);
    }
  }

  private void doOther()
  {
    System.out.println("current selection: " + jlist.getSelectedValue());
  }
}

EDIT

Thanks to mKorbel's suggestion (and useful link!), I got this to work:

1) Change the line ActionListener otherListener = new ActionListener(){...} to:

Action otherListener = new AbstractAction(){...}

2) Change the SPACE keystroke registration to:

KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
Object key = keyStroke.toString();
jlist.getInputMap().put(keyStroke, key);
jlist.getActionMap().put(key, otherAction);

Note that the registration is for the JList, not the JDialog as the other 2 keystrokes are.


Solution

  • hard to say whatever cleaver to your majesty ....

    EDIT

    I forgot about SPACE is registred KeyBindings as humans JButtons accelerator (Mouse, ENTER & SPACE), there is possible concurency if JButton is selected

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class DialogTest implements Runnable {
    
        private JDialog dialog;
        private JList jlist;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new DialogTest());
        }
    
        @Override
        public void run() {
            ActionListener okListener = new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    close(true);
                }
            };
    
            ActionListener cancelListener = new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    close(false);
                }
            };
    
            ActionListener otherListener = new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    doOther();
                }
            };
            JButton okButton = new JButton("OK");
            okButton.addActionListener(okListener);
            JButton cancelButton = new JButton("Cancel");
            cancelButton.addActionListener(cancelListener);
            jlist = new JList(new String[]{"A", "B", "C", "D", "E", "F", "G"});
            jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            jlist.setVisibleRowCount(5);
            JScrollPane scroll = new JScrollPane(jlist);
            scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            JPanel buttonsPanel = new JPanel(new FlowLayout());
            buttonsPanel.add(okButton);
            buttonsPanel.add(cancelButton);
            JPanel content = new JPanel(new BorderLayout());
            content.add(scroll, BorderLayout.CENTER);
            content.add(buttonsPanel, BorderLayout.SOUTH);
            dialog = new JDialog((Frame) null, true);
            dialog.setContentPane(content);
            dialog.pack();
            dialog.setLocationRelativeTo(null);
            dialog.getRootPane().registerKeyboardAction(okListener,
                    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
                    JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    
            dialog.getRootPane().registerKeyboardAction(cancelListener,
                    KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                    JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
            // This doesn't work
            /*dialog.getRootPane().registerKeyboardAction(otherListener,
            KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
            JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);*/
            dialog.getRootPane().getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "doSomething");
            dialog.getRootPane().getActionMap().put("doSomething", new AbstractAction() {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.exit(0);
                }
            });
            dialog.setVisible(true);
        }
    
        private void close(boolean commit) {
            if (commit) {
                System.out.println("Now saving...");
            } else {
                System.out.println("Now closing...");
                System.exit(0);
            }
        }
    
        private void doOther() {
            System.out.println("current selection: " + jlist.getSelectedValue());
        }
    }