Search code examples
javaswingjoptionpane

JOptionPane.showConfirmDialog with multiple inputs returns early when return key hit


Using JOptionPane.showConfirmDialog for multiple inputs:

int result = JOptionPane.showConfirmDialog(null, panel,
            prompt, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);

The panel is built the following way:

JTextField percentField = new JTextField(5);

JComboBox cb = new JComboBox(movingAveragesList);            
JPanel myPanel = new JPanel();
myPanel.add(new JLabel("Enter %:"));
myPanel.add(percentField);
myPanel.add(Box.createHorizontalStrut(5)); // a spacer
myPanel.add(new JLabel("Select MA:"));
myPanel.add(cb); 

When user enters the % field and hits return, the code returns without combobox selection completed. return key == clicking OK button. Anyway to fix this so OK button needs hit before returning?


Solution

  • Two options:

    Three options:

    (probably more)

    • Don't use a JOptionPane which will have your JTextField enter press default to call code to "accept" the the JOptionPane, closing it. Instead create your own modal JDialog, and either ignore the enter press in the JTextField or tab to the next component.
    • Or you could re-assign the key binding for the enter key within your JTextField to do nothing or to tab to the next component. This is a little tricker but a little more interesting for me, so I decided to try it, the results are as shown below:

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JTextField percentField = new JTextField(5);
    
            // get the JTextField's action and input map
            ActionMap actionMap = percentField.getActionMap();
            int condition = JComponent.WHEN_FOCUSED; // only interested in the input where we have focus
            InputMap inputMap = percentField.getInputMap(condition);
    
            // get the key for the enter key press in the input map
            Object enterKey = inputMap.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
    
            // now re-assign its entry in the action map to tab to next component
            actionMap.put(enterKey, new AbstractAction() {
    
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
                    manager.focusNextComponent();
                }
            });
    
            String[] myData = { "One", "Two", "Three", "Four" };
            JComboBox cb = new JComboBox(myData);
            JPanel myPanel = new JPanel();
            myPanel.add(new JLabel("Enter %:"));
            myPanel.add(percentField);
            myPanel.add(Box.createHorizontalStrut(5)); // a spacer
            myPanel.add(new JLabel("Select MA:"));
            myPanel.add(cb);
    
            String prompt = "Please Select";
            int result = JOptionPane.showConfirmDialog(null, myPanel, prompt,
                    JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
        });
    }
    

    A 3rd simpler option:

    • Simply give your JTextField an ActionListener that tabs to the next component. Since a JTextField's ActionListener is triggered whenever it has focus and enter is pressed, this will result in the action that you desire when enter is pressed.

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JTextField percentField = new JTextField(5);
            percentField.addActionListener(e -> {
                KeyboardFocusManager manager = KeyboardFocusManager
                        .getCurrentKeyboardFocusManager();
                manager.focusNextComponent();
            });
    
            String[] myData = { "One", "Two", "Three", "Four" };
            JComboBox cb = new JComboBox(myData);
            JPanel myPanel = new JPanel();
            myPanel.add(new JLabel("Enter %:"));
            myPanel.add(percentField);
            myPanel.add(Box.createHorizontalStrut(5)); // a spacer
            myPanel.add(new JLabel("Select MA:"));
            myPanel.add(cb);
    
            String prompt = "Please Select";
            int result = JOptionPane.showConfirmDialog(null, myPanel, prompt,
                    JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
        });
    }