Search code examples
javaswingdialogvisibilityblocking

How do I make non-modal dialog whose setVisible blocks?


In a Swing (J)Dialog, setModal sets the modality - that is, whether the dialog should block input to other windows or not. Then, setVisible docs say for modal dialogs:

If the dialog is not already visible, this call will not return until the dialog is hidden by calling setVisible(false) or dispose.

Indeed, setVisible does return right away if the dialog is not modal. Sample code:

JDialog jd = new JDialog();
jd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

/**
 * If set to false, setVisible returns right away.
 * If set to true, setVisible blocks until dialog is disposed.
 */
jd.setModal(false);

System.out.println("setting visible");
jd.setVisible(true);
System.out.println("set visible returned");

I want to make a dialog that doesn't block input to other windows, but still does block the caller. What is a good way to do this, now that setVisible doesn't block when the dialog is not modal?

Is there some rationale why setVisible's behavior depends on the modality?


Solution

  • I need to make a dialog that doesn't block input to other windows, but does block the caller so that I know when the dialog has been closed.

    I usually solve this not by blocking the caller, but by using a callback of some sort - a simple interface that the dialog invokes when it's done. Let's say your dialog has an "OK" and a "Cancel" button and you need to distinguish which one is pressed. Then you could do something like this:

    public interface DialogCallback {
        void ok();
        void cancel();
    }
    
    public class MyModelessDialog extends JDialog {
        private final DialogCallback cbk;
        private JButton okButton, cancelButton;        
    
        public MyModelessDialog(DialogCallback callback) {
            cbk = callback;
            setModalityType(ModalityType.MODELESS);
    
            okButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    onOK();
                }
            };
    
            cancelButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    onCancel();
                }
            };
    
            // Treat closing the dialog the same as pressing "Cancel":
            addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e)  {
                    onCancel();
                }
            };
        }
    
        private void onOK() {
            cbk.ok();
        }
    
        private void onCancel() {
            cbk.cancel();
        }
    }
    

    Then you just pass in an instance of DialogCallback to the constructor:

    MyModelessDialog dlg = new MyModelessDialog(new DialogCallback() {
        public void onOK() { 
            // react to OK
        }
        public void onCancel() { 
            // react to Cancel
        }
     });
    

    EDIT

    Is there some rationale why setVisible's behavior depends on the modality?

    Well, that's just how how modal windows are supposed to work, no? A modal window should block the current workflow when displayed, and a non-modal/modeless should not. See e.g. the Wikipedia pages on modal windows or dialog boxes.