javaswingmessagedialog

JOptionPane showMessageDialog - Un-focus 'OK' button


I am trying to create showMessageDialog that will have ONLY ONE button (i.e. OK). I want that button to be un-focused. That means when the dialog opens, is user clicks on Enter key, the dialog should not close. Use must have to click on 'OK' button then only the dialog should disappear.

But I am getting now is always the 'OK' button is focused. So when the dialog popup, and user doesn't see and press Enter key, the dialog closes.

Why I need this: This is not an error message. Only informational message that user must need to see before proceeding. And in this case the big monitors are a little far away from the users location. I have written code to make a sound when the dialog shows up, but user may not listen and hits Enter key. So I want user to see the dialog and clicks on OK button

Sample Code:

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;


import java.awt.Color;
import java.awt.Font;

public class MessageDialogInFrame extends JFrame{

    public MessageDialogInFrame() {
        getContentPane().setBackground(Color.DARK_GRAY);
        setTitle("Message Dialog in Frame");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        setResizable(false);
        setSize(400, 300);
        getContentPane().setLayout(null);
    }

    public static void main(String[] args){
        ImageIcon icon = new ImageIcon("src/images/turtle64.png");
        JTextArea commentTextArea = new JTextArea("Hello", 10, 50);
        commentTextArea.setBackground(Color.YELLOW);
        Font font = new java.awt.Font("Dialog", 0, 15);
        commentTextArea.setFont(font);
        commentTextArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(commentTextArea);
        scrollPane.setBackground(Color.YELLOW);       

        JOptionPane.showMessageDialog(new MessageDialogInFrame(), 
                "I appear as part of the frame!!", "Customized Dialog", 
                JOptionPane.INFORMATION_MESSAGE, icon);
    }
}

Original Code:

private static int showColoredDialog(int category, Component parentComp, String message, String title, int optionType, int messageType, Color color) {
    JTextArea commentTextArea = new JTextArea(message, 10, 50);
    commentTextArea.setBackground(color);
    commentTextArea.setFont(UiFactory.getInfo().getLabelFont());
    commentTextArea.setEditable(false);
    JScrollPane scrollPane = new JScrollPane(commentTextArea);
    scrollPane.setBackground(color);

    switch (category) {
    case 0:
        JOptionPane.showMessageDialog(parentComp, scrollPane, title, messageType);
        return 0;
    case 1:
        return JOptionPane.showConfirmDialog(parentComp, scrollPane, title, optionType, messageType);
    }
    return -1;
}

SOLUTION:

int result = JOptionPane.CLOSED_OPTION;
final JOptionPane pane = new JOptionPane(scrollPane, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION, null, new String[] {"OK"});
            JDialog dialog = pane.createDialog(null, title);
            commentTextArea.requestFocus();
            dialog.setVisible(true);
            if (pane.getValue() != null) result = JOptionPane.OK_OPTION;
            return result;

enter image description here


Solution

  • One way: create a JOptionPane object via its constructor, recursively search its component tree for the JButton, and make it non-focusable:

    JOptionPane optionPane = new JOptionPane();
    optionPane.setMessage("Fubars Rule!");
    optionPane.setMessageType(JOptionPane.PLAIN_MESSAGE);
    optionPane.setOptionType(JOptionPane.DEFAULT_OPTION);
    recursiveUnfocusButtons(optionPane);
    JDialog dialog = optionPane.createDialog(null, "Option Title");
    dialog.setVisible(true); 
    System.exit(0);
    

    and the magic sauce:

    private static void recursiveUnfocusButtons(Component component) {
        if (component instanceof JButton) {
            component.setFocusable(false);
            return;
        } else if (component instanceof Container) {
            for (Component c : ((Container) component).getComponents()) {
                recursiveUnfocusButtons(c);
            }
        }
    } 
    

    To get rid of the enter action, you need to change the key bindings for the enter key in the JOptionPane:

    import java.awt.Component;
    import java.awt.Container;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    
    import javax.swing.*;
    
    public class JOptionNoFocus {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                JOptionPane optionPane = new JOptionPane();
                optionPane.setMessage("Fubars Rule!");
                optionPane.setMessageType(JOptionPane.PLAIN_MESSAGE);
                optionPane.setOptionType(JOptionPane.DEFAULT_OPTION);
    
                KeyStroke enterStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
                optionPane.getInputMap(JComponent.WHEN_FOCUSED).put(enterStroke, enterStroke.toString());
                optionPane.getActionMap().put(enterStroke.toString(), new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // do nothing
                    }
                });
    
                recursiveUnfocusButtons(optionPane);
                JDialog dialog = optionPane.createDialog(null, "Option Title");
                dialog.setVisible(true); 
                System.exit(0);
            });
        }
    
        private static void recursiveUnfocusButtons(Component component) {
            if (component instanceof JButton) {
                JButton button = (JButton) component;
                button.setFocusable(false);
                return;
            } else if (component instanceof Container) {
                for (Component c : ((Container) component).getComponents()) {
                    recursiveUnfocusButtons(c);
                }
            }
        }
    }
    

    Once you get into creating more complex dialogs though, just skip creating JDialogs and instead create your own JDialog, one with components placed where you desire. Make them modal if you want them to stop the application until dealt with.